import dbus
from dbus.mainloop.glib import DBusGMainLoop
import re, string
import sys
from gi.repository import EDataServer
from gi.repository import GObject

class SyncEvoltuion:

    def __init__(self):
        self.dbus_loop = DBusGMainLoop()
        self.loop = GObject.MainLoop()
        self.bus = dbus.SessionBus(mainloop=self.dbus_loop)
        server_proxy = self.bus.get_object('org.syncevolution',
                                           '/org/syncevolution/Server')
        self.server = dbus.Interface(server_proxy, 
                                     dbus_interface='org.syncevolution.Server')

    def startSession(self, name, flags):
        session_path = self.server.StartSessionWithFlags(name, flags)
        session_proxy = self.bus.get_object('org.syncevolution',
                                           session_path)
        session = dbus.Interface(session_proxy, 
                                 dbus_interface='org.syncevolution.Session')
        print("Session status: %s" % session.GetStatus()[0])
        return session

    def on_sync_status_changed(self, status, error, sources, sender=None, **kwargs):
        print("sync status changed: status: %s, error: %d" % (status, error))
        if status == "done":
            GObject.idle_add(self.quit)    
        #print("sources", sources)

    def on_sync_progress_changed(self, progress, sources, sender=None, **kwargs):
        print("sync progress changed: progress %d" % (progress))
        #print("sources", sources)

    def sync(self, peer_name, source_name):
        sync_sources = {}
        sync_sources[source_name] = "refresh-from-remote"
        
        session = self.startSession(peer_name, [])
        session.connect_to_signal("StatusChanged", self.on_sync_status_changed, sender_keyword='sender')
        session.connect_to_signal("ProgressChanged", self.on_sync_progress_changed, sender_keyword='sender')

        session.Sync("", sync_sources)
        print("Syncing...")
        
        
    def configureCalendarSync(self, peer_name, account_id):
        session = self.startSession(peer_name, ["all-configs"])
        dbs = self.listRemoteDatabases(session, peer_name, account_id)
       
        config = session.GetNamedConfig("Google", True)
        config[""]["username"] = "uoa:%s,google-caldav"%(account_id)
        config[""]["password"] = ""

        source_to_db_map = {}
        template = config["source/calendar"]
        for db in dbs:
            source_name = self.normalize(db[0])

            new_calendar = template
            new_calendar["database"] = db[1]
            
            config["source/calendar_" + source_name ] = new_calendar

            #create local DB
            local_db_name = "%s_%s" % (source_name , account_id)
            source_id = self.createEDSSource(local_db_name)

            source_to_db_map[db[1]] = source_id
            print("Source: calendar_%s will be stored in %s:%s" % (source_name, source_id, local_db_name))
            

        session.SetNamedConfig("target-config@%s" % peer_name, False, False, config)
        session.Detach()
        del session
        session = None

        session = self.startSession("", ["all-configs"])
        #retrieve the @default config
        config = session.GetNamedConfig("@default", False)
        
        for db in dbs:
            source_name = self.normalize(db[0])
            new_source = {}
            new_source["backend"] = "evolution-calendar"
            new_source["database"] = source_to_db_map[db[1]]
            config["source/calendar_" + source_name] = new_source

            print("Create local data store: %s" % source_to_db_map[db[1]])

        #save changes on @default config
        session.SetNamedConfig("@default", False, False, config)
        session.Detach()
        del session
        session = None

        session = session = self.startSession("", ["all-configs"])
        config = session.GetNamedConfig("SyncEvolution_Client", True);
        
        # link with "gcal" remote source
        config[""]["syncURL"] = ("local://@%s" % (peer_name))
        config[""]["username"] = ""
        config[""]["password"] = ""

        #save the new config with name "gcal"
        session.SetNamedConfig(peer_name, False, False, config)
        session.Detach()
        del session
        session = None
        print("Account configured")
        GObject.idle_add(self.quit)
   

    def listRemoteDatabases(self, session, peer_name, account_id):
        config = session.GetNamedConfig("Google", True)
        config[""]["username"] = "uoa:%s,google-caldav"%(account_id)
        config[""]["password"] = ""
        config["source/calendar"]["backend"] = "caldav"
        session.SetNamedConfig(peer_name, False, True, config)
        print("Fetching remote databases (wait...)")
        dbs = session.GetDatabases("calendar")
        for db in dbs:
            print("Name: %s Source: %s Flag: %s" % (db[0], db[1], db[2]))
        return dbs

    def normalize(self, value):
        pattern = re.compile('[\W_]+', re.UNICODE)
        return pattern.sub('', value)

    def createEDSSource(self, source_name):        
        registry = EDataServer.SourceRegistry.new_sync(None)
        new_source = EDataServer.Source.new(None, None)
        new_source.set_display_name(source_name)
        new_source.set_parent("local-stub")
        calendar_ex = new_source.get_extension(EDataServer.SOURCE_EXTENSION_CALENDAR)
        calendar_ex.set_backend_name("local")
        registry.commit_source_sync(new_source, None)
        return new_source.get_uid()

    def run(self):        
        self.loop.run()

    def quit(self):
        self.loop.quit()

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("Usage:  python3 main.py config <account-id>\n\tpython3 main.py sync <source-name>")
    else:        
        syncevolution = SyncEvoltuion()
        if sys.argv[1] == "config":
            syncevolution.configureCalendarSync("gcal", sys.argv[2])
        else:
            syncevolution.sync("gcal", sys.argv[2])
        syncevolution.run()

