#!/usr/bin/python
import struct
import dbus
import dbus.glib
import gobject
import time
import os
import fcntl
import sys
input_event_struct = "@LLHHi"
input_event_size = struct.calcsize(input_event_struct)

logfile = None

def set_status(s):
    f = open("/tmp/answering-machine.status", "w+")
    f.write("%s\n" % s)
    f.close()
    
def dbg(s):
    if logfile:
        logfile.write("%s %s\n" % (time.strftime("%Y-%m-%d %H:%M:%S"), s))
        logfile.flush()
    else:
        print("%s %s" % (time.strftime("%H:%M:%S"), s))

def openlog():
    global logfile
    try:
        f = os.path.expanduser("~/.answering-machine/log.txt")
        fp = open(f, "a+")
        logfile = fp
    except:
        dbg("failed to open log")

def writepid():
    #try:
        f = os.path.expanduser("~/.answering-machine/pid")
        fp = open(f, "w+")
        fp.write(str(os.getpid()) + "\n")
        fp.close()
    #except:
    #    dbg("failed to write pid")
    
def formatDict(d):
    return " ".join(["(%s %s)" % (i, d[i]) for i in d.keys()])

class Main():
    def __init__(self, loop):
        self.loop = loop
        self.status = "release"
        self.prev_status = "release"
        
        openlog()
        writepid()
        #dbg("starting")
        #os.system("auxledblink-start 100 1500")
        bus = dbus.SystemBus()
        gsm_device_obj = bus.get_object("org.freesmartphone.ogsmd", "/org/freesmartphone/GSM/Device")
        self.gsm_network_iface = dbus.Interface(gsm_device_obj, 'org.freesmartphone.GSM.Network')
        self.gsm_network_iface.connect_to_signal("Status", self.cbNetworkStatus)
        self.gsm_call_iface = dbus.Interface(gsm_device_obj, 'org.freesmartphone.GSM.Call')
        self.gsm_call_iface.connect_to_signal("CallStatus", self.cbCallStatus)
        self.gsm_device_iface = dbus.Interface(gsm_device_obj, 'org.freesmartphone.GSM.Device')
        self.gsm_sim_iface = dbus.Interface(gsm_device_obj, 'org.freesmartphone.GSM.SIM')
        self.gsm_sim_iface.connect_to_signal("IncomingStoredMessage", self.cbIncomingMessage)
        self.gsm_sim_iface.connect_to_signal("IncomingMessageReceipt", self.cbIncomingMessageReceipt)

        server_obj = bus.get_object("org.freesmartphone.ogsmd", "/org/freesmartphone/GSM/Server")
        phone_iface = dbus.Interface(server_obj,  'org.freesmartphone.GSM.Phone')
        #phone_iface.StartAutoRegister("0000")

        fd = os.open("/dev/input/aux_button", os.O_NONBLOCK | os.O_RDONLY)
        fcntl.fcntl(fd, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
        fcntl.ioctl(fd, 0x40044590, 1) # EVIOCGRAB
        gobject.io_add_watch(fd, gobject.IO_IN | gobject.IO_ERR | gobject.IO_HUP, self.cbEventActivity)

        dbus_obj = bus.get_object("org.freedesktop.DBus", "/org/freedesktop/DBus")
        dbus_iface = dbus.Interface(dbus_obj, "org.freedesktop.DBus")
        dbus_iface.connect_to_signal("NameOwnerChanged", self.cbNameOwnerChanged)

        self.cbRequestReply()
        dbg("startup done")

    def cbRequestReply(self):
        dbg("cbRequestReply")
        self.cbStartAntenna(1)

    def cbStartAntenna(self, w):
        dbg("cbStartAntenna")
        self.gsm_device_iface.SetAntennaPower(True,
                                              reply_handler = self.cbAntennaPowerReply,
                                              error_handler = self.cbAntennaPowerError)
    def cbAntennaPowerReply(self):
        dbg("cbAntennaPowerReply")
        self.gsm_network_iface.Register(reply_handler = self.cbRegisterReply,
                                        error_handler = lambda e: (dbg("cbRegisterError %s" % e), self.loop.quit()))
    def cbAntennaPowerError(self, e):
        dbg("cbAntennaPowerError %s" % e)
        if str(e).find("current status is 'enabling'") >= 0 or str(e).find("current status is 'unknown'") >= 0:
            time.sleep(10)
            self.cbStartAntenna(0)
        else:
            self.gsm_sim_iface.GetAuthStatus(reply_handler = self.cbAuthStatusReply,
                                             error_handler = lambda e: (dbg("cbAuthStatusError %s" % e), self.loop.quit()))
    def cbAuthStatusReply(self, authstatus):
        dbg("cbAuthStatusReply %s" % authstatus)
        if authstatus == "READY":
            self.cbStartAntenna(0)
        elif authstatus == "SIM PIN":
            pin = "0000"
            if not pin:
                self.loop.quit()
            self.gsm_sim_iface.SendAuthCode(pin,
                                            reply_handler = self.cbAuthCodeReply,
                                            error_handler = lambda e: (dbg("cbAuthCodeError %s" % e), self.loop.quit()))
        else:
            self.loop.quit()
    def cbAuthCodeReply(self):
        dbg("cbAuthCodeReply")
        self.cbAuthStatusReply("READY")
    def cbRegisterReply(self):
        dbg("cbRegisterReply")

    def cbNetworkStatus(self, status):
        dbg("cbNetworkStatus %s" % formatDict(status))

    def cbCallStatus(self, id, status, properties):
        dbg("cbCallStatus %d, %s, %s" % (id, status, formatDict(properties)))
        self.status = status
        if status == "incoming":
            if "peer" in properties and properties["peer"] == "+358942832031":
                set_status("debug_call_received")
                self.gsm_call_iface.ReleaseAll()
                os.system("kapula-debug-call-handler &")
            else:
                set_status("call_received")
                os.system("set-alsa-profile stereoout")
                os.system("vibrator-start")
                os.system("ringtone-start")
                os.system("om-led aux_red 255 timer 400 200")
                if self.prev_status != self.status:
                    if "peer" in properties:
                        number = properties["peer"]
                    else:
                        number = "secret"
                    os.system("incoming-call-notify %s" % number)
        if status == "release":
            os.system("ringtone-stop")
            os.system("vibrator-stop")
            os.system("om-led aux_red 0")
        self.prev_status = status

    def cbActivate(self):
        dbg("cbActivate")
        os.system("vibrator-stop")
        os.system("ringtone-stop")
        os.system("set-alsa-profile gsmhandset")
        os.system("om-led aux_red 255 timer 100 1500")
        self.gsm_call_iface.Activate(1)

    def cbRelease(self):
        dbg("cbRelease")
        self.gsm_call_iface.Release(1)

    def cbEventActivity(self, source, condition):
        if condition == gobject.IO_IN:
            dbg("cbEventActivity")
        else:
            dbg("cbEventActivity %s" % repr(condition))
            self.loop.quit()
        data = os.read(source, 512)
        events = [ data[i:i+input_event_size] for i in range(0, len(data), input_event_size) ]
        for e in events:
            timestamp, microseconds, typ, code, value = struct.unpack( input_event_struct, e )
            # We need more then just second accuracy
            timestamp = timestamp + microseconds/1000000.0
            if typ != 0x00: # ignore EV_SYN (synchronization event)
                dbg("event %s %s %s %s %s" % (self.status, timestamp, typ, code, value))
                if code == 169 and value == 1:
                    if self.status == "incoming":
                        self.cbActivate()
                    elif self.status == "active":
                        self.cbRelease()
                if code == 116 and value == 1:
                    os.system("sudo apm -s")
        return True

    def cbIncomingMessage(self, index):
        dbg("cbIncomingMessage %s" % index)
        set_status("sms_received")
        os.system("set-alsa-profile stereoout")
        os.system("sms-receive-notify")

    def cbIncomingMessageReceipt(self, *args):
        dbg("cbIncomingMessageReceipt %s" % repr(args))
        set_status("sms_received_receipt")

    def cbNameOwnerChanged(self, name, oldOwner, newOwner):
        if name == "org.freesmartphone.ogsmd" and newOwner == "":
            dbg("cbNameOwnerChanged %s %s %s" % (name, oldOwner, newOwner))
            self.loop.quit()

if __name__ == "__main__":
    loop = gobject.MainLoop()
    Main(loop)
    loop.run()
