#
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#

import logging
import os.path

from gi.repository import Gdk, Gtk
from twisted.internet import defer

import deluge.component as component
from deluge.common import is_url, resource_filename
from deluge.configmanager import ConfigManager
from deluge.ui.client import client

from .common import get_deluge_icon

log = logging.getLogger(__name__)


def last_tier_trackers_from_liststore(trackers_liststore):
    """Create a list of tracker from existing liststore and find last tier number.

    Args:
        tracker_liststore (Gtk.ListStore): A Gtk.ListStore with [tier (int), tracker (str)] rows.

    Returns:
        tuple(int, list): A tuple containing last tier number and list of trackers.

    """

    last_tier = 0
    trackers_from_liststore = []
    for tier, tracker in trackers_liststore:
        trackers_from_liststore.append(tracker)
        if tier >= last_tier:
            last_tier = tier + 1

    return last_tier, trackers_from_liststore


def trackers_tiers_from_text(text_str=''):
    """Create a list of trackers from text.

    Any duplicate trackers are removed.

    Args:
        text_input (str): A block of text with tracker separated by newlines.

    Returns:
        list: The list of trackers.

    Notes:
        Trackers should be separated by newlines and empty line denotes start of new tier.

    """

    trackers = {}
    tier = 0

    lines = text_str.strip().split('\n')
    for line in lines:
        if not line:
            tier += 1
            continue
        line = line.replace('\\', '/')  # Fix any mistyped urls.
        if is_url(line) and line not in trackers:
            trackers[line] = tier

    return trackers


class EditTrackersDialog:
    def __init__(self, torrent_id, parent=None):
        self.torrent_id = torrent_id
        self.builder = Gtk.Builder()
        self.gtkui_config = ConfigManager('gtk3ui.conf')

        # Main dialog
        self.builder.add_from_file(
            resource_filename(__package__, os.path.join('glade', 'edit_trackers.ui'))
        )
        # add tracker dialog
        self.builder.add_from_file(
            resource_filename(
                __package__, os.path.join('glade', 'edit_trackers.add.ui')
            )
        )
        # edit tracker dialog
        self.builder.add_from_file(
            resource_filename(
                __package__, os.path.join('glade', 'edit_trackers.edit.ui')
            )
        )

        self.dialog = self.builder.get_object('edit_trackers_dialog')
        self.treeview = self.builder.get_object('tracker_treeview')
        self.add_tracker_dialog = self.builder.get_object('add_tracker_dialog')
        self.add_tracker_dialog.set_transient_for(self.dialog)
        self.edit_tracker_entry = self.builder.get_object('edit_tracker_entry')
        self.edit_tracker_entry.set_transient_for(self.dialog)
        self.dialog.set_icon(get_deluge_icon())

        self.load_edit_trackers_dialog_state()

        if parent is not None:
            self.dialog.set_transient_for(parent)

        # Connect the signals
        self.builder.connect_signals(self)

        # Create a liststore for tier, url
        self.liststore = Gtk.ListStore(int, str)

        # Create the columns
        self.treeview.append_column(
            Gtk.TreeViewColumn(_('Tier'), Gtk.CellRendererText(), text=0)
        )
        self.treeview.append_column(
            Gtk.TreeViewColumn(_('Tracker'), Gtk.CellRendererText(), text=1)
        )

        self.treeview.set_model(self.liststore)
        self.liststore.set_sort_column_id(0, Gtk.SortType.ASCENDING)

        self.dialog.connect('delete-event', self._on_delete_event)
        self.dialog.connect('response', self._on_response)
        self.treeview.connect('button_press_event', self.on_button_press_event)

        self.add_tracker_dialog.connect('key-press-event', self.on_key_add_press_event)
        self.add_tracker_dialog.connect('delete-event', self.on_delete_event_add)
        self.edit_tracker_entry.connect('key-press-event', self.on_key_edit_press_event)
        self.edit_tracker_entry.connect('delete-event', self.on_delete_event_edit)

    def run(self):
        # Make sure we have a torrent_id.. if not just return
        if self.torrent_id is None:
            return

        # Get the trackers for this torrent
        session = component.get('SessionProxy')
        session.get_torrent_status(self.torrent_id, ['trackers']).addCallback(
            self._on_get_torrent_status
        )
        client.force_call()

        self.deferred = defer.Deferred()
        return self.deferred

    def __del__(self):
        del self.gtkui_config

    def load_edit_trackers_dialog_state(self):
        w = self.gtkui_config['edit_trackers_dialog_width']
        h = self.gtkui_config['edit_trackers_dialog_height']
        if w is not None and h is not None:
            self.dialog.resize(w, h)

    def on_edit_trackers_dialog_configure_event(self, widget, event):
        self.gtkui_config['edit_trackers_dialog_width'] = event.width
        self.gtkui_config['edit_trackers_dialog_height'] = event.height

    def _on_delete_event(self, widget, event):
        self.deferred.callback(Gtk.ResponseType.DELETE_EVENT)
        self.dialog.destroy()

    def _on_response(self, widget, response):
        if response == 1:
            self.trackers = []

            def each(model, path, _iter, data):
                tracker = {}
                tracker['tier'] = model.get_value(_iter, 0)
                tracker['url'] = model.get_value(_iter, 1)
                self.trackers.append(tracker)

            self.liststore.foreach(each, None)
            if self.old_trackers != self.trackers:
                # Set the torrens trackers
                client.core.set_torrent_trackers(self.torrent_id, self.trackers)
                self.deferred.callback(Gtk.ResponseType.OK)
            else:
                self.deferred.callback(Gtk.ResponseType.CANCEL)
        else:
            self.deferred.callback(Gtk.ResponseType.CANCEL)
        self.dialog.destroy()

    def _on_get_torrent_status(self, status):
        """Display trackers dialog"""
        self.old_trackers = list(status['trackers'])
        for tracker in self.old_trackers:
            self.add_tracker(tracker['tier'], tracker['url'])
        self.treeview.set_cursor(0)
        self.dialog.show()

    def add_tracker(self, tier, url):
        """Adds a tracker to the list"""
        self.liststore.append([tier, url])

    def get_selected(self):
        """Returns the selected tracker"""
        return self.treeview.get_selection().get_selected()[1]

    def on_button_add_clicked(self, widget):
        log.debug('on_button_add_clicked')
        # Show the add tracker dialog
        self.add_tracker_dialog.show()
        self.builder.get_object('textview_trackers').grab_focus()
        self.dialog.set_sensitive(False)

    def on_button_remove_clicked(self, widget):
        log.debug('on_button_remove_clicked')
        selected = self.get_selected()
        if selected is not None:
            self.liststore.remove(selected)

    def on_button_edit_clicked(self, widget):
        """edits an existing tracker on edit button click"""
        log.debug('on_button_edit_clicked')
        self._edit_tracker()

    def on_button_press_event(self, widget, event):
        """edits an existing tracker on double click on tracker name"""
        if event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS:
            log.debug('button_press_event double click')
            self._edit_tracker()

    def _edit_tracker(self):
        """edits an existing tracker"""
        selected = self.get_selected()
        if selected:
            tracker = self.liststore.get_value(selected, 1)
            self.builder.get_object('entry_edit_tracker').set_text(tracker)
            self.edit_tracker_entry.show()
            self.edit_tracker_entry.grab_focus()
            self.dialog.set_sensitive(False)

    def _close_edit_dialog(self):
        self.dialog.set_sensitive(True)
        self.edit_tracker_entry.hide()

    def on_button_edit_cancel_clicked(self, widget):
        """handles the cancel button"""
        log.debug('on_button_edit_cancel_clicked')
        self._close_edit_dialog()

    def on_key_edit_press_event(self, widget, event):
        """handles Escape key press"""
        if event.keyval == Gdk.KEY_Escape:
            log.debug('on_key_edit_press_event')
            self._close_edit_dialog()

    def on_delete_event_edit(self, widget, event):
        """handles the Top-Right X button"""
        log.debug('on_delete_event_edit')
        self._close_edit_dialog()
        return True

    def on_button_edit_ok_clicked(self, widget):
        log.debug('on_button_edit_ok_clicked')
        selected = self.get_selected()
        tracker = self.builder.get_object('entry_edit_tracker').get_text()
        self.liststore.set_value(selected, 1, tracker)
        self.dialog.set_sensitive(True)
        self.edit_tracker_entry.hide()

    def on_button_up_clicked(self, widget):
        log.debug('on_button_up_clicked')
        selected = self.get_selected()
        num_rows = self.liststore.iter_n_children(None)
        if selected is not None and num_rows > 1:
            tier = self.liststore.get_value(selected, 0)
            if tier <= 0:
                return
            new_tier = tier - 1
            # Now change the tier for this tracker
            self.liststore.set_value(selected, 0, new_tier)

    def on_button_down_clicked(self, widget):
        log.debug('on_button_down_clicked')
        selected = self.get_selected()
        num_rows = self.liststore.iter_n_children(None)
        if selected is not None and num_rows > 1:
            tier = self.liststore.get_value(selected, 0)
            new_tier = tier + 1
            # Now change the tier for this tracker
            self.liststore.set_value(selected, 0, new_tier)

    def on_button_add_ok_clicked(self, widget):
        log.debug('on_button_add_ok_clicked')

        # Create a list of trackers from the textview widget
        textview_buf = self.builder.get_object('textview_trackers').get_buffer()
        trackers_text = textview_buf.get_text(
            *textview_buf.get_bounds(), include_hidden_chars=False
        )

        for tracker in trackers_tiers_from_text(trackers_text):
            # Figure out what tier number to use.. it's going to be the highest+1
            # Also check for duplicates
            # Check if there are any entries
            duplicate = False
            highest_tier = -1
            for row in self.liststore:
                tier = row[0]
                if tier > highest_tier:
                    highest_tier = tier
                if tracker == row[1]:
                    duplicate = True
                    break

            # If not a duplicate, then add it to the list
            if not duplicate:
                # Add the tracker to the list
                self.add_tracker(highest_tier + 1, tracker)

        # Clear the entry widget and hide the dialog
        textview_buf.set_text('')
        self.dialog.set_sensitive(True)
        self.add_tracker_dialog.hide()

    def _discard_and_close_add_dialog(self):
        # Clear the entry widget and hide the dialog
        b = Gtk.TextBuffer()
        self.builder.get_object('textview_trackers').set_buffer(b)
        self.dialog.set_sensitive(True)
        self.add_tracker_dialog.hide()

    def on_button_add_cancel_clicked(self, widget):
        """handles the cancel button"""
        log.debug('on_button_add_cancel_clicked')
        self._discard_and_close_add_dialog()

    def on_key_add_press_event(self, widget, event):
        """handles Escape key press"""
        if event.keyval == Gdk.KEY_Escape:
            log.debug('on_key_add_press_event')
            self._discard_and_close_add_dialog()

    def on_delete_event_add(self, widget, event):
        """handles the Top-Right X button"""
        log.debug('on_delete_event_add')
        self._discard_and_close_add_dialog()
        return True
