'''
Created on 18/11/2015

@author: MMPE
'''
from __future__ import print_function
from __future__ import division
from __future__ import unicode_literals
from __future__ import absolute_import
from io import open
from builtins import int
from future import standard_library
standard_library.install_aliases()
import os
from wetb.hawc2.htc_file import HTCFile
from collections import OrderedDict
import time
import math
UNKNOWN = "Unknown"
MISSING = "Log file not found (May be waiting for PBS allocation)"
PENDING = "Simulation not started yet"
INITIALIZATION = 'Initializing simulation'
SIMULATING = "Simulating"
DONE = "Simulation succeded"


class LogInterpreter(object):
    def __init__(self, time_stop):
        self.time_stop = time_stop
        self.hawc2version = "Unknown"
        self.reset()
        self.update_status()

    def reset(self):
        self.position = 0
        self.lastline = ""
        self.txt = ""
        self.status = UNKNOWN
        self.pct = 0
        self.errors = []
        self.info = []
        self.start_time = None
        self.current_time = 0
        self.remaining_time = None

    def __str__(self):
        return self.txt

    def clear(self):
        self.reset()

    def extract_time(self, txt):
        i1 = txt.rfind("Global time")
        if i1 == -1:
            return self.current_time
        else:
            time_line = txt[i1:].strip()

        if time_line == "":
            return self.current_time
        try:
            return float(time_line[time_line.index('=') + 1:time_line.index('Iter')])
        except:
            print("Cannot extract time from #" + time_line + "#")
            pass

    def update_status(self, new_lines=""):
        if self.txt == "" and new_lines == "":
            self.status = MISSING
        else:
            if self.status == UNKNOWN or self.status == MISSING:
                self.status = PENDING
            txt = new_lines
            self.txt += txt
            if self.status == PENDING and self.position > 0:
                self.status = INITIALIZATION

            if len(txt) > 0:
                if len(txt.strip()):
                    self.lastline = (txt.strip()[max(0, txt.strip().rfind("\n")):]).strip()
                if self.status == INITIALIZATION:
                    _3to2list = list(txt.split("Starting simulation"))
                    init_txt, rest, = _3to2list[:1] + [_3to2list[1:]]
                    if self.hawc2version == "Unknown" and "Version ID" in init_txt:
                        self.hawc2version = txt.split("Version ID : ")[1].split("\n", 1)[0].strip()
                    if "*** ERROR ***" in init_txt:
                        self.errors.extend([l.strip() for l in init_txt.strip().split("\n") if "error" in l.lower()])
                    if rest:
                        txt = rest[0]
                        self.status = SIMULATING

                if self.status == SIMULATING:
                    if self.start_time is None and not 'Elapsed time' in self.lastline:
                        i1 = txt.rfind("Global time")
                        if i1 > -1:
                            self.start_time = (self.extract_time(txt[i1:]), time.time())

                    _3to2list1 = list(txt.split('Elapsed time'))
                    simulation_txt, rest, = _3to2list1[:1] + [_3to2list1[1:]]
                    if "*** ERROR ***" in simulation_txt:
                        self.errors.extend([l.strip()
                                            for l in simulation_txt.strip().split("\n") if "error" in l.lower()])
                    i1 = simulation_txt.rfind("Global time")
                    if i1 > -1:
                        self.current_time = self.extract_time(simulation_txt[i1:])
                    if self.current_time is not None and self.time_stop > 0:
                        self.pct = int(100 * self.current_time // self.time_stop)
                    try:
                        self.remaining_time = (
                            time.time() - self.start_time[1]) / (self.current_time - self.start_time[0]) * (self.time_stop - self.current_time)
                    except:
                        pass
                    if rest:
                        self.status = DONE
                        self.pct = 100
                        self.elapsed_time = float(rest[0].replace(":", "").strip())

    def error_str(self):
        error_dict = OrderedDict()
        for error in self.errors:
            error_dict[error] = error_dict.get(error, 0) + 1
        return "\n".join([("%d x %s" % (v, k), k)[v == 1] for k, v in error_dict.items()])

    def remaining_time_str(self):
        if self.remaining_time:
            if self.remaining_time < 3600:
                m, s = divmod(self.remaining_time, 60)
                return "%02d:%02d" % (m, int(s))
            else:
                h, ms = divmod(self.remaining_time, 3600)
                m, s = divmod(ms, 60)
                return "%d:%02d:%02d" % (h, m, int(s))
        else:
            return "--:--"


class LogFile(LogInterpreter):

    def __init__(self, log_filename, time_stop):
        self.filename = log_filename
        LogInterpreter.__init__(self, time_stop)

    @staticmethod
    def from_htcfile(htcfile, modelpath=None):
        logfilename = htcfile.simulation.logfile[0]
        if not os.path.isabs(logfilename):
            if modelpath is None:
                modelpath = htcfile.modelpath
            logfilename = os.path.join(modelpath, logfilename)
        return LogFile(logfilename, htcfile.simulation.time_stop[0])

    def clear(self):
        # exist_ok does not exist in Python27
        # if not os.path.exists(os.path.dirname(self.filename)):
        #    os.makedirs(os.path.dirname(self.filename))  #, exist_ok=True)
        if os.path.isfile(self.filename):
            try:
                with open(self.filename, 'w', encoding='utf-8'):
                    pass
            except PermissionError as e:
                raise PermissionError(str(e) + "\nLog file cannot be cleared. Check if it is open in another program")
        LogInterpreter.clear(self)

    def update_status(self):
        if not os.path.isfile(self.filename):
            self.status = MISSING
        else:
            if self.status == UNKNOWN or self.status == MISSING:
                self.status = PENDING
            s = self.status
            with open(self.filename, 'rb') as fid:
                fid.seek(self.position)
                txt = fid.read()
            self.position += len(txt)
            txt = txt.decode(encoding='cp1252', errors='strict')
            if txt != "":
                LogInterpreter.update_status(self, txt)


class LogInfo(LogFile):
    def __init__(self, status, pct, remaining_time, lastline):
        self.status = status
        self.pct = int(pct)
        try:
            self.remaining_time = float(remaining_time)
        except:
            self.remaining_time = None
        self.lastline = lastline
        self.errors = []

    def update_status(self):
        pass
