# Copyright 2021 Vincent Texier <vit@free.fr>
#
# This software 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 software 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 sys
from typing import Optional

from PyQt5.QtCore import QPoint, Qt
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import QApplication, QMainWindow, QStyledItemDelegate, QWidget

from tikka.domains.application import Application
from tikka.domains.entities.constants import (
    DATA_PATH,
)
from tikka.domains.entities.datapod import DataPod
from tikka.domains.entities.events import (
    DataPodsEvent,
)
from tikka.slots.pyqt.entities.constants import (
    DATAPODS_TABLE_SORT_COLUMN_PREFERENCES_KEY,
    DATAPODS_TABLE_SORT_ORDER_PREFERENCES_KEY,
)
from tikka.slots.pyqt.entities.event_bus import EventBus
from tikka.slots.pyqt.entities.worker import ThreadManager
from tikka.slots.pyqt.models.datapods import DataPodsTableModel
from tikka.slots.pyqt.resources.gui.widgets.network_datapods_rc import (
    Ui_NetworkDataPodsWidget,
)
from tikka.slots.pyqt.widgets.datapod_menu import DataPodPopupMenu
from tikka.slots.pyqt.windows.datapod_add import DataPodAddWindow


class HighlightDataPodDelegate(QStyledItemDelegate):
    """
    Class to highlight connected datapod row
    """

    def __init__(self, table_model, application):
        """
        Initialize HighlightDataPodDelegate instance

        :param table_model: TableModel of
        :param application:
        """
        super().__init__()
        self.table_model = table_model
        self.application = application

    def paint(self, painter, option, index):
        # get current url/row indexer
        current_datapod_url = self.application.datapods.get_current_url()
        datapod = self.table_model.datapods[index.row()]
        # check connection...
        if datapod.url == current_datapod_url:
            color = (
                QColor("lightgreen")
                if self.application.connections.datapod.is_connected()
                else QColor("lightcoral")
            )
            painter.fillRect(option.rect, color)

        # paint rectangle
        super().paint(painter, option, index)


class NetworkDataPodsWidget(QWidget, Ui_NetworkDataPodsWidget):
    """
    NetworkDataPodsWidget class
    """

    def __init__(
        self,
        application: Application,
        parent: Optional[QWidget] = None,
    ) -> None:
        """
        Init NetworkDataPodsWidget instance

        :param application: Application instance
        :param parent: MainWindow instance
        """
        super().__init__(parent=parent)
        self.setupUi(self)

        self.application = application
        self._ = self.application.translator.gettext

        self.event_bus = EventBus.instance()
        self.event_bus.datapod_connected.connect(self.on_datapod_connection_event)
        self.event_bus.datapod_disconnected.connect(self.on_datapod_connection_event)
        self.event_bus.repository_data_updated.connect(
            self.on_repository_data_updated_event
        )
        self.event_bus.currency_opened.connect(self.on_repository_data_updated_event)

        self.selected_datapod: Optional[DataPod] = None

        self.datapods_table_model = DataPodsTableModel(self.application)
        self.dataPodsTableView.setModel(self.datapods_table_model)
        self.dataPodsTableView.resizeColumnsToContents()
        self.dataPodsTableView.customContextMenuRequested.connect(
            self.on_datapod_context_menu
        )
        self.dataPodsTableView.setItemDelegate(
            HighlightDataPodDelegate(self.datapods_table_model, self.application)
        )
        pref_sort_column = self.application.repository.preferences.get(
            DATAPODS_TABLE_SORT_COLUMN_PREFERENCES_KEY
        )
        if pref_sort_column is None:
            sort_column = 0
        else:
            sort_column = int(pref_sort_column)

        pref_sort_order = self.application.repository.preferences.get(
            DATAPODS_TABLE_SORT_ORDER_PREFERENCES_KEY
        )
        if pref_sort_order is None:
            sort_order = int(Qt.SortOrder.AscendingOrder)
        else:
            sort_order = int(pref_sort_order)
        self.dataPodsTableView.sortByColumn(sort_column, sort_order)

        # events
        self.addDataPodButton.clicked.connect(self._on_add_datapod_button_clicked)
        self.dataPodsTableView.doubleClicked.connect(self.on_datapod_table_double_click)

        # subscribe to application events
        self.application.event_dispatcher.add_event_listener(
            DataPodsEvent.EVENT_TYPE_LIST_CHANGED, self._on_datapod_list_changed_event
        )

        ##############################
        # ASYNC METHODS
        ##############################
        self.thread_manager: ThreadManager = ThreadManager(self)

    def on_datapod_connection_event(self):
        """
        Triggered when datapod connection has changed

        :return:
        """
        self.dataPodsTableView.viewport().update()

    def _on_add_datapod_button_clicked(self):
        """
        Trigger when user click on add data pod button

        :return:
        """
        DataPodAddWindow(self.application, self).exec_()

    def on_repository_data_updated_event(self, _=None):
        """
        When a currency opened event is triggered

        :param _: CurrencyEvent instance
        :return:
        """
        # update model
        self.dataPodsTableView.model().init_data()

        # resize view
        self.dataPodsTableView.resizeColumnsToContents()

    def _on_datapod_list_changed_event(self, _):
        """
        When the data pod list has changed

        :param _: DataPodsEvent instance
        :return:
        """
        # update model
        self.dataPodsTableView.model().init_data()
        # resize view
        self.dataPodsTableView.resizeColumnsToContents()

    def on_datapod_context_menu(self, position: QPoint):
        """
        When right button on data pod table view

        :param position: QPoint instance
        :return:
        """
        index = self.dataPodsTableView.indexAt(position)
        if index.isValid():
            # get selected node
            row = index.row()
            datapod = self.datapods_table_model.datapods[row]
            # display popup menu at click position
            DataPodPopupMenu(self.application, datapod).exec_(
                self.dataPodsTableView.mapToGlobal(position)
            )

    def on_datapod_table_double_click(self, index):
        """
        Triggered when user double-click on a row of datapod table view
        """
        row = index.row()
        # check index...
        if 0 <= row < len(self.datapods_table_model.datapods):
            self.selected_datapod = self.datapods_table_model.datapods[row]
            self.thread_manager.start(
                self.toggle_datapod_connection,
                self._on_finished_toggle_datapod_connection,
            )

    def toggle_datapod_connection(self):
        """
        Toggle data pod connection

        :return:
        """
        if self.selected_datapod is None:
            return
        # current url connected
        current_datapod_url = self.application.datapods.get_current_url()

        # if same server...
        if self.selected_datapod.url == current_datapod_url:
            # toggle connection...
            if self.application.connections.datapod.is_connected():
                self.application.datapods.network_disconnect()
            else:
                self.application.datapods.network_connect()
        else:
            # connect to another server...
            self.application.datapods.set_current_url(self.selected_datapod.url)
            self.application.datapods.network_connect()

    def _on_finished_toggle_datapod_connection(self):
        """
        Triggered when toggle_datapod_connection is finished

        :return:
        """
        if self.application.connections.datapod.is_connected():
            self.event_bus.datapod_connected.emit()
        else:
            self.event_bus.datapod_disconnected.emit()


if __name__ == "__main__":
    qapp = QApplication(sys.argv)

    application_ = Application(DATA_PATH)

    main_window = QMainWindow()
    main_window.show()

    main_window.setCentralWidget(NetworkDataPodsWidget(application_, main_window))

    sys.exit(qapp.exec_())
