# Copyright (C) 2006-2007 Red Hat, Inc.
# Copyright (C) 2008 One Laptop Per Child
# Copyright (C) 2008-2013 Sugar Labs
# Copyright (C) 2013 Daniel Francis
# Copyright (C) 2013 Walter Bender
#
# 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 logging
from gettext import gettext as _

from gi.repository import GObject
from gi.repository import Gio
from gi.repository import GLib
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GdkPixbuf

from sugar3.graphics import style
from sugar3.graphics.icon import Icon
from sugar3.graphics.icon import CanvasIcon
from sugar3.graphics.palettemenu import PaletteMenuItem
from sugar3.graphics.palettemenu import PaletteMenuItemSeparator
from sugar3.graphics.alert import Alert, ErrorAlert
from sugar3.graphics.xocolor import XoColor
from sugar3.activity import activityfactory
from sugar3 import dispatch
from sugar3.datastore import datastore

from jarabe.view.palettes import JournalPalette
from jarabe.view.palettes import CurrentActivityPalette
from jarabe.view.palettes import ActivityPalette
from jarabe.view.buddyicon import BuddyIcon
from jarabe.view.buddymenu import BuddyMenu
from jarabe.model.buddy import get_owner_instance
from jarabe.model import shell
from jarabe.model import bundleregistry
from jarabe.model import desktop
from jarabe.journal import misc

from jarabe.desktop import schoolserver
from jarabe.desktop.schoolserver import RegisterError
from jarabe.desktop import favoriteslayout
from jarabe.desktop.viewcontainer import ViewContainer
from jarabe.util.normalize import normalize_string

_logger = logging.getLogger('FavoritesView')

_ICON_DND_TARGET = ('activity-icon', Gtk.TargetFlags.SAME_WIDGET, 0)

LAYOUT_MAP = {favoriteslayout.RingLayout.key: favoriteslayout.RingLayout,
              # favoriteslayout.BoxLayout.key: favoriteslayout.BoxLayout,
              # favoriteslayout.TriangleLayout.key:
              # favoriteslayout.TriangleLayout,
              # favoriteslayout.SunflowerLayout.key:
              # favoriteslayout.SunflowerLayout,
              favoriteslayout.RandomLayout.key: favoriteslayout.RandomLayout}
"""Map numeric layout identifiers to uninstantiated subclasses of
`FavoritesLayout` which implement the layouts.  Additional information
about the layout can be accessed with fields of the class."""

_favorites_settings = None


class FavoritesBox(Gtk.VBox):
    __gtype_name__ = 'SugarFavoritesBox'

    def __init__(self, favorite_view):
        Gtk.VBox.__init__(self)

        self.favorite_view = favorite_view
        self._view = FavoritesView(self)
        self.pack_start(self._view, True, True, 0)
        self._view.show()

        self._alert = None

    def set_filter(self, query):
        self._view.set_filter(query)

    def set_resume_mode(self, resume_mode):
        self._view.set_resume_mode(resume_mode)

    def grab_focus(self):
        # overwrite grab focus in order to grab focus from the parent
        self._view.grab_focus()

    def add_alert(self, alert):
        if self._alert is not None:
            self.remove_alert()
        self._alert = alert
        self.pack_start(alert, False, True, 0)
        self.reorder_child(alert, 0)

    def remove_alert(self):
        self.remove(self._alert)
        self._alert = None

    def _get_selected(self, query):
        return self._view._get_selected(query)


class FavoritesView(ViewContainer):
    __gtype_name__ = 'SugarFavoritesView'

    def __init__(self, box):
        self._box = box
        self._layout = None

        owner_icon = OwnerIcon(style.XLARGE_ICON_SIZE)
        owner_icon.connect('register-activate', self.__register_activate_cb)

        current_activity = CurrentActivityIcon()

        ViewContainer.__init__(self, layout=self._layout,
                               owner_icon=owner_icon,
                               activity_icon=current_activity)
        self.set_can_focus(False)

        self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK |
                        Gdk.EventMask.POINTER_MOTION_HINT_MASK)
        self.drag_dest_set(0, [], 0)

        # Drag and drop is set only for the Random layout.  This is
        # the flag that enables or disables it.
        self._dragging_mode = False

        self._drag_motion_hid = None
        self._drag_drop_hid = None
        self._drag_data_received_hid = None

        self._dragging = False
        self._pressed_button = None
        self._press_start_x = 0
        self._press_start_y = 0
        self._hot_x = None
        self._hot_y = None
        self._last_clicked_icon = None

        self._alert = None
        self._resume_mode = True

        GLib.idle_add(self.__connect_to_bundle_registry_cb)

        favorites_settings = get_settings(self._box.favorite_view)
        favorites_settings.changed.connect(self.__settings_changed_cb)
        self._set_layout(favorites_settings.layout)

    def __settings_changed_cb(self, **kwargs):
        favorites_settings = get_settings(self._box.favorite_view)
        layout_set = self._set_layout(favorites_settings.layout)
        if layout_set:
            self.set_layout(self._layout)
            registry = bundleregistry.get_registry()
            for info in registry:
                if registry.is_bundle_favorite(info.get_bundle_id(),
                                               info.get_activity_version(),
                                               self._box.favorite_view):
                    self._add_activity(info)

    def _set_layout(self, layout):
        if layout not in LAYOUT_MAP:
            logging.warn('Unknown favorites layout: %r', layout)
            layout = favoriteslayout.RingLayout.key
            assert layout in LAYOUT_MAP

        if type(self._layout) == LAYOUT_MAP[layout]:
            return False

        if self._layout is not None and self._dragging_mode:
            self.disconnect(self._drag_motion_hid)
            self.disconnect(self._drag_drop_hid)
            self.disconnect(self._drag_data_received_hid)

        if layout == favoriteslayout.RandomLayout.key:
            self._dragging_mode = True
            self._drag_motion_hid = self.connect(
                'drag-motion', self.__drag_motion_cb)
            self._drag_drop_hid = self.connect(
                'drag-drop', self.__drag_drop_cb)
            self._drag_data_received_hid = self.connect(
                'drag-data-received', self.__drag_data_received_cb)
        else:
            self._dragging_mode = False

        self._layout = LAYOUT_MAP[layout]()
        return True

    layout = property(None, _set_layout)

    def do_add(self, child):
        if child != self._owner_icon and child != self._activity_icon:
            self._children.append(child)
            child.connect('button-pres
s-event', self.__button_press_cb)
            child.connect('button-release-event', self.__button_release_cb)
            child.connect('motion-notify-event', self.__motion_notify_event_cb)
            child.connect('drag-begin', self.__drag_begin_cb)
        if child.get_realized():
            child.set_parent_window(self.get_parent_window())
        child.set_parent(self)

    def __button_release_cb(self, widget
, event):
        if self._dragging:
            return True
        else:
            return False

    def __button_press_cb(self, widget, event):
        if event.button == 1 and event.type == Gdk.EventType.BUTTON_PRESS:
            self._last_clicked_icon = widget
            self._pressed_button = event.button
            self._press_start_x = event.x
            self._pres