#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""Pychemqt, Chemical Engineering Process simulator
Copyright (C) 2009-2017, Juan José Gómez Romera <jjgomera@gmail.com>

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 3 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, see <http://www.gnu.org/licenses/>."""


import argparse
from configparser import ConfigParser
import json
import logging
import os
import shutil
import sys
import urllib.error


# Parse command line options
desc = """pychemqt intended as a free software tool for calculation and \
design of chemical engineering unit operations."""
further = """For any suggestions, comments, bug ... you can contact me at \
https://github.com/jjgomera/pychemqt or by email jjgomera@gmail.com."""

parser = argparse.ArgumentParser(description=desc, epilog=further)
parser.add_argument("-l", "--log", dest="loglevel", default="INFO",
                    help="Set level of report in log file")
parser.add_argument("--debug", action="store_true",
                    help="Enable loglevel to debug, the more verbose option")
parser.add_argument("-n", "--nosplash", action="store_true",
                    help="Don't show the splash screen at start")
parser.add_argument("--style", help="Set qt style")
parser.add_argument("projectFile", nargs="*",
                    help="Optional pychemqt project files to load at startup")
args = parser.parse_args()


# Add pychemqt folder to python path
path = os.path.dirname(os.path.realpath(sys.argv[0]))
sys.path.append(path)

# Define pychemqt environment
os.environ["pychemqt"] = path + os.sep
conf_dir = os.path.expanduser("~") + os.sep + ".pychemqt" + os.sep

# Check mandatory external dependences
# PyQt5
try:
    from PyQt5 import QtCore, QtGui, QtWidgets
except ImportError as err:
    print("PyQt5 could not be found, you must install it.")
    raise err

# Qt application definition
app = QtWidgets.QApplication(sys.argv)
app.setOrganizationName("pychemqt")
app.setOrganizationDomain("pychemqt")
app.setApplicationName("pychemqt")


# Qt style definition
if args.style is not None:
    style = QtWidgets.QStyleFactory.create(args.style)
    if style:
        app.setStyle(style)
    else:
        print("Undefined style option, the available options are: %s" %
              QtWidgets.QStyleFactory.keys())

# Add style options
app.setStyleSheet(
    "QDialogButtonBox {dialogbuttonbox-buttons-have-icons: true;}")


# Check qt configuration file
settings = QtCore.QSettings()
if not settings.contains("LastFile"):
    filename = QtCore.QVariant()
    settings.setValue("LastFile", filename)
    recentFiles = QtCore.QVariant()
    settings.setValue("RecentFiles", recentFiles)
    settings.setValue("Geometry", QtCore.QVariant())
    settings.setValue("MainWindow/State", QtCore.QVariant())


# Translation
locale = QtCore.QLocale.system().name()
myTranslator = QtCore.QTranslator()
if myTranslator.load("pychemqt_" + locale, os.environ["pychemqt"] + "i18n"):
    app.installTranslator(myTranslator)
qtTranslator = QtCore.QTranslator()
path = QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.TranslationsPath)
if qtTranslator.load("qt_" + locale, path):
    app.installTranslator(qtTranslator)


# scipy
try:
    import scipy
except ImportError as err:
    msg = QtWidgets.QApplication.translate(
        "pychemqt", "scipy could not be found, you must install it.")
    print(msg)
    raise err
else:
    mayor, minor, corr = map(int, scipy.version.version.split("."))
    if mayor == 0 and minor < 14:
        msg = QtWidgets.QApplication.translate(
            "pychemqt",
            "Your version of scipy is too old, you must update it.")
        raise ImportError(msg)

# numpy
try:
    import numpy
except ImportError as err:
    msg = QtWidgets.QApplication.translate(
        "pychemqt", "numpy could not be found, you must install it.")
    print(msg)
    raise err
else:
    mayor, minor, corr = map(int, numpy.version.version.split("."))
    if mayor < 1 or minor < 8:
        msg = QtWidgets.QApplication.translate(
            "pychemqt",
            "Your version of numpy is too old, you must update it.")
        raise ImportError(msg)

# matplotlib
try:
    import matplotlib
except ImportError as err:
    msg = QtWidgets.QApplication.translate(
        "pychemqt", "matplotlib could not be found, you must install it.")
    print(msg)
    raise err
else:
    mayor, minor, corr = map(int, matplotlib.__version__.split("."))
    if mayor < 1 or (mayor == 1 and minor < 4):
        msg = QtWidgets.QApplication.translate(
            "pychemqt",
            "Your version of matplotlib is too old, you must update it.")
        raise ImportError(msg)

# iapws
# Externalized version of iapws, to avoid duple maintenance
try:
    import iapws  # noqa
except ImportError as err:
    msg = QtWidgets.QApplication.translate(
        "pychemqt", "iapws could not be found, you must install it.")
    print(msg)
    raise err
else:
    if iapws.__version__ < "1.5":
        msg = QtWidgets.QApplication.translate(
            "pychemqt",
            "Your version of iapws is too old, you must update it.")
        raise ImportError(msg)


# TODO: Disable python-graph external dependence, functional mock up in
# project yet useless
# python-graph
# try:
    # from pygraph.classes.graph import graph  # noqa
    # from pygraph.algorithms.cycles import find_cycle  # noqa
# except ImportError as err:
    # msg = QtWidgets.QApplication.translate(
    #     "pychemqt", "Python-graph don't found, you need install it")
    # print(msg)
    # raise err


# Check external optional modules
from tools.dependences import optional_modules  # noqa
for module, use in optional_modules:
    try:
        __import__(module)
        os.environ[module] = "True"
    except ImportError:
        print("%s could not be found, %s" % (module, use))
        os.environ[module] = ""
    else:
        # Check required version
        if module == "CoolProp":
            import CoolProp.CoolProp as CP
            version = CP.get_global_param_string("version")
            mayor, minor, rev = map(int, version.split("."))
            if mayor < 6:
                print("Find CoolProp %s but CoolProp 6 required" % version)
                os.environ[module] = ""


# Logging configuration
if args.debug:
    loglevel = "DEBUG"
else:
    loglevel = args.loglevel
loglevel = getattr(logging, loglevel.upper())

# Checking config folder
if not os.path.isdir(conf_dir):
    os.mkdir(conf_dir)

try:
    open(conf_dir + "pychemqt.log", 'x')
except FileExistsError:  # noqa
    pass

fmt = "[%(asctime)s.%(msecs)d] %(levelname)s: %(message)s"
logging.basicConfig(filename=conf_dir+"pychemqt.log", filemode="w",
                    level=loglevel, datefmt="%d-%b-%Y %H:%M:%S", format=fmt)
logging.info(
    QtWidgets.QApplication.translate("pychemqt", "Starting pychemqt"))


# Derive numpy error log to pychemqt log
class NumpyErrorLog(object):
    """Numpy error message catch and send to pychemqt log
    Use debug level for this messages"""
    @staticmethod
    def write(msg):
        logging.debug(msg)

from numpy import seterr, seterrcall  # noqa
seterrcall(NumpyErrorLog)
seterr(all='log')


class SplashScreen(QtWidgets.QSplashScreen):
    """Class to define a splash screen to show loading progress"""
    def __init__(self):
        QtWidgets.QSplashScreen.__init__(
            self,
            QtGui.QPixmap(os.environ["pychemqt"] + "/images/splash.jpg"))
        QtWidgets.QApplication.flush()

    def showMessage(self, msg):
        """Procedure to update message in splash"""
        align = QtCore.Qt.Alignment(QtCore.Qt.AlignBottom |
                                    QtCore.Qt.AlignRight |
                                    QtCore.Qt.AlignAbsolute)
        color = QtGui.QColor(QtCore.Qt.white)
        QtWidgets.QSplashScreen.showMessage(self, msg, align, color)
        QtWidgets.QApplication.processEvents()

    def clearMessage(self):
        QtWidgets.QSplashScreen.clearMessage(self)
        QtWidgets.QApplication.processEvents()


splash = SplashScreen()
if not args.nosplash:
    splash.show()


# Checking config files
from tools import firstrun  # noqa
splash.showMessage(QtWidgets.QApplication.translate(
    "pychemqt", "Checking config files..."))

# Checking config file
default_Preferences = firstrun.Preferences()
change = False
if not os.path.isfile(conf_dir + "pychemqtrc"):
    default_Preferences.write(open(conf_dir + "pychemqtrc", "w"))
    Preferences = default_Preferences
    change = True
else:
    # Check Preferences options to find set new options
    Preferences = ConfigParser()
    Preferences.read(conf_dir + "pychemqtrc")
    for section in default_Preferences.sections():
        if not Preferences.has_section(section):
            Preferences.add_section(section)
            change = True
        for option in default_Preferences.options(section):
            if not Preferences.has_option(section, option):
                value = default_Preferences.get(section, option)
                Preferences.set(section, option, value)
                change = True
                logging.warning("Using default configuration option for " +
                                "%s:%s" % (section, option) +
                                ", run preferences dialog for configure")
    if change:
        Preferences.write(open(conf_dir + "pychemqtrc", "w"))

# FIXME: This file might not to be useful but for now I use it to save project
# configuration data
if not os.path.isfile(conf_dir + "pychemqtrc_temporal"):
    Config = firstrun.config()
    Config.write(open(conf_dir + "pychemqtrc_temporal", "w"))

# Checking costindex
splash.showMessage(QtWidgets.QApplication.translate(
    "pychemqt", "Checking cost index..."))
if not os.path.isfile(conf_dir + "CostIndex.dat"):
        orig = os.path.join(os.environ["pychemqt"], "dat", "costindex.dat")
        with open(orig) as cost_index:
            lista = cost_index.readlines()[-1].split(" ")
            with open(conf_dir + "CostIndex.dat", "w") as archivo:
                for data in lista:
                    archivo.write(data.replace(os.linesep, "") + os.linesep)

# Checking currency rates
splash.showMessage(QtWidgets.QApplication.translate(
    "pychemqt", "Checking currency data"))
currency = False
if not os.path.isfile(conf_dir + "moneda.dat"):
    # Exchange rates file don't available
    currency = True
else:
    filename = conf_dir+"moneda.dat"
    try:
        archivo = open(filename, "r")
        rates = json.load(archivo)
    except urllib.error.URLError:
        # Failed to load json file
        currency = True

    if not isinstance(rates["date"], int):
        # Old version exchange rates format, force upgrade
        currency = True

if currency:
    # Try to retrieve exchange rates from yahoo
    try:
        firstrun.getrates(conf_dir + "moneda.dat")
    except (urllib.error.URLError, urllib.error.HTTPError) as e:
        # Internet error, get hardcoded exchanges from pychemqt distribution
        # Possible outdated file, try to update each some commits
        origen = os.path.join(os.environ["pychemqt"], "dat", "moneda.dat")
        shutil.copy(origen, conf_dir + "moneda.dat")
        print(QtWidgets.QApplication.translate("pychemqt",
              "Internet connection error, using archived currency rates"))

# Checking database with custom components
splash.showMessage(QtWidgets.QApplication.translate(
    "pychemqt", "Checking custom database..."))
if not os.path.isfile(conf_dir + "databank.db"):
    firstrun.createDatabase(conf_dir + "databank.db")

# Import internal libraries
splash.showMessage(QtWidgets.QApplication.translate(
    "pychemqt", "Importing libraries..."))
from lib import *  # noqa
from UI import *  # noqa
from equipment import UI_equipments, equipments  # noqa
from tools import *  # noqa
from plots import *  # noqa

# Load main program UI
splash.showMessage(QtWidgets.QApplication.translate(
    "pychemqt", "Loading main window..."))
from UI.mainWindow import UI_pychemqt  # noqa
pychemqt = UI_pychemqt()

# Load project files, opened in last pychemqt session and/or specified in
# command line
msg = QtWidgets.QApplication.translate("pychemqt", "Loading project files")
splash.showMessage(msg + "...")
logging.info(msg)

if change:
    config.Preferences = Preferences

filename = []
if config.Preferences.getboolean("General", "Load_Last_Project"):
    filename = pychemqt.lastFile
    if filename is None:
        filename = []
for file in args.projectFile:
    filename.append(file)
for fname in filename:
    if fname and QtCore.QFile.exists(fname):
        msg = QtWidgets.QApplication.translate("pychemqt",
                                               "Loading project files...")
        splash.showMessage(msg + "\n" + fname)
        logging.info(msg + ": " + fname)
        pychemqt.fileOpen(fname)


# Manage error message to avoid print to console
def exceptfunction(error, msg, traceback):
    sys.__excepthook__(error, msg, traceback)
sys.excepthook = exceptfunction  # noqa

# Finish splash and start qt main loop
pychemqt.show()
splash.finish(pychemqt)
sys.exit(app.exec_())