"""
/***************************************************************************
Name                 : GeoODK Converter
Description          : anchor class that provides interface for user input
                        when generating the XFORM document.
Date                 : 26/May/2017
copyright            : (C) 2017 by UN-Habitat and implementing partners.
                       See the accompanying file CONTRIBUTORS.txt in the root
email                : stdm@unhabitat.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/
"""

import os
import subprocess
import sys

from qgis.PyQt import uic

from qgis.PyQt.QtCore import (
    Qt,
    QDir,
    pyqtSignal,
)

from qgis.PyQt.QtWidgets import (
    QDialog,
    QAbstractItemView,
    QHeaderView,
    QTableView,
    QDialogButtonBox
)

from stdm.data.configuration.db_items import DbItem
from stdm.geoodk.geoodk_writer import GeoodkWriter
from stdm.settings import current_profile
from stdm.ui.notification import NotificationBar
from stdm.ui.wizard.custom_item_model import EntitiesModel
from stdm.utils.util import enable_drag_sort

from stdm.exceptions import DummyException

# from stdm.geoodk import  FormUploader

FORM_CLASS, _ = uic.loadUiType(os.path.join(
    os.path.dirname(__file__), 'ui_geoodk_converter.ui'))

HOME = QDir.home().path()
FORM_HOME = HOME + '/.stdm/geoodk/forms'

class GeoODKConverter(QDialog, FORM_CLASS):
    def __init__(self, parent=None):
        """Class Constructor."""
        super(GeoODKConverter, self).__init__(parent)

        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-aut o-connect
        self.connect_action = pyqtSignal(str)
        self.setupUi(self)

        self.chk_all.setCheckState(Qt.Checked)
        self.chk_all.setVisible(False)
        self.ck_social_tenure.setChecked(True)
        self.ck_social_tenure.setVisible(False)

        self.entity_model = EntitiesModel()
        self.set_entity_model_view(self.entity_model)

        for col in range(self.tvEntities.horizontalHeader().count()):
            self.tvEntities.horizontalHeader().setSectionResizeMode(col, QHeaderView.Interactive)
        self.tvEntities.horizontalHeader().setStretchLastSection(True)

        enable_drag_sort(self.tvEntities)

        self.tvEntities.setDropIndicatorShown(True)
        self.tvEntities.setDragEnabled(True)
        self.tvEntities.setAcceptDrops(True)
        self.tvEntities.setDragDropMode(QTableView.DragDrop)
        self.tvEntities.setDefaultDropAction(Qt.MoveAction)

        self.tvEntities.setColumnWidth(0, 200)

        self.stdm_config = None
        self.parent = parent
        self.load_profiles()
        #self.check_state_on()

        self.check_geoODK_path_exist()

        self.chk_all.stateChanged.connect(self.check_state_on)
        self.btnShowOutputFolder.clicked.connect(self.onShowOutputFolder)
        # self.btn_upload.clicked.connect(self.upload_generated_form)

        self._notif_bar_str = NotificationBar(self.vlnotification)

        self.buttonBox.button(QDialogButtonBox.Save).setText('Generate')
        title = self.windowTitle()+'- {} Profile'.format(current_profile().name)
        self.setWindowTitle(title)

    def onShowOutputFolder(self):
        output_path = FORM_HOME

        # windows
        if sys.platform.startswith('win32'):
            os.startfile(output_path)

        # *nix systems
        if sys.platform.startswith('linux'):
            subprocess.Popen(['xdg-open', output_path])

        # macOS
        if sys.platform.startswith('darwin'):
            subprocess.Popen(['open', output_path])

    def check_state_on(self):
        """
        Ensure all the items in the list are checked
        :return:
        """
        if self.entity_model.rowCount() > 0:
            for row in range(self.entity_model.rowCount()):
                item = self.entity_model.item(row)
                if self.chk_all.isChecked():
                    item.setCheckState(Qt.Checked)
                else:
                    item.setCheckState(Qt.Unchecked)

    def load_profiles(self):
        """
        Read and load profiles from StdmConfiguration instance
        """
        self.populate_view_models(current_profile())

    def profiles(self):
        """
        Get all profiles
        :return:
        """
        return list(self.load_config().values())

    def populate_view_models(self, profile):
        for entity in profile.entities.values():
            if entity.action == DbItem.DROP:
                continue

            if hasattr(entity, 'user_editable') and entity.TYPE_INFO != 'VALUE_LIST':
                if entity.user_editable == False:
                    continue

            if entity.TYPE_INFO not in ['SUPPORTING_DOCUMENT',
                                        'SOCIAL_TENURE', 'ADMINISTRATIVE_SPATIAL_UNIT',
                                        'ENTITY_SUPPORTING_DOCUMENT', 'ASSOCIATION_ENTITY', 'AUTO_GENERATE_CODE']:

                if entity.TYPE_INFO == 'VALUE_LIST':
                    pass
                else:
                    self.entity_model.add_entity(entity)
        self.set_model_items_selectable()

    def set_entity_model_view(self, entity_model):
        """
        Set our list view to the default model
        :return:
        """
        self.tvEntities.setModel(entity_model)

    def set_model_items_selectable(self):
        """
        Ensure that the entities  are checkable
        :return:
        """
        if self.entity_model.rowCount() > 0:
            for row in range(self.entity_model.rowCount()):
                index = self.entity_model.index(row, 0)
                item_index = self.entity_model.itemFromIndex(index)
                #item_index.setCheckable(True)

    def selected_entities_from_model(self) ->list[str]:
        """
        Get selected entities for conversion to Xform.
        """
        entity_list = []
        if self.entity_model.rowCount() > 0:
            for row in range(self.entity_model.rowCount()):
                item = self.entity_model.item(row)
                #if item.isCheckable() and item.checkState() == Qt.Checked:
                entity_list.append(item.text())
        return entity_list

    def check_geoODK_path_exist(self):
        """
        Check if the geoodk paths are there in the directory
        Otherwise create them
        :return:
        """
        if not os.access(FORM_HOME, os.F_OK):
            os.makedirs(str(FORM_HOME))

    def upload_generated_form(self):
        """
        Upload the generated Xform file to mobile phone.
        This eliminates the process of copying the file
        manually to the mobile device
        :return:
        """
        from stdm.ui.geoodk_mobile_upload import FormUploader
        form_uploader = FormUploader(self)
        form_uploader.exec_()

    def generate_mobile_form(self, profile_entities: list[str]):
        """
        Generate mobile form for entities of the current profile.
        """
        try:
            self._notif_bar_str.clear()
            geoodk_writer = GeoodkWriter(profile_entities, self.str_supported)

            created, create_msg = geoodk_writer.create_xml_file()

            if not created:
                self._notify_bar_str.insertErrorNotification(create_msg)
                return

            data_written, write_msg = geoodk_writer.write_data_to_xform()

            if not data_written:
                self._notify_bar_str.insertErrorNotification(write_msg)
                return

            msg = 'File saved in: {}'
            self._notif_bar_str.insertInformationNotification(write_msg)
            self._notif_bar_str.insertInformationNotification(msg.format(FORM_HOME))

        except DummyException as ex:
            self._notif_bar_str.insertErrorNotification(ex.message +
                                                        ': Unable to generate Mobile Form')

    def accept(self):
        """
        Generate mobile forms based on user selected entities.
        Check if str is enabled, then ensure str tables are enabled.
        :return:
        """
        entities = self.selected_entities_from_model()
        if len(entities) == 0:
            self._notif_bar_str.insertErrorNotification(
                'No entity selected. Please select at least one entity...'
            )
            return

        self.str_supported = False
        if self.ck_social_tenure.isChecked():
            self.str_supported = True
            str_definition = current_profile().social_tenure
            str_definition_party = str_definition.parties[0].short_name
            str_definition_spatial = str_definition.spatial_units[0].short_name
            if str_definition_party not in entities or str_definition_spatial not in entities:
                self._notif_bar_str.insertErrorNotification(
                    'One of the entities required to define str is not selected. Form not saved'
                )
                return

        self.generate_mobile_form(entities)
