# This file is part of EventGhost.
# Copyright (C) 2008 Lars-Peter Voss <bitmonster@eventghost.org>
# 
# EventGhost is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# 
# EventGhost is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with EventGhost; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

#Last change: 2009-12-17 19:51 GMT+1

import eg
from threading import Timer
        

eg.RegisterPlugin(
    name = "Deal Extreme USB PC Remote",
    author = "Bitmonster & Pako",
    version = "1.1.0",
    kind = "remote",
    guid = "{1AB25B82-97E0-4C27-B828-DB0BC3739360}",
    description = ur"""<rst>
Plugin for the **Deal Extreme USB PC Remote**.

|

.. image:: DXusbPCR.jpg
   :align: center

You can choose, whether *Numlock* is ignored or not.
If ignored, numpad-keys will always send digits,
regardless of the state of *Numlock*.""",
)

CODES = {
    263425:((263425,1967364,3802373,1,4,5,),(1,4,5), "Hotkey_A", "Hotkey_A"),
    328961:((328961,2032900,3867909,1,4,5,),(1,4,5), "Hotkey_B", "Hotkey_B"),
    394497:((394497,2098436,3933445,1,4,5,),(1,4,5), "Hotkey_C", "Hotkey_C"),
    460033:((460033,2163972,3998981,1,4,5,),(1,4,5), "Hotkey_D", "Hotkey_D"),
    33030:((33030,),(6,), "Power", "Power"),
    100867:((100867,3,),(3,), "E-mail", "E-mail"),
    140035:((140035,3,),(3,), "WWW", "WWW"),
    3998721:((3998721,1,),(1,), "Close", "Close"),
    8194:((8194,),(2,), "MouseLeft", "MouseLeft"),
    16386:((16386,),(2,), "MouseRight", "MouseRight"),
    46595:((46595,3,),(3,), "|<", "|<"),
    52483:((52483,3,),(3,), "Play/Pause", "Play/Pause"),
    46339:((46339,3,),(3,), ">|", ">|"),
    51971:((51971,5242881,5243140,328453,),(3,1,4,5,), "<<", "<<"),
    46851:((46851,3,),(3,), "Stop", "Stop"),
    51715:((51715,5177345,5177604,590597,),(3,1,4,5,), ">>", ">>"),
    59651:((59651,),(3,), "Volume+", "Volume+"),
    59907:((59907,),(3,), "Volume-", "Volume-"),
    143363:((143363,2622465,2097409,3,1,1,),(3,1,1,), "Full Screen", "Full Screen"),
    4915201:((4915201,),(1,), "Page+", "Page+"),
    5111809:((5111809,),(1,), "Page-", "Page-"),
    57859:((57859,3,),(3,), "Mute", "Mute"),
    526337:((526337,1,),(1,), "MyPC", "MyPC"),
    2752513:((2752513,),(1,), "Backspace", "Backspace"),
    1966081:((1966081,),(1,), "Num1", "Num1"),
    2031617:((2031617,),(1,), "Num2", "Num2"),
    2097153:((2097153,),(1,), "Num3", "Num3"),
    2162689:((2162689,),(1,), "Num4", "Num4"),
    2228225:((2228225,),(1,), "Num5", "Num5"),
    2293761:((2293761,),(1,), "Num6", "Num6"),
    2359297:((2359297,),(1,), "Num7", "Num7"),
    2424833:((2424833,),(1,), "Num8", "Num8"),
    2490369:((2490369,),(1,), "Num9", "Num9"),
    2555905:((2555905,),(1,), "Num0", "Num0"),
    2818049:((2818049,),(1,), "Tab", "Num1"),
    5373953:((5373953,),(1,), "Up_arrow", "Num2"),
    2049:((2049,1,),(1,), "Start", "Num3"),
    5242881:((5242881,),(1,), "Left_arrow", "Num4"),
    2621441:((2621441,),(1,), "Enter", "Num5"),
    5177345:((5177345,),(1,), "Right_arrow", "Num6"),
    1179905:((1179905,),(1,), "Open", "Num7"),
    5308417:((5308417,),(1,), "Down_arrow", "Num8"),
    2686977:((2686977,),(1,), "Escape", "Num9"),
    2819073:((2819073,1,),(1,), "SwitchWindows", "Num0"),
    460801:((460801,1,),(1,), "Desktop", "Desktop")
}
 
    
class Text:
    ignoreNumlock = "Ignore button Numlock"
    tooltip = """If this option is checked,
events from the Numpad-buttons of remote control
are always sent as a digits
(regardless of the state of Num Lock)."""
    
class DXusbPCR(eg.PluginBase):
    text = Text
                
    @eg.LogIt
    def __start__(self, ignoreNumLock):
        self.ignoreNumLock = ignoreNumLock
        self.timeout = 0.1
        self.timer = Timer(self.timeout, self.OnTimeout)        
        self.status = 0
        self.keyData = ()
        self.codesIx = 0
        self.usb = eg.WinUsb(self)
        self.usb.AddDevice(
            "Deal Extreme USB PC Remote",
            "USB\\VID_06B4&PID_1C70&REV_0200",
            "{72679574-1865-499d-B182-4B099D6D1391}",
            self.Callback,
            4,
            False,
         )
        self.usb.Open()
         
    def __stop__(self):
        self.usb.Close()

    def OnTimeout(self):
        self.EndLastEvent()
        self.status = 0
        self.codesIx = 0

#Status variable definition:
#---------------------------    
#Status 0 = idle
#Status 1 = receive key-down code
#Status 2 = (waiting for or) receive key-up code
#Status 3 = mousepad Up
#Status 4 = mousepad Down

    def Callback(self, data):
        self.timer.cancel()
        if tuple(data[:2]) <> (2,0): #standard buttons (no mousepad)
            value = (data[3] << 24) + (data[2] << 16) + (data[1] << 8) + data[0]
            if self.status == 0:
                if value in CODES:
                    self.keyData = CODES[value]
                    self.status = 1
                else: #flush
                    return
            if self.status == 1:        
                if value == self.keyData[0][self.codesIx]:
                    self.codesIx += 1
                    if self.codesIx == len(self.keyData[0]):
                        keyName = self.keyData[3] if self.ignoreNumLock else self.keyData[2]
                        self.TriggerEnduringEvent(keyName)
                        self.status = 2
                        self.codesIx = 0
                else: #flush
                    self.status = 0
                    self.codesIx = 0
            else: #if self.status == 2
                try:
                    if value == self.keyData[1][self.codesIx]:
                        self.codesIx += 1
                        if self.codesIx == len(self.keyData[1]):
                            self.EndLastEvent()
                            self.status = 0
                            self.codesIx = 0
                    else: #flush
                        self.status = 0
                        self.codesIx = 0
                except:
                    self.EndLastEvent()
                    if value in CODES:
                        self.keyData = CODES[value]
                        #self.status = 1
                        if value == self.keyData[0][self.codesIx]:
                            self.codesIx += 1
                            if self.codesIx == len(self.keyData[0]):
                                keyName = self.keyData[3] if self.ignoreNumLock else self.keyData[2]
                                self.TriggerEnduringEvent(keyName)
                                self.status = 2
                                self.codesIx = 0
                        else: #flush
                            self.status = 0
                            self.codesIx = 0
                    else:
                        self.status = 0
                        self.codesIx = 0
        else: #mousepad Up/Down
            if self.status == 0:
                if 15 & data[3]:
                    if 240 & data[3]:
                        self.status = 3 #Up
                        self.TriggerEnduringEvent("MouseUp")
                    else:
                        self.status = 4 #Down
                        self.TriggerEnduringEvent("MouseDown")
                    self.timer = Timer(self.timeout, self.OnTimeout)
                    self.timer.start()
                    
            elif self.status == 3: #mousepad Up
                if tuple(data[:2]) == (2,0) and (15 & data[3]) and (240 & data[3]):
                    self.timer = Timer(self.timeout, self.OnTimeout)
                    self.timer.start()
                else:
                    self.EndLastEvent()
                    self.status = 0
                    self.codesIx = 0
                
            elif self.status == 4:  #mousepad Down
                if tuple(data[:2]) == (2,0) and (240 ^ data[3]) > 240: #bits 4-7 = 0000
                    self.timer = Timer(self.timeout, self.OnTimeout)
                    self.timer.start()
                else:
                    self.EndLastEvent()
                    self.status = 0
                    self.codesIx = 0
            
            else: #maybe self.status == 2 for MouseLeft or MouseRight buttons
                self.EndLastEvent()
                self.status = 0
                self.codesIx = 0                
                
    def Configure(self, ignoreNumLock=False):
        panel = eg.ConfigPanel()
        ignoreNumLock = panel.CheckBox(ignoreNumLock, self.text.ignoreNumlock)
        toolTip = wx.ToolTip(self.text.tooltip)
        ignoreNumLock.SetToolTip(toolTip)
        #toolTip.SetDelay(10000)

        panel.AddLine(ignoreNumLock)

        while panel.Affirmed():
            panel.SetResult(
                ignoreNumLock.GetValue(),
            )
                