#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006  Donald N. Allingham
#               2008-2009  Stephane Charette <stephanecharette@gmail.com>
#               2009       Gary Burton
#               2011       Robert Cheramy <robert@cheramy.net>
# Copyright (C) 2011       Tim G L Lyons
# Copyright (C) 2013       Nick Hall
#
# 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 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 General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#

#-------------------------------------------------------------------------
#
# Standard python modules
#
#-------------------------------------------------------------------------
import os

#-------------------------------------------------------------------------
#
# GTK/Gnome modules
#
#-------------------------------------------------------------------------
from gi.repository import GdkPixbuf
from gi.repository import Gdk

#-------------------------------------------------------------------------
#
# gramps modules
#
#-------------------------------------------------------------------------
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.sgettext
from ..utils import open_file_with_default_application
from gramps.gen.const import THUMBSCALE
from gramps.gen.mime import get_description, get_type
from gramps.gen.utils.thumbnails import get_thumbnail_image, find_mime_type_pixbuf
from gramps.gen.utils.file import (media_path_full, find_file, create_checksum)
from gramps.gen.lib import NoteType
from gramps.gen.db import DbTxn
from ..glade import Glade
from .displaytabs import (CitationEmbedList, MediaAttrEmbedList, MediaBackRefList,
                         NoteTab)
from ..widgets import (MonitoredSpinButton, MonitoredEntry, PrivacyButton,
                       MonitoredDate, MonitoredTagList, SelectionWidget, Region)
from .editreference import RefTab, EditReference
from .addmedia import AddMedia
from gramps.gen.const import URL_MANUAL_SECT2

#-------------------------------------------------------------------------
#
# Constants
#
#-------------------------------------------------------------------------

WIKI_HELP_PAGE = URL_MANUAL_SECT2
WIKI_HELP_SEC = _('manual|Media_Reference_Editor_dialog')

#-------------------------------------------------------------------------
#
# EditMediaRef
#
#-------------------------------------------------------------------------
class EditMediaRef(EditReference):

    def __init__(self, state, uistate, track, media, media_ref, update):
        EditReference.__init__(self, state, uistate, track, media,
                               media_ref, update)
        if not self.source.get_handle():
            #show the addmedia dialog immediately, with track of parent.
            AddMedia(state, self.uistate, self.track, self.source,
                           self._update_addmedia)

    def _local_init(self):

        self.top = Glade()
        self.set_window(self.top.toplevel,
                        self.top.get_object('title'),
                        _('Media Reference Editor'))
        self.setup_configs('interface.media-ref', 600, 450)

        self.define_warn_box(self.top.get_object("warn_box"))
        self.top.get_object("label427").set_text(_("Y coordinate|Y"))
        self.top.get_object("label428").set_text(_("Y coordinate|Y"))

        tblref = self.top.get_object('table50')
        self.notebook_ref = self.top.get_object('notebook_ref')
        self.track_ref_for_deletion("notebook_ref")
        self.expander = self.top.get_object('expander1')
        #recreate start page as GrampsTab
        self.notebook_ref.remove_page(0)
        self.reftab = RefTab(self.dbstate, self.uistate, self.track,
                              _('General'), tblref)
        self.track_ref_for_deletion("reftab")
        tblref = self.top.get_object('table2')
        self.notebook_shared = self.top.get_object('notebook_shared')
        #recreate start page as GrampsTab
        self.notebook_shared.remove_page(0)
        self.track_ref_for_deletion("notebook_shared")
        self.primtab = RefTab(self.dbstate, self.uistate, self.track,
                              _('_General'), tblref)
        self.track_ref_for_deletion("primtab")
        self.rect_pixbuf = None

    def setup_filepath(self):
        self.select = self.top.get_object('file_select')
        self.track_ref_for_deletion("select")
        self.file_path = self.top.get_object("path")
        self.track_ref_for_deletion("file_path")

        self.file_path.set_text(self.source.get_path())
        self.select.connect('clicked', self.select_file)

    def determine_mime(self):
        descr = get_description(self.source.get_mime_type())
        if descr:
            self.mimetext.set_text(descr)

        path = self.file_path.get_text()
        path_full = media_path_full(self.db, path)
        if path != self.source.get_path() and path_full != self.source.get_path():
            #redetermine mime
            mime = get_type(find_file(path_full))
            self.source.set_mime_type(mime)
            descr = get_description(mime)
            if descr:
                self.mimetext.set_text(descr)
            else:
                self.mimetext.set_text(_('Unknown'))
        #if mime type not set, is note
        if not self.source.get_mime_type():
            self.mimetext.set_text(_('Note'))

    def draw_preview(self):
        """
        Draw the two preview images. This method can be called on eg change of
        the path.
        """
        mtype = self.source.get_mime_type()
        if mtype:
            fullpath = media_path_full(self.db, self.source.get_path())
            pb = get_thumbnail_image(fullpath, mtype)
            self.pixmap.set_from_pixbuf(pb)
            self.selection.load_image(fullpath)
        else:
            pb = find_mime_type_pixbuf('text/plain')
            self.pixmap.set_from_pixbuf(pb)
            self.selection.load_image('')

    def _setup_fields(self):

        ebox_shared = self.top.get_object('eventbox')
        ebox_shared.connect('button-press-event', self.button_press_event)
        self.pixmap = self.top.get_object("pixmap")
        self.mimetext = self.top.get_object("type")
        self.track_ref_for_deletion("mimetext")

        coord = self.source_ref.get_rectangle()
        #upgrade path: set invalid (from eg old db) to none

        if coord is not None and coord in (
                (None,)*4,
                (0, 0, 100, 100),
                (coord[0], coord[1])*2
            ):
            coord = None

        if coord is not None:
            self.rectangle = coord
        else:
            self.rectangle = (0, 0, 100, 100)

        self.selection = SelectionWidget()
        self.selection.set_multiple_selection(False)
        self.selection.connect("region-modified", self.region_modified)
        self.selection.connect("region-created", self.region_modified)
        self.expander.connect("activate", self.selection.expander)
        frame = self.top.get_object("frame9")
        frame.add(self.selection)
        self.track_ref_for_deletion("selection")
        self.selection.show()

        self.setup_filepath()
        self.determine_mime()

        corners = ["corner1_x", "corner1_y", "corner2_x", "corner2_y"]

        if coord and isinstance(coord, tuple):
            for index, corner in enumerate(corners):
                self.top.get_object(corner).set_value(coord[index])
        else:
            for corner, value in zip(corners, [0, 0, 100, 100]):
                self.top.get_object(corner).set_value(value)

        if self.dbstate.db.readonly:
            for corner in corners:
                self.top.get_object(corner).set_sensitive(False)

        self.corner1_x_spinbutton = MonitoredSpinButton(
            self.top.get_object("corner1_x"),
            self.set_corner1_x,
            self.get_corner1_x,
            self.db.readonly)
        self.track_ref_for_deletion("corner1_x_spinbutton")

        self.corner1_y_spinbutton = MonitoredSpinButton(
            self.top.get_object("corner1_y"),
            self.set_corner1_y,
            self.get_corner1_y,
            self.db.readonly)
        self.track_ref_for_deletion("corner1_y_spinbutton")

        self.corner2_x_spinbutton = MonitoredSpinButton(
            self.top.get_object("corner2_x"),
            self.set_corner2_x,
            self.get_corner2_x,
            self.db.readonly)
        self.track_ref_for_deletion("corner2_x_spinbutton")

        self.corner2_y_spinbutton = MonitoredSpinButton(
            self.top.get_object("corner2_y"),
            self.set_corner2_y,
            self.get_corner2_y,
            self.db.readonly)
        self.track_ref_for_deletion("corner2_y_spinbutton")

        self.descr_window = MonitoredEntry(
            self.top.get_object("description"),
            self.source.set_description,
            self.source.get_description,
            self.db.readonly)

        self.ref_privacy = PrivacyButton(
            self.top.get_object("private"),
            self.source_ref,
            self.db.readonly)

        self.gid = MonitoredEntry(
            self.top.get_object("gid"),
            self.source.set_gramps_id,
            self.source.get_gramps_id,
            self.db.readonly)

        self.privacy = PrivacyButton(
            self.top.get_object("privacy"),
            self.source,
            self.db.readonly)

        self.path_obj = MonitoredEntry(
            self.top.get_object("path"),
            self.source.set_path,
            self.source.get_path,
            self.db.readonly)

        self.date_field = MonitoredDate(
            self.top.get_object("date_entry"),
            self.top.get_object("date_edit"),
            self.source.get_date_object(),
            self.uistate, self.track,
            self.db.readonly)

        self.tags = MonitoredTagList(
            self.top.get_object("tag_label"),
            self.top.get_object("tag_button"),
            self.source.set_tag_list,
            self.source.get_tag_list,
            self.db,
            self.uistate, self.track,
            self.db.readonly)

    def _post_init(self):
        """
        Initialization that must happen after the window is shown.
        """
        self.draw_preview()
        self.update_region()

    def set_corner1_x(self, value):
        """
        Callback for the signal handling of the spinbutton for the first
        corner x coordinate of the subsection.
        Updates the subsection thumbnail using the given value

        @param value: the first corner x coordinate of the subsection in int
        """

        self.rectangle = (value,) + self.rectangle[1:]
        self.update_region()

    def set_corner1_y(self, value):
        """
        Callback for the signal handling of the spinbutton for the first
        corner y coordinate of the subsection.
        Updates the subsection thumbnail using the given value

        @param value: the first corner y coordinate of the subsection in int
        """

        self.rectangle = self.rectangle[:1] + (value,) + self.rectangle[2:]
        self.update_region()

    def set_corner2_x(self, value):
        """
        Callback for the signal handling of the spinbutton for the second
        corner x coordinate of the subsection.
        Updates the subsection thumbnail using the given value

        @param value: the second corner x coordinate of the subsection in int
        """

        self.rectangle = self.rectangle[:2] + (value,) + self.rectangle[3:]
        self.update_region()

    def set_corner2_y(self, value):
        """
        Callback for the signal handling of the spinbutton for the second
        corner y coordinate of the subsection.
        Updates the subsection thumbnail using the given value

        @param value: the second corner y coordinate of the subsection in int
        """

        self.rectangle = self.rectangle[:3] + (value,)
        self.update_region()

    def get_corner1_x(self):
        """
        Callback for the signal handling of the spinbutton for the first corner
        x coordinate of the subsection.

        @returns: the first corner x coordinate of the subsection or 0 if
                  there is no selection
        """
        return self.rectangle[0]

    def get_corner1_y(self):
        """
        Callback for the signal handling of the spinbutton for the first corner
        y coordinate of the subsection.

        @returns: the first corner y coordinate of the subsection or 0 if
                  there is no selection
        """
        return self.rectangle[1]

    def get_corner2_x(self):
        """
        Callback for the signal handling of the spinbutton for the second
        corner x coordinate of the subsection.

        @returns: the second corner x coordinate of the subsection or 100 if
                  there is no selection
        """
        return self.rectangle[2]

    def get_corner2_y(self):
        """
        Callback for the signal handling of the spinbutton for the second
        corner x coordinate of the subsection.

        @returns: the second corner x coordinate of the subsection or 100 if
                  there is no selection
        """
        return self.rectangle[3]

    def update_region(self):
        """
        Updates the thumbnail of the specified subsection.
        """
        if not self.selection.is_image_loaded():
            return
        real = self.selection.proportional_to_real_rect(self.rectangle)
        region = Region(real[0], real[1], real[2], real[3])
        self.selection.set_regions([region])
        self.selection.select(region)  # update the selection box shown
        self.selection.refresh()

    def region_modified(self, widget):
        """
        Update new values when the selection is changed.
        """
        real = self.selection.get_selection()
        coords = self.selection.real_to_proportional_rect(real)
        self.corner1_x_spinbutton.set_value(coords[0])
        self.corner1_y_spinbutton.set_value(coords[1])
        self.corner2_x_spinbutton.set_value(coords[2])
        self.corner2_y_spinbutton.set_value(coords[3])

    def build_menu_names(self, person):
        """
        Provide the information needed by the base class to define the
        window management menu entries.
        """
        if self.source:
            submenu_label = _('Media: %s')  % self.source.get_gramps_id()
        else:
            submenu_label = _('New Media')
        return (_('Media Reference Editor'),submenu_label)

    def button_press_event(self, obj, event):
        if (event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS
                and event.button == 1):
            photo_path = media_path_full(self.db, self.source.get_path())
            open_file_with_default_application(photo_path, self.uistate)

    def _update_addmedia(self, obj):
        """
        Called when the add media dialog has been called.
        This allows us to update the main form in response to
        any changes: Redraw relevant fields: description, mimetype and path
        """
        for obj in (self.descr_window, self.path_obj):
            obj.update()
        self.determine_mime()
        self.update_checksum()
        self.draw_preview()

    def update_checksum(self):
        self.uistate.set_busy_cursor(True)
        media_path = media_path_full(self.dbstate.db, self.source.get_path())
        self.source.set_checksum(create_checksum(os.path.normpath(media_path)))
        self.uistate.set_busy_cursor(False)

    def select_file(self, val):
        self.determine_mime()
        path = self.file_path.get_text()
        self.source.set_path(path)
        AddMedia(self.dbstate, self.uistate, self.track, self.source,
                       self._update_addmedia)

    def _connect_signals(self):
        self.define_cancel_button(self.top.get_object('button84'))
        self.define_ok_button(self.top.get_object('button82'),self.save)
        # TODO help button (rename glade button name)
        self.define_help_button(self.top.get_object('button104'),
                WIKI_HELP_PAGE, WIKI_HELP_SEC)

    def _connect_db_signals(self):
        """
        Connect any signals that need to be connected.
        Called by the init routine of the base class (_EditPrimary).
        """
        self._add_db_signal('media-rebuild', self.close)
        self._add_db_signal('media-delete', self.check_for_close)

    def _create_tabbed_pages(self):
        """
        Create the notebook tabs and inserts them into the main
        window.
        """
        notebook_ref = self.top.get_object('notebook_ref')
        notebook_src = self.top.get_object('notebook_shared')

        self._add_tab(notebook_src, self.primtab)
        self._add_tab(notebook_ref, self.reftab)

        self.srcref_list = CitationEmbedList(self.dbstate,
                                         self.uistate,
                                         self.track,
                                         self.source_ref.get_citation_list())
        self._add_tab(notebook_ref, self.srcref_list)
        self.track_ref_for_deletion("srcref_list")

        self.attr_list = MediaAttrEmbedList(self.dbstate,self.uistate,self.track,
                                       self.source_ref.get_attribute_list())
        self._add_tab(notebook_ref, self.attr_list)
        self.track_ref_for_deletion("attr_list")

        self.backref_list = MediaBackRefList(self.dbstate,self.uistate,self.track,
                             self.db.find_backlink_handles(self.source.handle),
                             self.enable_warnbox
                             )
        self._add_tab(notebook_src, self.backref_list)
        self.track_ref_for_deletion("backref_list")

        self.note_ref_tab = NoteTab(self.dbstate, self.uistate, self.track,
                                    self.source_ref.get_note_list(),
                                    notetype=NoteType.MEDIAREF)
        self._add_tab(notebook_ref, self.note_ref_tab)
        self.track_ref_for_deletion("note_ref_tab")

        self.src_srcref_list = CitationEmbedList(self.dbstate,
                                             self.uistate,
                                             self.track,
                                             self.source.get_citation_list())
        self._add_tab(notebook_src, self.src_srcref_list)
        self.track_ref_for_deletion("src_srcref_list")

        self.src_attr_list = MediaAttrEmbedList(self.dbstate,self.uistate,self.track,
                                           self.source.get_attribute_list())
        self._add_tab(notebook_src, self.src_attr_list)
        self.track_ref_for_deletion("src_attr_list")

        self.src_note_ref_tab = NoteTab(self.dbstate, self.uistate, self.track,
                                        self.source.get_note_list(),
                                        notetype=NoteType.MEDIA)
        self._add_tab(notebook_src, self.src_note_ref_tab)
        self.track_ref_for_deletion("src_note_ref_tab")

        self._setup_notebook_tabs(notebook_src)
        self._setup_notebook_tabs(notebook_ref)

    def save(self,*obj):

        #first save primary object
        if self.source.handle:
            with DbTxn(_("Edit Media Object (%s)") %
                       self.source.get_description(), self.db) as trans:
                self.db.commit_media(self.source, trans)
        else:
            if self.check_for_duplicate_id('Media'):
                return
            with DbTxn(_("Add Media Object (%s)") %
                       self.source.get_description(), self.db) as trans:
                self.db.add_media(self.source, trans)

        #save reference object in memory
        coord = (
            self.top.get_object("corner1_x").get_value_as_int(),
            self.top.get_object("corner1_y").get_value_as_int(),
            self.top.get_object("corner2_x").get_value_as_int(),
            self.top.get_object("corner2_y").get_value_as_int(),
            )

        #do not set unset or invalid coord

        if coord is not None and coord in (
                (None,)*4,
                (0, 0, 100, 100),
                (coord[0], coord[1])*2
            ):
            coord = None

        self.source_ref.set_rectangle(coord)

        #call callback if given
        if self.update:
            self.update(self.source_ref,self.source)
            self.update = None
        self.close()
