# -*- coding: utf-8 -*-

# Copyright (c) 2012 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing a personal information manager used to complete form
fields.
"""

import functools

from PyQt6.QtCore import Qt, QObject, QPoint
from PyQt6.QtWidgets import QDialog, QMenu

import Preferences
import UI.PixmapCache

from ..WebBrowserPage import WebBrowserPage


class PersonalInformationManager(QObject):
    """
    Class implementing the personal information manager used to complete form
    fields.
    """
    FullName = 0
    LastName = 1
    FirstName = 2
    Email = 3
    Mobile = 4
    Phone = 5
    Address = 6
    City = 7
    Zip = 8
    State = 9
    Country = 10
    HomePage = 11
    Special1 = 12
    Special2 = 13
    Special3 = 14
    Special4 = 15
    Max = 16
    Invalid = 256
    
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super().__init__(parent)
        
        self.__loaded = False
        self.__allInfo = {}
        self.__infoMatches = {}
        self.__translations = {}
        
        self.__view = None
        self.__clickedPos = QPoint()
    
    def __loadSettings(self):
        """
        Private method to load the settings.
        """
        self.__allInfo[self.FullName] = Preferences.getWebBrowser(
            "PimFullName")
        self.__allInfo[self.LastName] = Preferences.getWebBrowser(
            "PimLastName")
        self.__allInfo[self.FirstName] = Preferences.getWebBrowser(
            "PimFirstName")
        self.__allInfo[self.Email] = Preferences.getWebBrowser("PimEmail")
        self.__allInfo[self.Mobile] = Preferences.getWebBrowser("PimMobile")
        self.__allInfo[self.Phone] = Preferences.getWebBrowser("PimPhone")
        self.__allInfo[self.Address] = Preferences.getWebBrowser("PimAddress")
        self.__allInfo[self.City] = Preferences.getWebBrowser("PimCity")
        self.__allInfo[self.Zip] = Preferences.getWebBrowser("PimZip")
        self.__allInfo[self.State] = Preferences.getWebBrowser("PimState")
        self.__allInfo[self.Country] = Preferences.getWebBrowser("PimCountry")
        self.__allInfo[self.HomePage] = Preferences.getWebBrowser(
            "PimHomePage")
        self.__allInfo[self.Special1] = Preferences.getWebBrowser(
            "PimSpecial1")
        self.__allInfo[self.Special2] = Preferences.getWebBrowser(
            "PimSpecial2")
        self.__allInfo[self.Special3] = Preferences.getWebBrowser(
            "PimSpecial3")
        self.__allInfo[self.Special4] = Preferences.getWebBrowser(
            "PimSpecial4")
        
        self.__translations[self.FullName] = self.tr("Full Name")
        self.__translations[self.LastName] = self.tr("Last Name")
        self.__translations[self.FirstName] = self.tr("First Name")
        self.__translations[self.Email] = self.tr("E-mail")
        self.__translations[self.Mobile] = self.tr("Mobile")
        self.__translations[self.Phone] = self.tr("Phone")
        self.__translations[self.Address] = self.tr("Address")
        self.__translations[self.City] = self.tr("City")
        self.__translations[self.Zip] = self.tr("ZIP Code")
        self.__translations[self.State] = self.tr("State/Region")
        self.__translations[self.Country] = self.tr("Country")
        self.__translations[self.HomePage] = self.tr("Home Page")
        self.__translations[self.Special1] = self.tr("Custom 1")
        self.__translations[self.Special2] = self.tr("Custom 2")
        self.__translations[self.Special3] = self.tr("Custom 3")
        self.__translations[self.Special4] = self.tr("Custom 4")
        
        self.__infoMatches[self.FullName] = ["fullname", "realname"]
        self.__infoMatches[self.LastName] = ["lastname", "surname"]
        self.__infoMatches[self.FirstName] = ["firstname", "name"]
        self.__infoMatches[self.Email] = ["email", "e-mail", "mail"]
        self.__infoMatches[self.Mobile] = ["mobile", "mobilephone"]
        self.__infoMatches[self.Phone] = ["phone", "telephone"]
        self.__infoMatches[self.Address] = ["address"]
        self.__infoMatches[self.City] = ["city"]
        self.__infoMatches[self.Zip] = ["zip"]
        self.__infoMatches[self.State] = ["state", "region"]
        self.__infoMatches[self.Country] = ["country"]
        self.__infoMatches[self.HomePage] = ["homepage", "www"]
        
        self.__loaded = True
    
    def showConfigurationDialog(self):
        """
        Public method to show the configuration dialog.
        """
        from .PersonalDataDialog import PersonalDataDialog
        dlg = PersonalDataDialog()
        if dlg.exec() == QDialog.DialogCode.Accepted:
            dlg.storeData()
            self.__loadSettings()
    
    def createSubMenu(self, menu, view, hitTestResult):
        """
        Public method to create the personal information sub-menu.
        
        @param menu reference to the main menu (QMenu)
        @param view reference to the view (HelpBrowser)
        @param hitTestResult reference to the hit test result
            (WebHitTestResult)
        """
        self.__view = view
        self.__clickedPos = hitTestResult.pos()
        
        if not hitTestResult.isContentEditable():
            return
        
        if not self.__loaded:
            self.__loadSettings()
        
        submenu = QMenu(self.tr("Insert Personal Information"), menu)
        submenu.setIcon(UI.PixmapCache.getIcon("pim"))
        
        for key, info in sorted(self.__allInfo.items()):
            if info:
                act = submenu.addAction(self.__translations[key])
                act.setData(info)
                act.triggered.connect(
                    functools.partial(self.__insertData, act))
        
        submenu.addSeparator()
        submenu.addAction(self.tr("Edit Personal Information"),
                          self.showConfigurationDialog)
        
        menu.addMenu(submenu)
        menu.addSeparator()
    
    def __insertData(self, act):
        """
        Private slot to insert the selected personal information.
        
        @param act reference to the action that triggered
        @type QAction
        """
        if self.__view is None or self.__clickedPos.isNull():
            return
        
        info = act.data()
        info = info.replace('"', '\\"')
        
        source = """
            var e = document.elementFromPoint({0}, {1});
            if (e) {{
                var v = e.value.substring(0, e.selectionStart);
                v += "{2}" + e.value.substring(e.selectionEnd);
                e.value = v;
            }}""".format(self.__clickedPos.x(), self.__clickedPos.y(), info)
        self.__view.page().runJavaScript(source, WebBrowserPage.SafeJsWorld)
    
    def viewKeyPressEvent(self, view, evt):
        """
        Protected method to handle key press events we are interested in.
        
        @param view reference to the view (HelpBrowser)
        @param evt reference to the key event (QKeyEvent)
        @return flag indicating handling of the event (boolean)
        """
        if view is None:
            return False
        
        isEnter = evt.key() in [Qt.Key.Key_Return, Qt.Key.Key_Enter]
        isControlModifier = (
            evt.modifiers() & Qt.KeyboardModifier.ControlModifier
        )
        if not isEnter or not isControlModifier:
            return False
        
        if not self.__loaded:
            self.__loadSettings()
        
        source = """
            var inputs = document.getElementsByTagName('input');
            var table = {0};
            for (var i = 0; i < inputs.length; ++i) {{
                var input = inputs[i];
                if (input.type != 'text' || input.name == '')
                    continue;
                for (var key in table) {{
                    if (!table.hasOwnProperty(key))
                        continue;
                    if (key == input.name || input.name.indexOf(key) != -1) {{
                        input.value = table[key];
                        break;
                    }}
                }}
            }}""".format(self.__matchingJsTable())
        view.page().runJavaScript(source, WebBrowserPage.SafeJsWorld)
        
        return True
    
    def connectPage(self, page):
        """
        Public method to allow the personal information manager to connect to
        the page.
        
        @param page reference to the web page
        @type WebBrowserPage
        """
        page.loadFinished.connect(lambda ok: self.__pageLoadFinished(ok, page))
    
    def __pageLoadFinished(self, ok, page):
        """
        Private slot to handle the completion of a page load.
        
        @param ok flag indicating a successful load
        @type bool
        @param page reference to the web page object
        @type WebBrowserPage
        """
        if page is None or not ok:
            return
        
        if not self.__loaded:
            self.__loadSettings()
        
        source = """
            var inputs = document.getElementsByTagName('input');
            var table = {0};
            for (var i = 0; i < inputs.length; ++i) {{
                var input = inputs[i];
                if (input.type != 'text' || input.name == '')
                    continue;
                for (var key in table) {{
                    if (!table.hasOwnProperty(key) || table[key] == '')
                        continue;
                    if (key == input.name || input.name.indexOf(key) != -1) {{
                        input.style['-webkit-appearance'] = 'none';
                        input.style['-webkit-box-shadow'] =
                            'inset 0 0 2px 1px #000EEE';
                        break;
                    }}
                }}
            }}""".format(self.__matchingJsTable())
        page.runJavaScript(source, WebBrowserPage.SafeJsWorld)
    
    def __matchingJsTable(self):
        """
        Private method to create the common part of the JavaScript sources.
        
        @return JavaScript source
        @rtype str
        """
        values = []
        for key, names in self.__infoMatches.items():
            for name in names:
                value = self.__allInfo[key].replace('"', '\\"')
                values.append('"{0}":"{1}"'.format(name, value))
        return "{{ {0} }}".format(",".join(values))
