# -*- coding: utf-8 -*-
#
#     ||          ____  _ __
#  +------+      / __ )(_) /_______________ _____  ___
#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
#
#  Copyright (C) 2018 Bitcraze AB
#
#  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.
#
#  This program 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, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA  02110-1301, USA.

"""
Dialog box used to configure anchor positions. Used from the LPS tab.
"""
import logging

import cfclient
from PyQt5 import QtWidgets
from PyQt5 import uic
from PyQt5.QtCore import QAbstractTableModel, QVariant, Qt
from PyQt5.QtGui import QBrush, QColor
from PyQt5.QtWidgets import QInputDialog, QFileDialog
import yaml
import os

__author__ = 'Bitcraze AB'
__all__ = ['AnchorPositionDialog']

logger = logging.getLogger(__name__)

(anchor_postiong_widget_class, connect_widget_base_class) = (
    uic.loadUiType(
        cfclient.module_path + '/ui/dialogs/anchor_position_dialog.ui')
)


class AnchorPositionConfigTableModel(QAbstractTableModel):
    def __init__(self, headers, parent=None, *args):
        QAbstractTableModel.__init__(self, parent)
        self._anchor_positions = []
        self._headers = headers
        self._latest_known_anchor_positions = {}

        self._green_brush = QBrush(QColor(200, 255, 200))
        self._red_brush = QBrush(QColor(255, 200, 200))

    def rowCount(self, parent=None, *args, **kwargs):
        return len(self._anchor_positions)

    def columnCount(self, parent=None, *args, **kwargs):
        return len(self._headers)

    def data(self, index, role=None):
        value = self._anchor_positions[index.row()][index.column()]
        if index.isValid():
            if index.column() == 0:
                if role == Qt.CheckStateRole:
                    return QVariant(value)
            elif index.column() == 1:
                if role == Qt.DisplayRole:
                    return QVariant(value)
            else:
                if role == Qt.DisplayRole:
                    return QVariant('%.2f' % (value))
                elif role == Qt.EditRole:
                    return QVariant(value)
                elif role == Qt.BackgroundRole:
                    return self._get_background(index.row(), index.column())

        return QVariant()

    def setData(self, index, value, role=Qt.EditRole):
        if not index.isValid():
            return False

        self._anchor_positions[index.row()][index.column()] = value
        return True

    def headerData(self, col, orientation, role=None):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return QVariant(self._headers[col])
        return QVariant()

    def flags(self, index):
        if not index.isValid():
            return None

        if index.column() == 0:
            return Qt.ItemIsEnabled | Qt.ItemIsUserCheckable
        elif index.column() == 1:
            return Qt.ItemIsEnabled
        else:
            return Qt.ItemIsEnabled | Qt.ItemIsEditable

    def add_anchor(self, anchor_id, x=0.0, y=0.0, z=0.0):
        if not self._id_exist(anchor_id):
            self.layoutAboutToBeChanged.emit()
            self._anchor_positions.append([0, anchor_id, x, y, z])
            self._anchor_positions.sort(key=lambda row: row[1])
            self.layoutChanged.emit()

    def replace_anchors_from_latest_known_positions(self):
        self.replace_anchor_positions(self._latest_known_anchor_positions)

    def replace_anchor_positions(self, anchor_positions):
        self.layoutAboutToBeChanged.emit()
        self._anchor_positions = []
        for id, position in anchor_positions.items():
            self.add_anchor(id, x=position[0], y=position[1], z=position[2])
        self.layoutChanged.emit()

    def get_anchor_postions(self):
        result = {}
        for row in self._anchor_positions:
            result[row[1]] = (row[2], row[3], row[4])
        return result

    def anchor_postions_updated(self, anchor_positions):
        self.layoutAboutToBeChanged.emit()
        self._latest_known_anchor_positions = anchor_positions
        self.layoutChanged.emit()

    def remove_selected_anchors(self):
        self.layoutAboutToBeChanged.emit()
        self._anchor_positions = list(filter(
            lambda row: row[0] == 0, self._anchor_positions))
        self.layoutChanged.emit()

    def _id_exist(self, anchor_id):
        for anchor in self._anchor_positions:
            if anchor[1] == anchor_id:
                return True
        return False

    def _get_background(self, row, col):
        id = self._anchor_positions[row][1]
        if id in self._latest_known_anchor_positions:
            current_value = self._anchor_positions[row][col]
            latest_value = self._latest_known_anchor_positions[id][col - 2]

            if abs(current_value - latest_value) < 0.005:
                return self._green_brush
            else:
                return self._red_brush

        return QVariant()


class AnchorPositionDialog(QtWidgets.QWidget, anchor_postiong_widget_class):

    def __init__(self, lps_tab, *args):
        super(AnchorPositionDialog, self).__init__(*args)
        self.setupUi(self)

        self._current_folder = os.path.expanduser('~')

        self._lps_tab = lps_tab

        self._headers = ['', 'id', 'x', 'y', 'z']
        self._data_model = AnchorPositionConfigTableModel(self._headers, self)
        self._table_view.setModel(self._data_model)

        self._table_view.verticalHeader().setVisible(False)

        header = self._table_view.horizontalHeader()
        header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)
        header.setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch)
        header.setSectionResizeMode(3, QtWidgets.QHeaderView.Stretch)
        header.setSectionResizeMode(4, QtWidgets.QHeaderView.Stretch)

        self._add_anchor_button.clicked.connect(
            self._add_anchor_button_clicked)
        self._remove_anchors_button.clicked.connect(
            self._data_model.remove_selected_anchors)
        self._get_from_anchors_button.clicked.connect(
            self._get_from_anchors_button_clicked)
        self._write_to_anchors_button.clicked.connect(
            self._write_to_anchors_button_clicked)
        self._close_button.clicked.connect(self.close)
        self._load_button.clicked.connect(
            self._load_button_clicked)
        self._save_button.clicked.connect(
            self._save_button_clicked)

    def _add_anchor_button_clicked(self):
        anchor_id, ok = QInputDialog.getInt(
            self, "New anchor", "Enter id", min=0, max=255)
        if ok:
            self._data_model.add_anchor(anchor_id)

    def _get_from_anchors_button_clicked(self):
        self._data_model.replace_anchors_from_latest_known_positions()

    def _write_to_anchors_button_clicked(self):
        anchor_positions = self._data_model.get_anchor_postions()
        self._lps_tab.write_positions_to_anchors(anchor_positions)

    def anchor_postions_updated(self, anchor_positions):
        self._data_model.anchor_postions_updated(anchor_positions)

    def _load_button_clicked(self):
        names = QFileDialog.getOpenFileName(self, 'Open file',
                                            self._current_folder,
                                            "*.yaml;;*.*")

        if names[0] == '':
            return

        self._current_folder = os.path.dirname(names[0])

        f = open(names[0], 'r')
        with f:
            data = yaml.load(f)

            anchor_positions = {}
            for id, pos in data.items():
                anchor_positions[id] = (pos['x'], pos['y'], pos['z'])
            self._data_model.replace_anchor_positions(anchor_positions)

    def _save_button_clicked(self):
        anchor_positions = self._data_model.get_anchor_postions()
        data = {}
        for id, pos in anchor_positions.items():
            data[id] = {'x': pos[0], 'y': pos[1], 'z': pos[2]}

        names = QFileDialog.getSaveFileName(self, 'Save file',
                                            self._current_folder,
                                            "*.yaml;;*.*")

        if names[0] == '':
            return

        self._current_folder = os.path.dirname(names[0])

        if not names[0].endswith(".yaml") and names[0].find(".") < 0:
            filename = names[0] + ".yaml"
        else:
            filename = names[0]

        f = open(filename, 'w')
        with f:
            yaml.dump(data, f)