#!/usr/bin/env python
#
# Copyright (c) 2013 Stefano Karapetsas
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 Lesser General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.
#
# Authors: Stefano Karapetsas <stefano@karapetsas.com>

import glob
import optparse
import os
import sys

# to read MateConf files
import xml.etree.ElementTree as ElementTree

# gobject introspection
import gi
# to show dialogs
gi.require_version("Gtk", "2.0")
from gi.repository import Gtk
# to read convert files
from gi.repository import GLib
# to write GSettings
from gi.repository import Gio

# command options
parser = optparse.OptionParser()
parser.add_option("-d", "--debug", dest="debug", action="store_true", help="enable debug messages")
parser.add_option("-f", "--force", dest="force", action="store_true", help="force migration of settings")
parser.add_option("-l", "--linuxmint", dest="linuxmint", action="store_true", help="force detection of linuxmint")
(options, args) = parser.parse_args()

# print a message only if debug option is enabled
def debug_message(*argv):
    args = []
    for arg in argv:
        args.append(str(arg))
    if options.debug:
        print " ".join(args)

# get a mateconf string list
def mateconf_get_string_list(root, key):
    for child in root:
        if child.attrib['name'] == key:
            if child.attrib["type"] == "list":
                if child.attrib["ltype"] == "string":
                    if len(list(child)) > 0:
                        strv = []
                        for ch in child:
                            for li in ch:
                                strv.append(li.text)
                        return strv
                    else:
                        return []
    return None

def gsettings_id(id):
    return id.replace("_", "-")

mateconf_user_path = os.path.dirname(os.path.join(GLib.get_home_dir(), ".mateconf/"))
if not os.path.exists(mateconf_user_path):
    #FIXME save key in GSettings
    debug_message(mateconf_user_path, "not found!")
    sys.exit(0)

#FIXME check GSettings key
dialog = Gtk.MessageDialog (None,
                            Gtk.DialogFlags.MODAL,
                            Gtk.MessageType.QUESTION,
                            Gtk.ButtonsType.YES_NO,
                            "Are you sure to migrate settings from MATE 1.4?\n" + \
                            "This will delete permanently all your current 1.6 settings.")
res = dialog.run()
if res != Gtk.ResponseType.YES:
    dialog.destroy()
    sys.exit(0)
dialog.destroy()

# load .convert files list from MateConf folder
convert_files = glob.glob("/usr/share/MateConf/gsettings/*")

no_background = False
for convert_file in convert_files:

    # load .convert file
    keyfile = GLib.KeyFile.new()
    GLib.KeyFile.load_from_file(keyfile, convert_file, 0)
    groups = GLib.KeyFile.get_groups(keyfile)

    # iter GSettings schemas
    for group in groups[0]:

        # each group is a GSettings schema
        gsettings_schema = group
        keys = GLib.KeyFile.get_keys(keyfile, group);
        settings = Gio.Settings.new(gsettings_schema)

        # delay settings to save them only once
        settings.delay()

        # iter GSettings keys
        for gsettings_key in keys[0]:

            # get mateconf xml file,
            mateconf_fullkey = GLib.KeyFile.get_string(keyfile, group, gsettings_key)
            mateconf_path = os.path.dirname(os.path.join(mateconf_user_path, mateconf_fullkey[1:]))
            mateconf_file = os.path.join(mateconf_path, "%mateconf.xml")
            mateconf_key = os.path.basename(mateconf_fullkey)

            # if user has custom mateconf
            if os.path.exists(mateconf_file):

                # MateConf file could be empty
                try:
                    tree = ElementTree.parse(mateconf_file)
                    root = tree.getroot()
                except:
                    continue

                for child in root:
                    # if the key exists, the user has a custom value
                    if child.attrib['name'] == mateconf_key:

                        # boolean
                        if child.attrib["type"] == "bool":
                            if child.attrib["value"] == "true":
                                debug_message(gsettings_schema, gsettings_key, True)
                                settings.set_boolean(gsettings_key, True)
                            else:
                                debug_message(gsettings_schema, gsettings_key, False)
                                settings.set_boolean(gsettings_key, False)

                        # int
                        elif child.attrib["type"] == "int":
                            debug_message(gsettings_schema, gsettings_key, int(child.attrib["value"]))
                            settings.set_int(gsettings_key, int(child.attrib["value"]))

                        # string
                        elif child.attrib["type"] == "string":

                            value = child[0].text
                            # hack for background
                            if gsettings_schema == "org.mate.background" and gsettings_key == "picture-filename":
                                # check if the background exists before apply it
                                if not os.path.exists(value):
                                    if "/pixmaps/backgrounds/" in value:
                                        value = value.replace("/pixmaps/backgrounds/", "/backgrounds/")
                                        if not os.path.exists(value):
                                            no_background = True
                                            continue
                                    else:
                                        no_background = True
                                        continue
                            if no_background and gsettings_schema == "org.mate.background":
                                continue

                            if value != None:
                                debug_message(gsettings_schema, gsettings_key, value)
                                settings.set_string(gsettings_key, value)

                        # list
                        elif child.attrib["type"] == "list":

                            # list of strings
                            if child.attrib["ltype"] == "string":
                                
                                if len(list(child)) > 0:
                                    strv = []
                                    for ch in child:
                                        for li in ch:
                                            strv.append(li.text)
                                    debug_message(gsettings_schema, gsettings_key, strv)
                                    settings.set_strv(gsettings_key, strv)
                                else:
                                    debug_message(gsettings_schema, gsettings_key, "[]")
                                    settings.set_strv(gsettings_key, [])
                            else:
                                debug_message("Unknown list type", child.attrib["ltype"])

                        else:
                            debug_message("Unknown type", child.attrib["type"])

        settings.apply()

# ugly hack to migrate panel layout
is_linuxmint = os.path.exists("/etc/linuxmint/info") or options.linuxmint
mateconf_panel_file = os.path.join(mateconf_user_path, "apps/panel/general", "%mateconf.xml")
if os.path.exists(mateconf_panel_file):

    # load list of panels
    tree = ElementTree.parse(mateconf_panel_file)
    root = tree.getroot()

    # old lists
    toplevels = mateconf_get_string_list (root, "toplevel_id_list")
    objects = mateconf_get_string_list (root, "object_id_list")
    applets = mateconf_get_string_list (root, "applet_id_list")

    panel_settings = Gio.Settings.new("org.mate.panel")
    panel_settings.delay()

    # new lists (applets are together with objects in 1.6)
    new_toplevels = []
    new_objects = []
    new_applets_without_toplevel = []
    new_bottom_toplevel = None
    new_top_toplevel = None

    # import objects
    for obj in objects:
        debug_message("object", obj)
        mateconf_obj_file = os.path.join(mateconf_user_path, "apps/panel/objects", obj, "%mateconf.xml")
        if not os.path.exists(mateconf_obj_file):
            debug_message ("object", obj, "mateconf file not found!")
            continue
        try:
            obj_tree = ElementTree.parse(mateconf_obj_file)
            obj_root = obj_tree.getroot()
        except:
            debug_message ("object", obj, "mateconf file invalid!")
            continue
        obj_id = gsettings_id(obj)
        obj_settings = Gio.Settings.new_with_path("org.mate.panel.object", "/org/mate/panel/objects/%s/" % obj_id)
        new_objects.append(obj_id)

        for child in obj_root:
            # toplevel-id
            if child.attrib["name"] == "toplevel_id":
                obj_settings.set_string("toplevel-id", gsettings_id(child[0].text))
            # object-type
            elif child.attrib["name"] == "object_type":
                obj_type = child[0].text
                # changed types in 1.6
                if obj_type == "launcher-object":
                    obj_type = "launcher"
                elif obj_type == "action-applet":
                    obj_type = "action"
                elif obj_type == "drawer-object":
                    obj_type = "drawer"
                obj_settings.set_string("object-type", obj_type)
            # other settings
            elif "type" in child.attrib:
                name = gsettings_id(child.attrib["name"])
                # boolean
                if child.attrib["type"] == "bool":
                    if child.attrib["value"] == "true":
                        obj_settings.set_boolean(name, True)
                    else:
                        obj_settings.set_boolean(name, False)
                # int
                elif child.attrib["type"] == "int":
                    obj_settings.set_int(name, int(child.attrib["value"]))
                # string
                elif child.attrib["type"] == "string":
                    obj_settings.set_string(name, child[0].text)

    # import applets
    for app in applets:
        debug_message("applet", app)
        mateconf_app_file = os.path.join(mateconf_user_path, "apps/panel/applets", app, "%mateconf.xml")
        if not os.path.exists(mateconf_app_file):
            debug_message ("applet", app, "mateconf file not found!")
        try:
            app_tree = ElementTree.parse(mateconf_app_file)
            app_root = app_tree.getroot()
        except:
            debug_message ("applet", app, "mateconf file invalid!")
            # empty mateconf file, try to create an applet
            # FIXME: we should add default applets with their values
            # this means use default 1.4 layout, except for linuxmint
            # (that should be the only distribution with a custom panel layout)
            applet_iid = None
            right_stick = False
            position = 0
            # if you add an applet here, add also the same iid couple later
            if "showdesktop" in app:
                applet_iid = "WnckletFactory::ShowDesktopApplet"
            elif "notification" in app:
                applet_iid = "NotificationAreaAppletFactory::NotificationArea"
                right_stick = True
                position = 10
            elif "clock" in app:
                applet_iid = "ClockAppletFactory::ClockApplet"
                right_stick = True
            elif "windowlist" in app:
                applet_iid = "WnckletFactory::WindowListApplet"
                position = 10
            elif "mintmenu" in app:
                applet_iid = "MintMenuAppletFactory::MintMenuApplet"
            if applet_iid != None:
                debug_message ("applet", app, "try to create as '%s'" % applet_iid)
                app_id = gsettings_id(app)
                app_settings = Gio.Settings.new_with_path("org.mate.panel.object", "/org/mate/panel/objects/%s/" % app_id)
                new_objects.append(app_id)
                new_applets_without_toplevel.append(app_id)
                app_settings.set_string("object-type", "applet")
                app_settings.set_string("applet-iid", applet_iid)
                app_settings.set_boolean("panel-right-stick", right_stick)
                app_settings.set_int("position", position)
            continue
        app_id = gsettings_id(app)
        app_settings = Gio.Settings.new_with_path("org.mate.panel.object", "/org/mate/panel/objects/%s/" % app_id)
        new_objects.append(app_id)

        applet_iid = None
        toplevel_id = None
        app_type = None
        for child in app_root:
            # toplevel-id
            if child.attrib["name"] == "toplevel_id":
                toplevel_id = gsettings_id(child[0].text)
                app_settings.set_string("toplevel-id", toplevel_id)
            # object-type
            elif child.attrib["name"] == "object_type":
                app_type = child[0].text
                if app_type == "external-applet":
                    app_type = "applet"
                elif app_type == "matecomponent-applet":
                    app_type = "applet"
                app_settings.set_string("object-type", app_type)
            # applet-iid
            elif child.attrib["name"] == "applet_iid":
                applet_iid = child[0].text
                # changed iids in 1.6
                if applet_iid == "OAFIID:SensorsApplet":
                    applet_iid = "SensorsAppletFactory::SensorsApplet"
                elif applet_iid == "OAFIID:MATE_NetspeedApplet":
                    applet_iid = "NetspeedAppletFactory::NetspeedApplet"
                elif applet_iid == "OAFIID:MATE_DictionaryApplet":
                    applet_iid = "DictionaryAppletFactory::DictionaryApplet"
                elif applet_iid == "OAFIID:Invest_Applet":
                    applet_iid = "InvestAppletFactory::InvestApplet"
                elif applet_iid == "OAFIID:MATE_DictionaryApplet":
                    applet_iid = "DictionaryAppletFactory::DictionaryApplet"
                elif applet_iid == "OAFIID:MATE_InhibitApplet":
                    applet_iid = "InhibitAppletFactory::InhibitApplet"
                elif applet_iid == "OAFIID:TimerApplet":
                    # not yet ready in 1.6 :(
                    applet_iid = "TimerAppletFactory::TimerApplet"
                debug_message ("applet", app, "is '%s'" % applet_iid)
                app_settings.set_string("applet-iid", applet_iid)
            # other settings
            elif "type" in child.attrib:
                name = gsettings_id(child.attrib["name"])
                # boolean
                if child.attrib["type"] == "bool":
                    if child.attrib["value"] == "true":
                        app_settings.set_boolean(name, True)
                    else:
                        app_settings.set_boolean(name, False)
                # int
                elif child.attrib["type"] == "int":
                    app_settings.set_int(name, int(child.attrib["value"]))
                # string
                elif child.attrib["type"] == "string":
                    app_settings.set_string(name, child[0].text)

        # applet-iid could be missing if it is a default applet, but the
        # user changes some values of it (for example, position)
        if applet_iid == None:
            if "showdesktop" in app:
                applet_iid = "WnckletFactory::ShowDesktopApplet"
            elif "notification" in app:
                applet_iid = "NotificationAreaAppletFactory::NotificationArea"
            elif "clock" in app:
                applet_iid = "ClockAppletFactory::ClockApplet"
            elif "windowlist" in app:
                applet_iid = "WnckletFactory::WindowListApplet"
            elif "mintmenu" in app:
                applet_iid = "MintMenuAppletFactory::MintMenuApplet"
            if applet_iid != None:
                debug_message ("applet", app, "id was missing: '%s'" % applet_iid)
                app_settings.set_string("applet-iid", applet_iid)

        if toplevel_id == None:
            new_applets_without_toplevel.append(app_id)
        if app_type == None:
            app_settings.set_string("object-type", "applet")

    # import panels
    for toplevel in toplevels:
        debug_message("toplevel", toplevel)
        mateconf_toplevel_file = os.path.join(mateconf_user_path, "apps/panel/toplevels", toplevel, "%mateconf.xml")
        if not os.path.exists(mateconf_toplevel_file):
            debug_message ("toplevel", toplevel, "mateconf file not found!")
        try:
            toplevel_tree = ElementTree.parse(mateconf_toplevel_file)
            toplevel_root = toplevel_tree.getroot()
        except:
            # empty mateconf file, it's a default panel
            debug_message ("toplevel", toplevel, "mateconf file invalid, creating as default!")
            toplevel_id = gsettings_id(toplevel)
            toplevel_settings = Gio.Settings.new_with_path("org.mate.panel.toplevel", "/org/mate/panel/toplevels/%s/" % toplevel_id)
            if is_linuxmint:
                toplevel_settings.set_string("orientation", "bottom")
            else:
                toplevel_settings.set_string("orientation", "top")
            new_toplevels.append(toplevel_id)
            continue

        toplevel_id = gsettings_id(toplevel)
        toplevel_settings = Gio.Settings.new_with_path("org.mate.panel.toplevel", "/org/mate/panel/toplevels/%s/" % toplevel_id)
        new_toplevels.append(toplevel_id)

        for child in toplevel_root:
            if "type" in child.attrib:
                name = gsettings_id(child.attrib["name"])
                if name == "orientation":
                    if child[0].text == "top":
                        new_top_toplevel = toplevel_id
                    elif child[0].text == "bottom":
                        new_bottom_toplevel = toplevel_id
                # boolean
                if child.attrib["type"] == "bool":
                    if child.attrib["value"] == "true":
                        toplevel_settings.set_boolean(name, True)
                    else:
                        toplevel_settings.set_boolean(name, False)
                # int
                elif child.attrib["type"] == "int":
                    toplevel_settings.set_int(name, int(child.attrib["value"]))
                # string
                elif child.attrib["type"] == "string":
                    toplevel_settings.set_string(name, child[0].text)

    # set created applets to first toplevel
    for app_id in new_applets_without_toplevel:
        app_settings = Gio.Settings.new_with_path("org.mate.panel.object", "/org/mate/panel/objects/%s/" % app_id)
        if is_linuxmint and (new_bottom_toplevel != None):
            app_settings.set_string("toplevel-id", new_bottom_toplevel)
        else:
            app_settings.set_string("toplevel-id", new_toplevels[0])

    # set new toplevels and objects
    panel_settings.set_strv("toplevel-id-list", new_toplevels)
    panel_settings.set_strv("object-id-list", new_objects)

    # apply settings!
    panel_settings.apply()

# ugly hack to migrate terminal default profile
mateconf_terminal_file = os.path.join(mateconf_user_path, "apps/mate-terminal/profiles/Default", "%mateconf.xml")
if os.path.exists(mateconf_terminal_file):

    tree = ElementTree.parse(mateconf_terminal_file)
    root = tree.getroot()

    gsettings_schema = "org.mate.terminal.profile"
    terminal_settings = Gio.Settings.new_with_path(gsettings_schema, "/org/mate/terminal/profiles/default/")
    terminal_settings.delay()

    for child in root:
        # if the key exists, the user has a custom value
        if 'name' in child.attrib:

            gsettings_key = gsettings_id(child.attrib['name'])

            # boolean
            if child.attrib["type"] == "bool":
                if child.attrib["value"] == "true":
                    debug_message(gsettings_schema, gsettings_key, True)
                    terminal_settings.set_boolean(gsettings_key, True)
                else:
                    debug_message(gsettings_schema, gsettings_key, False)
                    terminal_settings.set_boolean(gsettings_key, False)

            # int
            elif child.attrib["type"] == "int":
                debug_message(gsettings_schema, gsettings_key, int(child.attrib["value"]))
                terminal_settings.set_int(gsettings_key, int(child.attrib["value"]))

            # string
            elif child.attrib["type"] == "string":

                value = child[0].text
                if value != None:
                    debug_message(gsettings_schema, gsettings_key, value)
                    terminal_settings.set_string(gsettings_key, value)

            else:
                debug_message("Unknown type", child.attrib["type"])

    # apply settings!
    terminal_settings.apply()

# success!
dialog = Gtk.MessageDialog (None,
                            Gtk.DialogFlags.MODAL,
                            Gtk.MessageType.INFO,
                            Gtk.ButtonsType.OK,
                            "MATE 1.4 settings migration completed!")
dialog.run()
dialog.destroy()