# -*- coding: utf-8 -*-
"""
PKMeter Configuration
"""
import json, keyring, os, shlex
from pkm import APPNAME, CONFIGPATH, SHAREDIR
from pkm import log, utils
from pkm.pkwidgets import PKWidget
from PyQt5 import QtGui, QtWidgets
from PyQt5.QtCore import Qt
from xml.etree import ElementTree

NAMESPACE_ROLE = 99


class PKConfig(PKWidget):
    TEMPLATE = os.path.join(SHAREDIR, 'templates', 'config.html')

    def __init__(self, pkmeter, parent=None):
        with open(self.TEMPLATE) as tmpl:
            template = ElementTree.fromstring(tmpl.read())
        PKWidget.__init__(self, template, self, parent)
        self.pkmeter = pkmeter                          # Save reference to pkmeter
        self._init_window()                             # Init ui window elements
        self.values = self.load()                       # Active configuration values
        self.listitems = []                             # List of items in the sidebar
        self.datatable = self._init_datatable()         # Init reusable data table
        self.pconfigs = self._init_pconfigs()           # Reference to each plugin config
        self.setWindowFlags(Qt.Dialog)
        self.setWindowModality(Qt.ApplicationModal)

    def _init_window(self):
        # Load core stylesheet
        stylepath = os.path.join(SHAREDIR, 'pkmeter.css')
        with open(stylepath) as handle:
            self.setStyleSheet(handle.read())
        # Init self properties
        self.setWindowTitle('PKMeter Preferences')
        self.setWindowModality(Qt.ApplicationModal)
        self.setWindowIcon(QtGui.QIcon(QtGui.QPixmap('img:logo.png')))
        self.layout().setContentsMargins(10,10,10,10)
        # Init window elements
        self.manifest.tabbar.setExpanding(False)
        self.manifest.tabbar.addTab('Settings')
        self.manifest.tabbar.addTab('Data')
        self.manifest.contents.setSizePolicy(QtWidgets.QSizePolicy(
            QtWidgets.QSizePolicy.Expanding,
            QtWidgets.QSizePolicy.Expanding))
        self.manifest.tabbar.currentChanged.connect(self.load_tab)
        # Init the ListWidget
        listwidget = self.manifest.list
        for module in sorted(self.pkmeter.modules.values(), key=self._sortKey):
            if getattr(module, 'Plugin', None) or getattr(module, 'Config', None):
                item = QtWidgets.QListWidgetItem(utils.name(module), parent=listwidget, type=0)
                item.setData(NAMESPACE_ROLE, utils.namespace(module))
                listwidget.addItem(item)
        self.manifest.list.itemSelectionChanged.connect(self.load_tab)

    def _sortKey(self, module):
        name = utils.name(module).lower()
        return '__pkmeter' if name == 'pkmeter' else name

    def _init_datatable(self):
        template = ElementTree.fromstring("""
          <vframe>
            <hframe>
              <QLineEdit id='filter' name='input_xlarge' placeholder='Filter'/>
              <stretch/><pushbutton text='Refresh' click='refresh_datatable'/>
            </hframe>
            <vframe id='table'/>
          </vframe>""")
        # Build the datatable
        self.datatable_wrap = PKWidget(template, self)
        datatable = QtWidgets.QTableWidget(0, 3, parent=None)
        datatable.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
        datatable.setHorizontalHeaderLabels(['Variable', 'Value', 'Type   '])
        datatable.horizontalHeader().setSectionResizeMode(0, QtWidgets.QHeaderView.Interactive)
        datatable.horizontalHeader().setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch)
        datatable.horizontalHeader().setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
        datatable.horizontalHeader().resizeSection(0, 200)
        datatable.verticalHeader().setVisible(False)
        datatable.verticalHeader().setDefaultSectionSize(19)
        datatable.setParent(self.datatable_wrap)
        self.datatable_wrap.manifest.table.layout().addWidget(datatable)
        # Connect the filter input
        self.datatable_wrap.manifest.filter.textChanged.connect(self.filter_datatable)
        return datatable

    def _init_pconfigs(self):
        pconfigs = {}
        for module in filter(lambda m: getattr(m, 'Config', None), self.pkmeter.modules.values()):
            pconfig = module.Config(self.pkmeter, self)
            pconfigs[pconfig.namespace] = pconfig
        return pconfigs

    def btn_reset(self):
        for pconfig in self.pconfigs.values():
            for field in pconfig.fields.values():
                self._reset_field(pconfig, field)

    def _reset_field(self, pconfig, field, index=None):
        from_keyring = field.get('save_to_keyring', False)
        field.value = self.get(pconfig.namespace, field.name, field.get('default'), from_keyring)
        field.lastchecked = field.value
        if field.input: field.input.set_value(field.value)
        if field.status: field.status.setText('')
        if field.help: field.help.setText(field.help_default)

    def btn_apply(self):
        for pconfig in self.pconfigs.values():
            for field in pconfig.fields.values():
                self.set(pconfig.namespace, field.name, field.value)
        self.save()
        self.pkmeter.reload()

    def btn_save(self):
        for pconfig in self.pconfigs.values():
            for field in pconfig.fields.values():
                to_keyring = field.get('save_to_keyring', False)
                self.set(pconfig.namespace, field.name, field.value, to_keyring)
        self.save()
        self.hide()
        self.pkmeter.reload()

    def get(self, namespace, path, default=None, from_keyring=False):
        path = '%s.%s' % (namespace, path)
        if from_keyring:
            value = keyring.get_password(APPNAME, path)
        else:
            value = utils.rget(self.values, path)
        return value if value is not None else default

    def load(self):
        self.values = getattr(self, 'values', {})
        log.info('Loading config: %s' % CONFIGPATH)
        if os.path.isfile(CONFIGPATH):
            with open(CONFIGPATH, 'r') as handle:
                self.values.update(json.load(handle))
        return self.values

    def save(self):
        log.info('Saving config: %s' % CONFIGPATH)
        os.makedirs(os.path.dirname(CONFIGPATH), exist_ok=True)
        self.set('pkmeter', 'positions', [w.position() for w in self.pkmeter.widgets])
        with open(CONFIGPATH, 'w') as handle:
            json.dump(self.values, handle, indent=2, sort_keys=True)

    def set(self, namespace, path, value, to_keyring=False):
        path = '%s.%s' % (namespace, path)
        if to_keyring:
            keyring.set_password(APPNAME, path, value)
        else:
            utils.rset(self.values, path, value)

    def show(self):
        self.btn_reset()
        self.resize(*self.initsize)
        self.manifest.list.setCurrentRow(0)
        self.load_tab()
        PKWidget.show(self)

    def load_tab(self, index=None, refresh=False):
        self.manifest.tabbar.setTabText(0, '%s Settings' % self.manifest.list.currentItem().text())
        tab = self.manifest.tabbar.currentIndex()
        utils.remove_children(self.manifest.contents)
        self.load_tab_settings(refresh) if tab == 0 else self.load_tab_data(refresh)

    def load_tab_settings(self, refresh=False):
        namespace = self.manifest.list.currentItem().data(NAMESPACE_ROLE)
        pconfig = self.pconfigs.get(namespace)
        if not pconfig:
            return self.load_message('No configuration for this module.')
        for field in pconfig.fields.values():
            pconfig._validate(field, force=True)
        pconfig.setParent(self.manifest.contents)
        self.manifest.contents.layout().addWidget(pconfig)

    def load_tab_data(self, refresh=False):
        namespace = self.manifest.list.currentItem().data(NAMESPACE_ROLE)
        data = self.pkmeter.data.get(namespace, {})
        data = utils.flatten_datatree(data, namespace)
        if not data:
            return self.load_message('Data not available for this module.')
        self.datatable.setRowCount(len(data))
        for row in range(len(data)):
            for col in range(3):
                item = QtWidgets.QTableWidgetItem(data[row][col], 0)
                self.datatable.setItem(row, col, item)
        if not refresh:
            self.datatable_wrap.manifest.filter.setText('')
        else:
            self.filter_datatable()
        self.datatable_wrap.setParent(self.manifest.contents)
        self.manifest.contents.layout().addWidget(self.datatable_wrap)

    def filter_datatable(self, _=None):
        try:
            text = self.datatable_wrap.manifest.filter.text()
            searches = shlex.split(text.lower().strip())
        except:
            searches = text.lower().strip()
        for row in range(self.datatable.rowCount()):
            teststrs = []
            for col in range(self.datatable.columnCount()):
                teststrs.append(self.datatable.item(row, col).text().lower())
            teststr = '||'.join(teststrs)
            showit = True
            for search in searches:
                if ((search.startswith('-') and search[1:] in teststr) or
                   (not search.startswith('-') and search not in teststr)):
                    showit = False
            self.datatable.setRowHidden(row, not showit)

    def refresh_datatable(self, event):
        self.load_tab(1, refresh=True)

    def load_message(self, message):
        layout = self.manifest.contents.layout()
        layout.addWidget(QtWidgets.QLabel(message, parent=self.manifest.contents))
        layout.addStretch(1)