#!/usr/bin/env python
# --!-- coding: utf8 --!--

import locale
import importlib
import os

from PyQt5.QtCore import QSettings, QRegExp, Qt, QDir
from PyQt5.QtGui import QIcon, QBrush, QColor, QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import QWidget, QAction, QFileDialog, QSpinBox, QLineEdit, QLabel, QPushButton, QTreeWidgetItem, \
    qApp, QMessageBox

from manuskript import loadSave
from manuskript import settings
from manuskript.enums import Outline
from manuskript.functions import mainWindow, iconFromColor, appPath
from manuskript.models.characterModel import characterModel
from manuskript.models import outlineItem, outlineModel
from manuskript.models.plotModel import plotModel
from manuskript.models.worldModel import worldModel
from manuskript.ui.welcome_ui import Ui_welcome
from manuskript.ui import style as S

import logging
LOGGER = logging.getLogger(__name__)

try:
    locale.setlocale(locale.LC_ALL, '')
except:
    pass

class welcome(QWidget, Ui_welcome):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.setupUi(self)

        self.template = []

        self.mw = mainWindow()
        self.btnOpen.clicked.connect(self.openFile)
        self.btnCreate.clicked.connect(self.createFile)
        self.chkLoadLastProject.toggled.connect(self.setAutoLoad)
        self.tree.itemClicked.connect(self.changeTemplate)
        self.btnAddLevel.clicked.connect(self.templateAddLevel)
        self.btnAddWC.clicked.connect(self.templateAddWordCount)
        self.btnCreateText = self.btnCreate.text()

        self.populateTemplates()
        self._templates = self.templates()

    def updateValues(self):
        # Auto load
        autoLoad, last = self.getAutoLoadValues()
        self.chkLoadLastProject.setChecked(autoLoad)

        # Recent Files
        self.loadRecents()

    def getLastAccessedDirectory(self):
        sttgs = QSettings()
        lastDirectory = sttgs.value("lastAccessedDirectory", defaultValue=".", type=str)
        if lastDirectory != '.':
            LOGGER.info("Last accessed directory \"{}\" loaded.".format(lastDirectory))
        return lastDirectory

    def setLastAccessedDirectory(self, dir):
        sttgs = QSettings()
        sttgs.setValue("lastAccessedDirectory", dir)

    ###############################################################################
    # AUTOLOAD
    ###############################################################################

    def showEvent(self, event):
        """Waiting for things to be fully loaded to start opening projects."""
        QWidget.showEvent(self, event)

        # Auto load last project
        autoLoad, last = self.getAutoLoadValues()

        if self.mw._autoLoadProject:
            project = self.mw._autoLoadProject
            self.mw._autoLoadProject = None
            self.appendToRecentFiles(project)
            self.mw.loadProject(project)

        elif autoLoad and last:
            self.mw.loadProject(last)

    def getAutoLoadValues(self):
        """
        Reads manuskript system's settings and returns a tuple:
        - `bool`: whether manuskript should automatically load
                  the last opened project or display the
                  welcome widget.
        - `str`:  the absolute path to the last opened project.
        """
        sttgns = QSettings()
        autoLoad = sttgns.value("autoLoad", defaultValue=False, type=bool)
        if autoLoad and sttgns.contains("lastProject"):
            last = sttgns.value("lastProject")
        else:
            last = ""

        return autoLoad, last

    def setAutoLoad(self, v):
        if type(v) == bool:
            QSettings().setValue("autoLoad", v)

    ###############################################################################
    # RECENTS
    ###############################################################################

    def loadRecents(self):
        sttgns = QSettings()
        self.mw.menuRecents.setIcon(QIcon.fromTheme("folder-recent"))
        if sttgns.contains("recentFiles"):
            lst = sttgns.value("recentFiles")
            self.mw.menuRecents.clear()
            for f in [f for f in lst if os.path.exists(f)]:
                name = os.path.split(f)[1]
                a = QAction(name, self)
                a.setData(f)
                a.setStatusTip(f)
                a.triggered.connect(self.loadRecentFile)
                self.mw.menuRecents.addAction(a)

            self.btnRecent.setMenu(self.mw.menuRecents)

    def appendToRecentFiles(self, project):
        sttgns = QSettings()
        if sttgns.contains("recentFiles"):
            recentFiles = sttgns.value("recentFiles")
        else:
            recentFiles = []

        while project in recentFiles:
            recentFiles.remove(project)
        recentFiles.insert(0, project)
        recentFiles = recentFiles[:10]
        sttgns.setValue("recentFiles", recentFiles)

    def loadRecentFile(self):
        act = self.sender()
        self.appendToRecentFiles(act.data())
        self.mw.closeProject()
        self.mw.loadProject(act.data())

    ###############################################################################
    # DIALOGS
    ###############################################################################

    def openFile(self):
        lastDirectory = self.getLastAccessedDirectory()

        """File dialog that request an existing file. For opening project."""
        filename = QFileDialog.getOpenFileName(self,
                                               self.tr("Open project"),
                                               lastDirectory,
                                               self.tr("Manuskript project (*.msk);;All files (*)"))[0]
        if filename:
            self.setLastAccessedDirectory(os.path.dirname(filename))
            self.appendToRecentFiles(filename)
            self.mw.loadProject(filename)

    def saveAsFile(self):
        lastDirectory = self.getLastAccessedDirectory()

        """File dialog that request a file, existing or not.
        Save data to that file, which then becomes the current project."""
        filename = QFileDialog.getSaveFileName(self,
                                               self.tr("Save project as..."),
                                               lastDirectory,
                                               self.tr("Manuskript project (*.msk)"))[0]

        if filename:
            self.setLastAccessedDirectory(os.path.dirname(filename))
            if filename[-4:] != ".msk":
                filename += ".msk"
            self.appendToRecentFiles(filename)
            loadSave.clearSaveCache()  # Ensure all file(s) are saved under new filename
            self.mw.saveDatas(filename)
            # Update Window's project name with new filename
            pName = os.path.split(filename)[1]
            if pName.endswith('.msk'):
                pName=pName[:-4]
            self.mw.setWindowTitle(pName + " - " + self.tr("Manuskript"))

    def createFile(self, filename=None, overwrite=False):
        lastDirectory = self.getLastAccessedDirectory()

        """When starting a new project, ask for a place to save it.
        Datas are not loaded from file, so they must be populated another way."""
        if not filename:
            filename = QFileDialog.getSaveFileName(
                           self,
                           self.tr("Create New Project"),
                           lastDirectory,
                           self.tr("Manuskript project (*.msk)"))[0]

        if filename:
            self.setLastAccessedDirectory(os.path.dirname(filename))
            if filename[-4:] != ".msk":
                filename += ".msk"
            if os.path.exists(filename) and not overwrite:
                # Check if okay to overwrite existing project
                result = QMessageBox.warning(self, self.tr("Warning"),
                    self.tr("Overwrite existing project {} ?").format(filename),
                    QMessageBox.Ok|QMessageBox.Cancel, QMessageBox.Cancel)
                if result == QMessageBox.Cancel:
                    return
            # Create new project
            self.appendToRecentFiles(filename)
            self.loadDefaultDatas()
            self.mw.loadProject(filename, loadFromFile=False)

    ###############################################################################
    # TEMPLATES
    ###############################################################################

    def templates(self):
        return [
            (self.tr("Empty fiction"), [], "Fiction"),
            (self.tr("Novel"), [
                (20, self.tr("Chapter")),
                (5, self.tr("Scene")),
                (500, None)  # A line with None is word count
            ], "Fiction"),
            (self.tr("Novella"), [
                (10, self.tr("Chapter")),
                (5, self.tr("Scene")),
                (500, None)
            ], "Fiction"),
            (self.tr("Short Story"), [
                (10, self.tr("Scene")),
                (1000, None)
            ], "Fiction"),
            (self.tr("Trilogy"), [
                (3, self.tr("Book")),
                (3, self.tr("Section")),
                (10, self.tr("Chapter")),
                (5, self.tr("Scene")),
                (500, None)
            ], "Fiction"),
            (self.tr("Empty non-fiction"), [], "Non-fiction"),
            (self.tr("Research paper"), [
                (3, self.tr("Section")),
                (1000, None)
            ], "Non-fiction")
        ]

    def changeTemplate(self, item, column):
        template = [i for i in self._templates if i[0] == item.text(0)]
        self.btnCreate.setText(self.btnCreateText)

        # Selected item is a template
        if len(template):
            self.template = template[0]
            self.updateTemplate()

        # Selected item is a sample project
        elif item.data(0, Qt.UserRole):
            name = item.data(0, Qt.UserRole)
            # Clear templates
            self.template = self._templates[0]
            self.updateTemplate()
            # Change button text
            self.btnCreate.setText("Open {}".format(name))
            # Load project
            self.mw.loadProject(appPath("sample-projects/{}".format(name)))

    def updateTemplate(self):
        # Clear layout
        def clearLayout(l):
            while l.count() != 0:
                i = l.takeAt(0)
                if i.widget():
                    i.widget().deleteLater()
                if i.layout():
                    clearLayout(i.layout())

        clearLayout(self.lytTemplate)

        # self.templateLayout.addStretch()
        # l = QGridLayout()
        # self.templateLayout.addLayout(l)

        k = 0
        hasWC = False
        for templateIndex, d in enumerate(self.template[1]):
            spin = QSpinBox(self)
            spin.setRange(0, 999999)
            spin.setValue(d[0])
            # Storing the level of the template in that spinbox, so we can use
            # it to update the template when valueChanged on that spinbox
            # (we do that in self.updateWordCount for convenience).
            spin.setProperty("templateIndex", templateIndex)
            spin.valueChanged.connect(self.updateWordCount)
            if d[1] != None:
                txt = QLineEdit(self)
                txt.setProperty("templateIndex", templateIndex)
                txt.textEdited.connect(self.updateWordCount)
                txt.setText(d[1])

            else:
                hasWC = True
                txt = QLabel(self.tr("words each."), self)

            if k != 0:
                of = QLabel(self.tr("of"), self)
                self.lytTemplate.addWidget(of, k, 0)

                btn = QPushButton("", self)
                btn.setIcon(QIcon.fromTheme("edit-delete"))
                btn.setProperty("deleteRow", k)
                btn.setFlat(True)
                btn.clicked.connect(self.deleteTemplateRow)

                self.lytTemplate.addWidget(btn, k, 3)

            self.lytTemplate.addWidget(spin, k, 1)
            self.lytTemplate.addWidget(txt, k, 2)
            k += 1

        self.btnAddWC.setEnabled(not hasWC and len(self.template[1]) > 0)
        self.btnAddLevel.setEnabled(True)
        self.lblTotal.setVisible(hasWC)
        self.updateWordCount()

    def templateAddLevel(self):
        if len(self.template[1]) > 0 and \
                        self.template[1][len(self.template[1]) - 1][1] == None:
            # has word count, so insert before
            self.template[1].insert(len(self.template[1]) - 1, (10, self.tr("Text")))
        else:
            # No word count, so insert at end
            self.template[1].append((10, self.tr("Something")))
        self.updateTemplate()

    def templateAddWordCount(self):
        self.template[1].append((500, None))
        self.updateTemplate()

    def deleteTemplateRow(self):
        btn = self.sender()
        row = btn.property("deleteRow")
        self.template[1].pop(row)
        self.updateTemplate()

    def updateWordCount(self):
        """
        Updates the word count of the template, and displays it in a label.

        Also, updates self.template, which is used to create the items when
        calling self.createFile.
        """
        total = 1

        # Searching for every spinboxes on the widget, and multiplying
        # their values to get the number of words.
        for s in self.findChildren(QSpinBox, QRegExp(".*"),
                                   Qt.FindChildrenRecursively):
            total = total * s.value()

            # Update self.template to reflect the changed count values
            templateIndex = s.property("templateIndex")
            self.template[1][templateIndex] = (
                s.value(),
                self.template[1][templateIndex][1])

        for t in self.findChildren(QLineEdit, QRegExp(".*"),
                                   Qt.FindChildrenRecursively):
            # Update self.template to reflect the changed name values
            templateIndex = t.property("templateIndex")
            if templateIndex != None :
                self.template[1][templateIndex] = (
                self.template[1][templateIndex][0],
                t.text())

        if total == 1:
            total = 0

        self.lblTotal.setText(self.tr("<b>Total:</b> {} words (~ {} pages)").format(
                locale.format_string("%d", total, grouping=True),
                locale.format_string("%d", total / 250, grouping=True)
        ))

    def addTopLevelItem(self, name):
        item = QTreeWidgetItem(self.tree, [name])
        item.setBackground(0, QBrush(QColor(S.highlightLight)))
        item.setForeground(0, QBrush(QColor(S.highlightedTextDark)))
        item.setTextAlignment(0, Qt.AlignCenter)
        item.setFlags(Qt.ItemIsEnabled)
        f = item.font(0)
        f.setBold(True)
        item.setFont(0, f)
        return item

    def populateTemplates(self):
        self.tree.clear()
        self.tree.setIndentation(0)

        # Add templates
        item = self.addTopLevelItem(self.tr("Fiction"))
        templates = [i for i in self.templates() if i[2] == "Fiction"]
        for t in templates:
            sub = QTreeWidgetItem(item, [t[0]])

        # Add templates: non-fiction
        item = self.addTopLevelItem(self.tr("Non-fiction"))
        templates = [i for i in self.templates() if i[2] == "Non-fiction"]
        for t in templates:
            sub = QTreeWidgetItem(item, [t[0]])


        # Add Demo project
        item = self.addTopLevelItem(self.tr("Demo projects"))
        dir = QDir(appPath("sample-projects"))
        for f in dir.entryList(["*.msk"], filters=QDir.Files):
            sub = QTreeWidgetItem(item, [f[:-4]])
            sub.setData(0, Qt.UserRole, f)

        self.tree.expandAll()

    def loadDefaultDatas(self):
        """Initialize a basic Manuskript project."""

        # Empty settings
        importlib.reload(settings)
        settings.initDefaultValues()
        self.mw.loadEmptyDatas()

        if self.template:
            t = [i for i in self._templates if i[0] == self.template[0]]
            if t and t[0][2] == "Non-fiction":
                settings.viewMode = "simple"

        # Tasks
        self.mw.mdlFlatData.setRowCount(2)     # data from: infos.txt, summary.txt
        self.mw.mdlFlatData.setColumnCount(8)  # version_1.py: len(infos.txt) == 8

        # Labels
        for color, text in [
            (Qt.transparent, ""),
            (Qt.yellow, self.tr("Idea")),
            (Qt.green, self.tr("Note")),
            (Qt.blue, self.tr("Chapter")),
            (Qt.red, self.tr("Scene")),
            (Qt.cyan, self.tr("Research"))
        ]:
            self.mw.mdlLabels.appendRow(QStandardItem(iconFromColor(color), text))

        # Status
        for text in [
            "",
            self.tr("TODO"),
            self.tr("First draft"),
            self.tr("Second draft"),
            self.tr("Final")
        ]:
            self.mw.mdlStatus.appendRow(QStandardItem(text))

        # Plot (nothing special needed)

        # Outline

        root = self.mw.mdlOutline.rootItem
        _type = "md"

        def addElement(parent, datas):
            if len(datas) == 2 and datas[1][1] == None or \
                            len(datas) == 1:
                # Next item is word count
                n = 0
                for i in range(datas[0][0]):
                    n += 1
                    item = outlineItem(title="{} {}".format(
                            datas[0][1],
                            str(n)),
                            _type=_type,
                            parent=parent)
                    if len(datas) == 2:
                        item.setData(Outline.setGoal, datas[1][0])
                        # parent.appendChild(item)
            else:
                n = 0
                for i in range(datas[0][0]):
                    n += 1
                    item = outlineItem(title="{} {}".format(
                            datas[0][1],
                            str(n)),
                            _type="folder",
                            parent=parent)
                    # parent.appendChild(item)
                    addElement(item, datas[1:])

        if self.template and self.template[1]:
            addElement(root, self.template[1])

        # World (nothing special needed)
