#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Jun  3 22:34:32 2020

@author: yves


    This file is part of Saqqarah.

    Saqqarah 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 3 of the License, or
    (at your option) any later version.

    Saqqarah 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 Saqqarah.  If not, see <https://www.gnu.org/licenses/>
"""

from . import Pyramid, PyramidPrinterImage, PyramidPrinterTikz

from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtCore import QTimeLine, QThread
from PySide2.QtGui import QRegExpValidator
from PySide2.QtWidgets import QFileDialog, QAction

from PIL.ImageQt import toqpixmap

from . import PreferencesUI
from . import Parameters

from PySide2.QtUiTools import QUiLoader

from . import version, codename

from . import ui
try:
    import importlib.resources as pkg_resources
except ImportError:
    # Try backported to PY<37 `importlib_resources`.
    import importlib_resources as pkg_resources
    
class LookingForPuzzleThread(QThread):
    def __init__(self, ui):
        QThread.__init__(self)
        self.param = ui.param
        self.pyramid = ui.pyramid

    def __del__(self):
        self.wait()

    # TODO : pyramid uses parameters() too
    def run(self):
        self.pyramid.get_puzzle(difficulty=self.param.difficulty,
                                min = self.param.value_min,   
                                max = self.param.value_max,
                                seed = self.param.seed,
                                exclude_zero = self.param.exclude_zero)

class SaqqarahUI:
    def __init__(self, window):
        # defaut values are inside
        self.param = Parameters()

        self.images = {}

        self.generating = False

        self.init_ui(window)
        # 1000 seconds
        self.timeline = QTimeLine(2_000, self.window)
        self.init_timeline()
        self.combo_init()
        self.seed_init()
        self.zero_init()
        self.update_pyramid()
        
    def zero_init(self):
        zeroCheck = self.window.zeroCheckbox
        self.exlude_zero = zeroCheck.isChecked()
        zeroCheck.stateChanged.connect(self.zero_changed)

    def zero_changed(self):
        self.param.exclude_zero = self.window.eroCheckbox.isChecked()

    def seed_init(self):
        seed = self.window.seedEdit
        seed.setValidator(QRegExpValidator("[1-9][0-9]*"))
        seed.editingFinished.connect(self.seed_changed)

    def seed_changed(self):
        value = self.window.seedEdit.text()
        self.param.seed = int(value)

    def combo_init(self):
        outCombo = self.window.outputTypeCombo
        outCombo.addItem('Images', userData=PyramidPrinterImage)
        outCombo.addItem('Tikz code', userData=PyramidPrinterTikz)

    def update_pyramid(self):        
        self.pyramid = Pyramid(self.param.size, ui=self)
        self.imagePrinter = PyramidPrinterImage(self.pyramid, self.param, memory_images=True)
        self.clean_images()
           
    def init_ui(self,window):
        self.window = window
        self.window.quitButton.clicked.connect(QtWidgets.QApplication.instance().quit)
        self.spinbox_init(window.sizeBox, 
                          value=self.param.size, 
                          min=self.param.size_min, 
                          max=self.param.size_max, 
                          on_change=self.size_changed )
        self.check_size_difficulty()

        self.spinbox_init(window.difficultyBox, 
                          value=self.param.difficulty, 
                          min=self.param.difficulty_min, 
                          max=self.param.difficulty_max,
                          on_change=self.difficulty_changed )
        self.spinbox_init(window.minBox, 
                          value=self.param.value_min, 
                          min=-1000, 
                          max=self.param.value_max-1,
                          on_change=self.min_changed )
        self.spinbox_init(window.maxBox, 
                          value=self.param.value_max, 
                          min=self.param.value_min+1, 
                          max=1000,
                          on_change=self.max_changed )
        self.window.puzzleButton.clicked.connect(self.generate_puzzle)
        self.window.directoryButton.clicked.connect(self.choose_directory)
        self.window.saveButton.clicked.connect(self.save_puzzle)
        self.progressbar_init()
        self.menus_init()
        
    def menus_init(self):
        entryExit = self.window.actionExit
        entryExit.triggered.connect(QtWidgets.QApplication.instance().quit)
        self.window.actionPreferences.triggered.connect(self.preferences_run)
        self.window.actionAbout.triggered.connect(self.about_run)
        
    def about_run(self):
        loader = QUiLoader()
        
        with pkg_resources.path(ui, 'about.ui') as ui_file:
            about_ui = loader.load(str(ui_file), self.window)
            
        about_ui.versionLabel.setText(f"Version {version()} ({codename()})")
        about_ui.show()
        
    def preferences_run(self):
        self.pref_ui = PreferencesUI(self).pref_ui
        self.pref_ui.accepted.connect(self.validate_pref)
  
    def init_timeline(self):
        # 0.1 sec frame
        self.timeline.setUpdateInterval(10)
        self.timeline.setFrameRange(0, 100)
        self.timeline.frameChanged[int].connect(self.update_progressbar)
        #2**12 * 10 seconds is 11 hours
        self.timeline.setLoopCount(2**15)
        self.timeline.setCurveShape(self.timeline.LinearCurve)

    def update_progressbar(self, frame):
        # animation to make sure ui is not locked
        X = frame*2 - 100
        self.window.progressBar.setInvertedAppearance(X >= 0)
        X = 100 - abs(X)
        self.window.progressBar.setValue(X)
     
    def progressbar_init(self):
        self.window.progressBar.setRange(0,100)
        self.window.progressBar.setInvertedAppearance(False)
        self.window.progressBar.reset()

    def progressbar_stop(self):
        self.timeline.stop()
        self.window.progressBar.setInvertedAppearance(False)
        self.window.progressBar.setRange(0,100)
        self.window.progressBar.reset()
        
    def progressbar_run(self):
        self.window.progressBar.setRange(0,100)
        self.window.progressBar.setFormat("Looking…")
        self.window.progressBar.reset()
        self.timeline.start()
        
    def spinbox_init(self, spin, *, value = 0, min=0, max=1000, step=1, on_change = None):
        spin.setRange(min,max)
        spin.setValue(value)
        if on_change:
            spin.valueChanged.connect(on_change)
        
    def size_changed(self, value):
        self.param.size = value
        self.update_pyramid()
        self.check_size_difficulty()

    def check_size_difficulty(self):
        if self.param.size < 5:
            self.window.difficultyBox.setMaximum(3)
        else:
            self.window.difficultyBox.setMaximum(5)
            
    def difficulty_changed(self, value):
        self.param.difficulty = value
    def min_changed(self, value):
        self.param.value_min = value
        self.window.maxBox.setMinimum(value+1)
    def max_changed(self, value):
        self.param.value_max = value
        self.window.minBox.setMaximum(value-1)

    def update_images(self):
        if not self.images:
            return
        
        if 'puzzle' in self.images:
            self.puzzlePix = toqpixmap(self.images['puzzle'])
        if 'solution' in self.images:
            self.solutionPix = toqpixmap(self.images['solution'])
        if 'nopuzzle' in self.images:
            self.puzzlePix = toqpixmap(self.images['nopuzzle'])
            self.solutionPix = toqpixmap(self.images['nopuzzle'])

        self.window.puzzleImageLabel.setPixmap(self.puzzlePix)
        self.window.solutionImageLabel.setPixmap(self.solutionPix)
        
    def generate_puzzle(self):
        # in another thread. That can be long.
        if not self.generating:
            self.generating = True
            self.progressbar_run()  
            self.information("Looking for a puzzle.")
            self.clean_images()
            self.myThread = LookingForPuzzleThread(self)
            self.myThread.finished.connect(self.when_get_puzzle)
            self.myThread.start(QThread.LowPriority)
            self.puzzleButtonText = self.window.puzzleButton.text()
            self.window.puzzleButton.setText("&Cancel")
        else:
            # cancel the running puzzle generation
            self.generating = False
            self.pyramid.cancel = True

    def when_get_puzzle(self):
        if self.pyramid.cancel:
            self.information("Operation cancelled.")
        else:
            self.redraw_images()

        self.generating = False
        self.window.puzzleButton.setText(self.puzzleButtonText)
        self.progressbar_stop()
        del self.myThread
        
    def information(self, text):
        self.window.informationLabel.setText(str(text))
        
    def clean_images(self):
        self.pyramid.puzzle = {}
        self.pyramid.solution = []
        self.redraw_images()

    def choose_directory(self):
        dirDialog = QFileDialog()
        # default irectory to "Documents"
        dirDialog.setDirectory(self.param.output_directory)
        self.param.output_directory = dirDialog.getExistingDirectory(self.window, "Select Directory")

    def save_puzzle(self):
        combo = self.window.outputTypeCombo
        printerClass = combo.currentData()
        printerClass(self.pyramid, self.param).print()
        
    def validate_pref(self):
        ts_index = self.pref_ui.timestampCombo.currentIndex()
        lw = self.pref_ui.linewidthBox.value()
        resolution = int(self.pref_ui.resolutionCombo.currentText())
        basename = self.pref_ui.basenameEdit.text()
        
        self.param.use_timestamp = True if ts_index == 0 else False
        self.param.line_width = lw
        self.param.resolution = resolution
        if basename:
            self.param.basename = basename
        self.redraw_images()
            
    def redraw_images(self):
        #redraw images with new parameters
        self.imagePrinter.print()
        self.images = self.imagePrinter.get_images()
        self.update_images()    

        
        
        
