# -*- coding: utf-8 -*-
"""
/***************************************************************************
 VarMixViewer
                                 A QGIS plugin
 Shows results from VarMix from NOVASIB INFOSYS as geometry.
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2018-05-16
        git sha              : $Format:%H$
        copyright            : (C) 2018 by Florian Timm
        email                : qgis@florian-timm.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/
"""
from PyQt5.QtCore import QSettings, QTranslator, qVersion, QCoreApplication
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QAction

from qgis.core import QgsVectorLayer, QgsDistanceArea, QgsCoordinateReferenceSystem, QgsCoordinateTransformContext, QgsFeature, QgsPointXY, \
    QgsGeometry, QgsExpression, QgsFeatureRequest, QgsProject

# Initialize Qt resources from file resources.py
from .resources import *
# Import the code for the dialog
from .VarMixViewer_dialog import VarMixViewerDialog
from .PointLayer_dialog import PointLayerDialog
import os.path


class VarMixViewer:
    """QGIS Plugin Implementation."""

    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(
            self.plugin_dir,
            'i18n',
            'VarMixViewer_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)

        # Create the dialog (after translation) and keep reference
        self.dlg = VarMixViewerDialog()
        self.pld = PointLayerDialog()

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr(u'&VarMixViewer')
        # TODO: We are going to let the user set this up in a future iteration
        self.toolbar = self.iface.addToolBar(u'VarMixViewer')
        self.toolbar.setObjectName(u'VarMixViewer')

        self.distanz = QgsDistanceArea()
        self.distanz.setSourceCrs(QgsCoordinateReferenceSystem(25832), QgsCoordinateTransformContext())
        self.distanz.setEllipsoid('GRS80')

        self.netz = QgsVectorLayer()

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('VarMixViewer', message)


    def add_action(
        self,
        icon_path,
        text,
        callback,
        enabled_flag=True,
        add_to_menu=True,
        add_to_toolbar=True,
        status_tip=None,
        whats_this=None,
        parent=None):
        """Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToVectorMenu(
                self.menu,
                action)

        self.actions.append(action)

        return action

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        icon_path = ':/plugins/VarMixViewer/icon.png'
        self.add_action(
            icon_path,
            text=self.tr(u'VarMixViewer'),
            callback=self.run,
            parent=self.iface.mainWindow())

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginVectorMenu(
                self.tr(u'&VarMixViewer'),
                action)
            self.iface.removeToolBarIcon(action)
        # remove the toolbar
        del self.toolbar

    @staticmethod
    def calc_factor(section, xls_feature):
        section_length = section['Abschnittslaenge']
        geom = section.geometry()
        geo_length = geom.length()
        factor = 1
        if section_length > 0:
            factor = geo_length / section_length
        vst = xls_feature['vst'] * factor
        bst = xls_feature['bst'] * factor
        points = geom.asPolyline()
        return points, bst, vst

    def cut_line(self, section, xls_feature):
        feat = QgsFeature(xls_feature)
        points, bst, vst = self.calc_factor(section, xls_feature)

        p_old = QgsPointXY(points[0])

        sum = 0
        line = []
        for p in points:
            p = QgsPointXY(p)
            dist = self.distanz.measureLine(p_old, p)
            sum += dist
            if sum > vst and len(line) == 0:
                # print("Anfang")
                part = (sum - vst) / dist

                dx = p.x() - p_old.x()
                dy = p.y() - p_old.y()

                x = p.x() - part * dx
                y = p.y() - part * dy

                line.append(QgsPointXY(x, y))
            if sum > bst:
                part = (sum - bst) / dist
                # print(part)

                dx = p.x() - p_old.x()
                dy = p.y() - p_old.y()

                x = p.x() - part * dx
                y = p.y() - part * dy
                line.append(QgsPointXY(x, y))
                break
            if sum > vst:
                line.append(p)
                # print(sum)
            p_old = p
        feat.setGeometry(QgsGeometry.fromPolylineXY(line))
        return feat

    def cut_point(self, section, xls_feature, side_dist_field = -1):
        feat = QgsFeature(xls_feature)
        points, bst, vst = self.calc_factor(section, xls_feature)

        p_old = QgsPointXY(points[0])

        sum_len = 0
        count_points = len(points)
        for i in range(count_points):
            p = QgsPointXY(points[i])
            dist = self.distanz.measureLine(p_old, p)
            sum_len += dist
            # print(str(sum_len) + " > " + str(vst))
            if sum_len > vst or i == count_points - 1:
                part = (sum_len - vst) / dist

                dx = p.x() - p_old.x()
                dy = p.y() - p_old.y()

                side_factor = 0
                if side_dist_field >= 0:
                    side_factor = xls_feature[side_dist_field] / dist

                x = p.x() - part * dx + dy * side_factor
                y = p.y() - part * dy - dx * side_factor

                feat.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(x, y)))
                return feat
            p_old = p

    def select_section(self, vnk, nnk):
        """Returns section out of Strassennetz.tab"""
        exp = QgsExpression('\"Anfangsnetzknoten\" = \'' + vnk + '\' AND \"Endnetzknoten\" = \'' + nnk + '\'')
        request = QgsFeatureRequest(exp)
        for f in self.netz.getFeatures(request):
            return f
        return None

    def generate_layer(self):
        """Generates temporary layers with geometry"""
        self.netz = QgsVectorLayer(self.dlg.mQgsFileWidget_2.filePath(), "netz", "ogr")
        varmix = QgsVectorLayer(self.dlg.mQgsFileWidget.filePath(), "varMix", "ogr")
        varmix.setProviderEncoding(u'UTF-8')
        varmix.dataProvider().setEncoding(u'UTF-8')
        # varmix = QgsVectorLayer("D:\kreis.xls", "varMix", "ogr")

        # d.setEllipsoidalMode(True)
        layer_without = QgsVectorLayer("None", os.path.basename(self.dlg.mQgsFileWidget.filePath()) + " (Ohne Geometrie)", "memory")
        layer_without.startEditing()
        without_data = layer_without.dataProvider()

        without_data.addAttributes(varmix.fields())  #
        layer_without.commitChanges()
        QgsProject.instance().addMapLayer(layer_without)

        layer_points = QgsVectorLayer("point?crs=" + self.netz.crs().authid(), os.path.basename(self.dlg.mQgsFileWidget.filePath()) + " (Punkte)", "memory")
        layer_points.startEditing()
        point_data = layer_points.dataProvider()

        point_data.addAttributes(varmix.fields())  #
        layer_points.commitChanges()
        QgsProject.instance().addMapLayer(layer_points)

        layer_lines = QgsVectorLayer("linestring?crs=" + self.netz.crs().authid(), os.path.basename(self.dlg.mQgsFileWidget.filePath()) + " (Linien)", "memory")
        layer_lines.startEditing()
        lines_data = layer_lines.dataProvider()

        lines_data.addAttributes(varmix.fields())  #
        layer_lines.commitChanges()
        QgsProject.instance().addMapLayer(layer_lines)

        results = varmix.getFeatures()
        vnk = ""
        nnk = ""
        feat = None

        side_dist_field = None;

        for r in results:
            if not (vnk == r['VNK'] and nnk == r['NNK']):
                vnk = r['VNK']
                nnk = r['NNK']
                feat = self.select_section(vnk, nnk)
            if feat is None:
                without_data.addFeatures([r])
            elif r['VST'] == r['BST']:
                if side_dist_field is None:
                    self.pld.show()
                    self.pld.comboBox.clear()
                    self.pld.comboBox.addItem("-- kein seitlicher Abstand --")
                    for f in varmix.fields():
                        self.pld.comboBox.addItem(f.name())
                    self.pld.comboBox.setCurrentIndex(0)
                    result = self.pld.exec_()
                    if result:
                        side_dist_field = self.pld.comboBox.currentIndex()-1
                f = self.cut_point(feat, r, side_dist_field)
                if f is not None:
                    point_data.addFeatures([f])
                else:
                    without_data.addFeatures([r])
            else:
                f = self.cut_line(feat, r)
                if f is not None:
                    lines_data.addFeatures([f])
                else:
                    without_data.addFeatures([r])

        if lines_data.featureCount() == 0:
            QgsProject.instance().removeMapLayer(layer_lines)
        if point_data.featureCount() == 0:
            QgsProject.instance().removeMapLayer(layer_points)
        if without_data.featureCount() == 0:
            QgsProject.instance().removeMapLayer(layer_without)

    def run(self):
        """Run method that performs all the real work"""
        # show the dialog
        self.dlg.show()
        # Run the dialog event loop
        result = self.dlg.exec_()
        # See if OK was pressed
        if result:
            # Do something useful here - delete the line containing pass and
            # substitute with your code.
            self.generate_layer()
            pass


