#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#   Copyright (C) 2018 Oslandia <infos@oslandia.com>
#
#   This file is a piece of free software; you can redistribute it and/or
#   modify it under the terms of the GNU Library General Public
#   License as published by the Free Software Foundation; either
#   version 2 of the License, or (at your option) any later version.
#
#   This library 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
#   Library General Public License for more details.
#   You should have received a copy of the GNU Library General Public
#   License along with this library; if not, see <http://www.gnu.org/licenses/>.
#

from qgis.PyQt.QtCore import Qt, QRectF
from qgis.PyQt.QtGui import QFont, QFontMetrics
from PyQt5.QtGui import QPolygonF

from qgis.core import (
    QgsGeometry,
    QgsFields,
    QgsFeature,
    QgsRectangle,
    QgsFeatureRenderer,
    QgsReadWriteContext,
    QgsProject,
    QgsPointXY,
    QgsFeatureRequest,
)

import numpy as np

from .common import LogItem, POINT_RENDERER, LINE_RENDERER, POLYGON_RENDERER, qgis_render_context
from .units import unitConversionFactor


class SymbolLegend(LogItem):
    # margin all around the whole legend item
    LEGEND_ITEM_MARGIN = 5
    # margin between title and legend line
    LEGEND_LINE_MARGIN = 4

    def __init__(
        self, width, plots_name, plots_renderer, plots_renderer_type, is_vertical=False, parent=None
    ):
        """ Legend Item (header)

        :param width: width
        :type width: int
        :param title: legend title
        :type title: str
        :param station: station name
        :type station: str
        :param min_value: min value
        :type min_value: float
        :param max_value: max value
        :type max_value: float
        :param unit_of_measure: unit
        :type unit_of_measure: str
        :param is_vertical: orientation
        :type is_vertical: bool
        :param parent: parent widget
        :type parent: QWidget
        """
        LogItem.__init__(self, parent)
        self.__width = width
        self.__plots_name = plots_name
        self.__plots_renderer = plots_renderer
        self.__plots_renderer_type = plots_renderer_type
        self.__is_vertical = is_vertical

        self.__selected = False

        # name font
        self.__font1 = QFont()
        self.__font1.setPointSize(9)

        fm1 = QFontMetrics(self.__font1)
        self.__height = 3 + (fm1.height() + self.LEGEND_LINE_MARGIN) * len(plots_name)

    def boundingRect(self):
        """ Return bounding rectangle

        :return: bounding rectangle
        :rtype: QRectF
        """
        if self.__is_vertical:
            return QRectF(0, 0, self.__height, self.__width)
        else:
            return QRectF(0, 0, self.__width, self.__height)

    def is_vertical(self):
        return self.__is_vertical

    def selected(self):
        """ Return if the column is selected

        :return: selected
        :rtype: bool
        """
        return self.__selected

    def paint(self, painter, option, widget):
        """ Paint legend item, heritated from QGraphicsItem

        :param painter: QPainter
        :type painter: QPainter
        :param option: QStyleOptionGraphicsItem
        :type option: QStyleOptionGraphicsItem
        :param widget: QWidget
        :type widget: QWidget
        """
        self.draw_background(painter, outline=True)

        painter.save()
        if self.__is_vertical:
            painter.translate(self.__height / 2, self.__width / 2)
            painter.rotate(-90.0)
            painter.translate(-self.__width / 2, -self.__height / 2)

        painter.setFont(self.__font1)
        fm = painter.fontMetrics()
        # add "..." if needed
        y = self.LEGEND_ITEM_MARGIN * 3
        for i, name in enumerate(self.__plots_name):
            title = fm.elidedText(name + " : ", Qt.ElideRight, self.__width - 20)
            # w1 = (self.__width - fm.width(title)) / 2
            painter.drawText(self.LEGEND_ITEM_MARGIN, y, title)
            renderer = self.__plots_renderer[i]
            render_type = self.__plots_renderer_type[i]

            if render_type == LINE_RENDERER:
                # WKB structure of a linestring
                #
                #   01 : endianness
                #   02 00 00 00 : WKB type (linestring)
                #   nn nn nn nn : number of points (int32)
                # Then, for each point:
                #   xx xx xx xx xx xx xx xx : X coordinate (float64)
                #   yy yy yy yy yy yy yy yy : Y coordinate (float64)

                n_points = 2
                xx = [
                    self.LEGEND_ITEM_MARGIN * 3 + fm.width(title),
                    self.LEGEND_ITEM_MARGIN * 5 + fm.width(title),
                ]
                yy = [self.__height - (y - 3), self.__height - (y - 3)]

                wkb = np.zeros(8 * 2 * n_points + 9, dtype="uint8")
                wkb[0] = 1  # wkb endianness
                wkb[1] = 2  # linestring
                size_view = np.ndarray(buffer=wkb, dtype="int32", offset=5, shape=(1,))
                size_view[0] = n_points
                coords_view = np.ndarray(buffer=wkb, dtype="float64", offset=9, shape=(n_points, 2))
                coords_view[:, 0] = xx[:]
                coords_view[:, 1] = yy[:]
                geom = QgsGeometry()
                geom.fromWkb(wkb.tobytes())
            elif render_type == POINT_RENDERER:
                # WKB structure of a multipoint
                #
                #   01 : endianness
                #   04 00 00 00 : WKB type (multipoint)
                #   nn nn nn nn : number of points (int32)
                # Then, for each point:
                #   01 : endianness
                #   01 00 00 00 : WKB type (point)
                #   xx xx xx xx xx xx xx xx : X coordinate (float64)
                #   yy yy yy yy yy yy yy yy : Y coordinate (float64)

                n_points = 1
                xx = [self.LEGEND_ITEM_MARGIN * 3 + fm.width(title)]
                yy = [self.__height - (y - 3)]

                wkb = np.zeros((8 * 2 + 5) * n_points + 9, dtype="uint8")
                wkb[0] = 1  # wkb endianness
                wkb[1] = 4  # multipoint
                size_view = np.ndarray(buffer=wkb, dtype="int32", offset=5, shape=(1,))
                size_view[0] = n_points
                coords_view = np.ndarray(
                    buffer=wkb,
                    dtype="float64",
                    offset=9 + 5,
                    shape=(n_points, 2),
                    strides=(16 + 5, 8),
                )
                coords_view[:, 0] = xx[:]
                coords_view[:, 1] = yy[:]
                # header of each point
                h_view = np.ndarray(
                    buffer=wkb, dtype="uint8", offset=9, shape=(n_points, 2), strides=(16 + 5, 1)
                )
                h_view[:, 0] = 1  # endianness
                h_view[:, 1] = 1  # point
                geom = QgsGeometry()
                geom.fromWkb(wkb.tobytes())
            elif render_type == POLYGON_RENDERER:
                # WKB structure of a polygon
                #
                #   01 : endianness
                #   03 00 00 00 : WKB type (polygon)
                #   01 00 00 00 : Number of rings (always 1 here)
                #   nn nn nn nn : number of points (int32)
                # Then, for each point:
                #   xx xx xx xx xx xx xx xx : X coordinate (float64)
                #   yy yy yy yy yy yy yy yy : Y coordinate (float64)
                #
                # We add two additional points to close the polygon

                n_points = 4
                geom = QgsGeometry.fromQPolygonF(
                    QPolygonF(
                        QRectF(
                            self.LEGEND_ITEM_MARGIN * 3 + fm.width(title),
                            self.__height - (y - 2),
                            10,
                            5,
                        )
                    )
                )

            painter.setClipRect(0, 0, self.__width, self.__height)

            fields = QgsFields()
            # fields.append(QgsField("", QVariant.String))
            feature = QgsFeature(fields, 1)
            feature.setGeometry(geom)

            context = qgis_render_context(painter, self.__width, self.__height)
            context.setExtent(QgsRectangle(0, 1, self.__width, self.__height))

            renderer.startRender(context, fields)
            renderer.renderFeature(feature, context)
            renderer.stopRender(context)

            y += fm.height() + self.LEGEND_LINE_MARGIN

        painter.restore()


def format_number(f, num_decimals=1):
    """ Format number in scientific notation or not

    :param f: number
    :type f: float
    :param num_decimals: displayed decimals limit
    :type num_decimals: int
    """
    return ("{:." + str(num_decimals) + "g}").format(f)
