#!/usr/bin/env python3
#-*- coding: utf-8 -*-
#
# Indicator-Privacy - Unity appindicator to switch privacy settings
#                     http://www.florian-diesch.de/software/indicator-privacy/
#
# Copyright (C) 2013 Florian Diesch <devel@florian-diesch.de>
#
#
# This program 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 3 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 General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#


import gi
gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1')
from gi.repository import Gtk, Gio, GLib
from gi.repository import AppIndicator3 as appindicator

try:
    from zeitgeist.datamodel import Event, Subject
    from zeitgeist.client import ZeitgeistClient
    HAVE_ZEITGEIST = True
except ImportError:
    HAVE_ZEITGEIST = False

import shutil, os, time, subprocess, sys

from gettext import gettext as _
import gettext

# import settings, about, dialogs, messages
from . import settings, about, dialogs, messages   # noqa

SCHEME_SELF = 'net.launchpad.indicator-privacy'
KEY_SAVED_WALLPAPER_URI = 'saved-wallpaper-uri'
KEY_SAVED_WALLPAPER_OPTIONS = 'saved-wallpaper-options'

SCHEME_REMOTE_SEARCH = 'com.canonical.Unity.Lenses'
KEY_REMOTE_SEARCH = 'remote-content-search'

SCHEME_DISPLAY_REAL_NAME = 'com.canonical.indicator.session'
KEY_DISPLAY_REAL_NAME = 'show-real-name-on-panel'

SCHEME_GEOCLUE = 'com.ubuntu.geoip'
KEY_GEOCLUE_URL = 'geoip-url'

SCHEME_WALLPAPER = 'org.gnome.desktop.background'
KEY_WALLPAPER_URI = 'picture-uri'
KEY_WALLPAPER_OPTIONS = 'picture-options'
KEY_SHOW_DESKTOP_ICONS = 'show-desktop-icons'

SCHEME_HUD = 'com.canonical.indicator.appmenu.hud'
KEY_STORE_HUD_USAGE_DATA = 'store-usage-data'

SCHEME_ALTF2_HISTORY = 'com.canonical.Unity.Runner'
KEY_ALTF2_HISTORY = 'history'

if HAVE_ZEITGEIST:
    ALL_EVENTS = Event.new_for_values()
    ID_BLOCK_ALL = 'block-all'


GEOCLUE_OFF_URL='http://localhost:0/'

ALL_GSETTINGS_SCHEMAS = None

def get_unity_version():
    cmd = ['unity', '--version']
    try:
        versionstr = subprocess.check_output(cmd)
    except OSError as e:
        msg = _('Error running %s:\n%s') % (' '.join(cmd), str(e))
        msg += '\n\n'
        msg += _("Maybe you don't have the Unity desktop environment "
                "installed.")
        dialogs.error(msg)
        sys.exit(1)
    version = versionstr.split()[1]
    version = version.decode()
    major = int(version.split('.')[0])
    return major


def get_all_gsettings_schemas():
    return Gio.Settings.list_schemas()

def gsettings_schema_exists(schema):
    global ALL_GSETTINGS_SCHEMAS
    if ALL_GSETTINGS_SCHEMAS is None:
        ALL_GSETTINGS_SCHEMAS = set(get_all_gsettings_schemas())
    return schema in ALL_GSETTINGS_SCHEMAS
    
def create_gsettings_obj(scheme, keys, func):
    if not gsettings_schema_exists(scheme):
        return

    if isinstance(keys, str):
        keys = [keys]
    
    gs = Gio.Settings.new(scheme)
    for key in keys:
        gs.connect('changed::%s' % key, lambda *args: func())
    return gs


def create_check_menu_item(menu, title, callback):
    item = Gtk.CheckMenuItem(label=title)
    item.connect('toggled', callback)
    menu.append(item)
    return item

def create_menu_item(menu, title, callback):
    item = Gtk.MenuItem(label=title)
    item.connect('activate', callback)
    menu.append(item)
    return item
    
def add_menu_separator(menu):
    item = Gtk.SeparatorMenuItem()
    menu.append(item)
        


class IndicatorPrivacy(object):
    def __init__(self):
        gettext.bindtextdomain(settings.GETTEXT_DOMAIN)
        gettext.textdomain(settings.GETTEXT_DOMAIN)
        gettext.bind_textdomain_codeset(settings.GETTEXT_DOMAIN, 'UTF-8')


        if get_unity_version() < 7:
            msg = _('You need at least Unity v7 (Ubuntu 14.04) to run Privacy Indicator.')
            dialogs.error(msg)
            sys.exit(1)
            
                

        self.ind = appindicator.Indicator.new (
           settings.app_name,
           settings.ICON,
           settings.CATEGORY)

        self.ind.set_status (settings.DEFAULT_STATUS)

        self.gs_self = create_gsettings_obj(
            SCHEME_SELF, [], 
            lambda *args: None
            )

        self.gs_remote_search = create_gsettings_obj(
            SCHEME_REMOTE_SEARCH, KEY_REMOTE_SEARCH,
            self.update_remote_search_item)

        self.gs_display_real_name = create_gsettings_obj(
            SCHEME_DISPLAY_REAL_NAME, KEY_DISPLAY_REAL_NAME, 
            self.update_real_name_item)

        self.gs_default_wallpaper = create_gsettings_obj(
            SCHEME_WALLPAPER, [KEY_WALLPAPER_URI, KEY_WALLPAPER_OPTIONS],
            self.update_default_wallpaper_item)

        self.gs_show_desktop_icons = create_gsettings_obj(
            SCHEME_WALLPAPER, KEY_SHOW_DESKTOP_ICONS,
            self.update_show_desktop_icons_item)

        self.gs_geoclue_url = create_gsettings_obj(
            SCHEME_GEOCLUE, KEY_GEOCLUE_URL,
            self.update_geoclue_url_item)

        self.gs_store_hud_usage_data = create_gsettings_obj(
            SCHEME_HUD, KEY_STORE_HUD_USAGE_DATA, 
            self.update_store_hud_usage_data_item)

        self.gs_altf2_history = create_gsettings_obj(
            SCHEME_ALTF2_HISTORY, KEY_ALTF2_HISTORY, 
            lambda *args: None)

        if HAVE_ZEITGEIST:
            CLIENT = ZeitgeistClient()
            self.blacklist = CLIENT._iface.get_extension('Blacklist', 
                                                     'blacklist')
            self.blacklist.connect('TemplateAdded', 
                               self.on_zeitgeist_blacklist_changed)
            self.blacklist.connect('TemplateRemoved', 
                               self.on_zeitgeist_blacklist_changed)
        
        self.create_menu()


    
    def create_menu(self):
        menu = Gtk.Menu()
        
        if self.gs_remote_search:
            self.item_remote_search = create_check_menu_item(
                menu, _('Use online search results in the Dash'),
                self.on_remote_search_toggled)

        if HAVE_ZEITGEIST:
            self.item_zeitgeist = create_check_menu_item(
                menu, _('Record activity (Zeitgeist)'),
                self.on_zeitgeist_toggled)

        if self.gs_store_hud_usage_data:
            self.item_store_hud_usage_data = create_check_menu_item(
                menu, _('Store HUD usage data'),
                self.on_store_hud_usage_data_toggled)

        if self.gs_geoclue_url:
            self.item_geoclue_url = create_check_menu_item(
                menu, _('Use Ubuntu GeoIP service'),
                self.on_geoclue_url_toggled)

        add_menu_separator(menu)

        if self.gs_display_real_name:
            self.item_real_name = create_check_menu_item(
                menu, _('Show real name on panel'),
                self.on_real_name_toggled)
        
        add_menu_separator(menu)

        if self.gs_show_desktop_icons:
            self.item_show_desktop_icons = create_check_menu_item(
                menu, _('Show desktop icons'),
                self.on_show_desktop_icons_toggled)

        if self.gs_default_wallpaper:
            self.item_default_wallpaper = create_check_menu_item(
                menu, _('Use default wallpaper (needs desktop'
                        ' icons activated)'),
                self.on_use_default_walpaper_toggled)


        add_menu_separator(menu)

        if self.gs_altf2_history:
            self.item_clear_altf2_history = create_menu_item(
                menu, _('Clear Alt-F2 history'),
                self.on_clear_altf2_history)

        self.item_clear_recent = create_menu_item(
            menu, _('Clear recent file list'),
            self.on_clear_recent_activate)

        if HAVE_ZEITGEIST:
            self.item_clear_zeitgeist = create_menu_item(
                menu, _('Clear Zeitgeist log'),            
                self.on_clear_zeitgeist)

        add_menu_separator(menu)

        self.item_clear_thumbs = create_menu_item(
            menu, _('Clear thumbnail cache'),
            self.on_clear_thumbs_cache)

        self.item_clear_dvdcss_keys =  create_menu_item(
            menu, _('Clear libdvdcss key cache'),
            self.on_clear_dvdcss_keys)
        
        self.item_clear_gvfs_meta =  create_menu_item(
            menu, _('Clear GVFS metadata'),
            self.on_clear_gvfs_meta)

        add_menu_separator(menu)


        item = Gtk.MenuItem(label='%s' % settings.APP_NAME)
        menu.append(item)
        submenu = Gtk.Menu()
        item.set_submenu(submenu)
        about.add_about_item(submenu)
        about.add_help_menu(submenu)

        item = Gtk.SeparatorMenuItem()
        submenu.append(item)

        create_menu_item(submenu, _('Quit'), lambda *args: self.quit())

        self.update_zeitgeist_item()
        self.update_real_name_item()
        self.update_default_wallpaper_item()
        self.update_show_desktop_icons_item(),
        self.update_remote_search_item()
        self.update_geoclue_url_item()
        self.update_store_hud_usage_data_item()
        menu.show_all()

        self.ind.set_menu(menu)

    def quit(self):
        Gtk.main_quit()

    def run(self):
        Gtk.main()

    def update_zeitgeist_item(self):
        if not HAVE_ZEITGEIST:
            return
        value = self.get_zeitgeist_activated()
        self.item_zeitgeist.set_active(value)
        
    def update_real_name_item(self):
        if not self.gs_display_real_name:
            return
        value = self.gs_display_real_name.get_boolean(
            KEY_DISPLAY_REAL_NAME)
        self.item_real_name.set_active(value)
        
    def update_default_wallpaper_item(self):
        if not self.gs_default_wallpaper:
            return
        uri = self.gs_default_wallpaper.get_value(
            KEY_WALLPAPER_URI)
        options = self.gs_default_wallpaper.get_value(
            KEY_WALLPAPER_OPTIONS)
        default =  self.gs_default_wallpaper.get_default_value(
            KEY_WALLPAPER_URI)
        is_default = uri == default
        if not is_default:
            self.gs_self.set_value(KEY_SAVED_WALLPAPER_URI, uri)
            self.gs_self.set_value(KEY_SAVED_WALLPAPER_OPTIONS, options)
        self.item_default_wallpaper.set_active(is_default)

    def update_show_desktop_icons_item(self):
        if not self.gs_show_desktop_icons:
            return
        value = self.gs_show_desktop_icons.get_boolean(
            KEY_SHOW_DESKTOP_ICONS)
        self.item_show_desktop_icons.set_active(value)
        self.item_default_wallpaper.set_sensitive(value)

    def update_remote_search_item(self):
        if not self.gs_remote_search:
            return
        value = self.gs_remote_search.get_string(KEY_REMOTE_SEARCH)
        self.item_remote_search.set_active(value == 'all')

    def update_store_hud_usage_data_item(self):
        if not self.gs_store_hud_usage_data:
            return
        value = self.gs_store_hud_usage_data.get_boolean(
            KEY_STORE_HUD_USAGE_DATA)
        self.item_store_hud_usage_data.set_active(value)
   
    def update_geoclue_url_item(self):
        if not self.gs_geoclue_url:
            return
        value = self.gs_geoclue_url.get_string(KEY_GEOCLUE_URL)
        self.item_geoclue_url.set_active(value != GEOCLUE_OFF_URL)

    def get_zeitgeist_activated(self):
        if not HAVE_ZEITGEIST:
            return False
        try:
            templates = self.blacklist.GetTemplates()
            return not any(map(ALL_EVENTS.matches_template, 
                                          templates.values())
                           )
        except Exception as e:
            print ("Error connecting Zeitgeist: %s"% e)
            return False

############
# zeitgeist
#
    def on_zeitgeist_blacklist_changed(self, *args):
        self.update_zeitgeist_item()

#######
# menu

    def on_remote_search_toggled(self, *args):
        value = 'all' if self.item_remote_search.get_active() else 'none'
        current_value = self.gs_remote_search.get_string(KEY_REMOTE_SEARCH)
        if value == current_value:
            return
        self.gs_remote_search.set_string(KEY_REMOTE_SEARCH, value)

    def on_real_name_toggled(self, *args):
        value = self.item_real_name.get_active()
        current_value =  self.gs_display_real_name.get_boolean(
            KEY_DISPLAY_REAL_NAME)
        if value == current_value:
            return
        self.gs_display_real_name.set_boolean(KEY_DISPLAY_REAL_NAME, value)

    def on_geoclue_url_toggled(self, *args):
        value = None if self.item_geoclue_url.get_active() else GEOCLUE_OFF_URL
        current_value = self.gs_geoclue_url.get_string(KEY_GEOCLUE_URL)
        if value == current_value:
            return
        if value is None:
            self.gs_geoclue_url.reset(KEY_GEOCLUE_URL)
        else:            
            self.gs_geoclue_url.set_string(KEY_GEOCLUE_URL, value) 

    def on_zeitgeist_toggled(self,  widget, *args):
        current_state = self.get_zeitgeist_activated()
        value = widget.get_active()        
        if value == current_state:
            return
        if value:
            self.blacklist.RemoveTemplate(ID_BLOCK_ALL)
        else:
            self.blacklist.AddTemplate(ID_BLOCK_ALL, ALL_EVENTS)

    def on_clear_altf2_history(self,  widget, *args):
        def func():
            self.gs_altf2_history.set_strv(KEY_ALTF2_HISTORY, None)

        dialogs.cleaning_dlg(
            func, messages.clear_altf2_history_msg,
            messages.clear_altf2_history_explanation)

    def on_clear_recent_activate(self,  widget, *args):       
        def func():
            rman = Gtk.RecentManager.get_default()
            rman.purge_items()
        dialogs.cleaning_dlg(func, messages.clear_recent_activate_msg,
                             messages.clear_recent_activate_explanation)


    def on_store_hud_usage_data_toggled(self, *args):
        value = self.item_store_hud_usage_data.get_active()
        current_value =  self.gs_store_hud_usage_data.get_boolean(
            KEY_STORE_HUD_USAGE_DATA)
        if value == current_value:
            return
        self.gs_store_hud_usage_data.set_boolean(
            KEY_STORE_HUD_USAGE_DATA, value)

    def on_show_desktop_icons_toggled(self, *args):
        value = self.item_show_desktop_icons.get_active()
        self.gs_show_desktop_icons.set_boolean(KEY_SHOW_DESKTOP_ICONS, 
                                               value)
          
    def on_use_default_walpaper_toggled(self, *args):
        active = self.item_default_wallpaper.get_active()
        show_icons = self.gs_show_desktop_icons.get_boolean(
            KEY_SHOW_DESKTOP_ICONS)
    
        self.gs_default_wallpaper.delay()
        if active:
            default_uri =  self.gs_default_wallpaper.get_default_value(
                KEY_WALLPAPER_URI)
            uri = self.gs_default_wallpaper.get_value(
                KEY_WALLPAPER_URI)
            options = self.gs_default_wallpaper.get_value(
                KEY_WALLPAPER_OPTIONS)
            if uri not in (default_uri, ''):
                self.gs_self.set_value(KEY_SAVED_WALLPAPER_URI, uri)
                self.gs_self.set_value(KEY_SAVED_WALLPAPER_OPTIONS, options)

            self.gs_default_wallpaper.reset(KEY_WALLPAPER_URI)
            self.gs_default_wallpaper.reset(KEY_WALLPAPER_OPTIONS)
        else:
            uri = self.gs_self.get_value(KEY_SAVED_WALLPAPER_URI)
            options = self.gs_self.get_value(KEY_SAVED_WALLPAPER_OPTIONS)
            self.gs_default_wallpaper.set_value(KEY_WALLPAPER_URI, 
                                                uri)
            self.gs_default_wallpaper.set_value(KEY_WALLPAPER_OPTIONS, 
                                                options)
        self.gs_default_wallpaper.apply()


    def on_clear_recent_activate(self,  widget, *args):       
        def func():
            rman = Gtk.RecentManager.get_default()
            rman.purge_items()
        dialogs.cleaning_dlg(func, messages.clear_recent_activate_msg,
                             messages.clear_recent_activate_explanation)
        
    def on_clear_zeitgeist(self,  widget, *args):
        def func():
            client = ZeitgeistClient()
            def callback(event_ids):
                es = []
                # workaround for https://dev.guardianproject.info/issues/555
                for e in event_ids:
                    es.append(e)
                    if len(es) > 10:
                        client.delete_events(es)
                        es = []
                        while Gtk.events_pending():
                            Gtk.main_iteration()
                        time.sleep(0.1)
                    else:
                        es.append(e)
                client.delete_events(es)
                
            client.find_event_ids_for_templates([ALL_EVENTS], 
                                                callback, num_events=0)
        dialogs.cleaning_dlg(func, messages.clear_zeitgeist_msg,
                             messages.clear_zeitgeist_explanation)



    def on_clear_thumbs_cache(self,  widget, *args):
        def func():
            if not os.path.isdir(settings.THUMBS_CACHE_PATH):
                return
            try:
                shutil.rmtree(settings.THUMBS_CACHE_PATH)
                os.makedirs(settings.THUMBS_CACHE_PATH)
            except Exception as e:
                dialogs.error(messages.clear_thumbs_cache_error % e)
                return True
            
        dialogs.cleaning_dlg(func, messages.clear_thumbs_cache_msg,
                             messages.clear_thumbs_cache_explanation)


    def on_clear_gvfs_meta(self,  widget, *args):
        def func():
            if not os.path.isdir(settings.GVFS_METADATA_PATH):
                return
            try:
                shutil.rmtree(settings.GVFS_METADATA_PATH)
            except Exception as e:
                dialogs.error(messages.clear_gvfs_meta_error_error % e)
                return True
            
        dialogs.cleaning_dlg(func, messages.clear_gvfs_meta_msg,
                              messages.clear_gvfs_meta_explanation)


    def on_clear_dvdcss_keys(self,  widget, *args):
        def func():
            if not os.path.isdir(settings.LIBDVDCSS_KEYS_PATH):
                return
            try:
                shutil.rmtree(settings.LIBDVDCSS_KEYS_PATH)
            except Exception as e:
                dialogs.error(messages.clear_dvdcss_keys_error % e)
                return True
            
        dialogs.cleaning_dlg(func, messages.clear_dvdcss_keys_msg,
                             messages.clear_dvdcss_keys_explanation)



def main():
    ind = IndicatorPrivacy()
    ind.run()
    

if __name__ == '__main__':
    main()
