# -*- coding: UTF-8 -*-
'''
================================================================================
Project: SiriL-ic ( SiriL Image Companion )

This script structures the SiriL work folder into a subfolder, copies the
astronomical images into the subfolders, and builds the associated SiriL
script. It can also group scripts.
================================================================================
   Author:  M27trognondepomme <pebe92 (at) gmail.com>

This program is provided without any guarantee.

The license is  LGPL-v3
For details, see GNU General Public License, version 3 or later.
                       "https://www.gnu.org/licenses/gpl.html"
================================================================================
'''
import sys
import glob
import os
import gettext
import locale
import re
import math
from ast import literal_eval

# =============================================================================


def resource_path(relative_path, pathabs=None):
    """ Get absolute path to resource, works for dev and for PyInstaller """
    try:
        # PyInstaller creates a temp folder and stores path in _MEIPASS
        base_path = getattr(sys, '_MEIPASS')
    except AttributeError:  # pylint: disable=bare-except
        if pathabs is None:
            base_path = os.path.dirname(sys.argv[0])
        else:
            base_path = pathabs

    return os.path.join(base_path, relative_path)


def GetIconPath(iconname):
    iconname = os.path.join("..", "icon", iconname)
    if hasattr(sys, '_MEIPASS'):
        iconname = os.path.join(".", "icon", iconname)
    iconpath = os.path.dirname(os.path.abspath(__file__))
    return resource_path(iconname, pathabs=iconpath)


def init_language():
    try:
        if sys.platform.startswith('win'):
            if os.getenv('LANG') is None:
                lang, enc = locale.getdefaultlocale()
                os.environ['LANG'] = lang
                if len(enc) > 0:
                    os.environ['LANG'] += "." + enc
        langue = os.environ['LANG'].split('.')[0]
        if not re.search("_", langue):
            langue = langue.lower() + "_" + langue.upper()
        path_abs = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        i18n_dir = resource_path(os.path.join(
            'i18n', langue), pathabs=path_abs)

        if not os.path.exists(i18n_dir):
            gettext.install('sirilic')
        else:
            lang = gettext.translation('sirilic', os.path.dirname(i18n_dir),
                                       languages=[langue, os.environ['LANG']])
            lang.install()
    except Exception as e:   # pylint: disable=broad-except
        print("error init_language():" + str(e))
        gettext.install('sirilic')


init_language()


# =============================================================================
class GestionThread:

    _Abort_thread = False
    _Running_thread = None
    _Pipe_subprocess = None

    def __init__(self):
        pass

    def IsAborted(self):
        return GestionThread._Abort_thread

    def IsRunning(self):
        if GestionThread._Running_thread is not None:
            if GestionThread._Running_thread.is_alive():
                return True
        return False

    def SetRunning(self, value):
        GestionThread._Running_thread = value
        GestionThread._Abort_thread = False
        GestionThread._Pipe_subprocess = None

    def SetPipe(self, pipe_value):
        GestionThread._Pipe_subprocess = pipe_value

    def Abort(self):
        if self.IsRunning():
            GestionThread._Abort_thread = True
        if GestionThread._Pipe_subprocess is not None:
            GestionThread._Pipe_subprocess.kill()


# =============================================================================
def Stringify(data, ident1=4, ident2=8):
    IDENT1 = " " * ident1
    IDENT2 = " " * ident2
    str_data = str(data).replace(',', ',\n' + IDENT2)
    str_data = str_data.replace('{', IDENT1 + '{\n' + IDENT2)
    str_data = str_data.replace('}', '\n' + IDENT1 + '}')
    return str_data


# =============================================================================
def String2data(lines):
    str_data = ""
    for chaine in lines:
        chaine = chaine.lstrip()
        chaine = chaine.rstrip()
        if not chaine:
            continue
        if chaine[0] == "#":
            continue   # skip comments
        str_data = str_data + chaine
    return literal_eval(str_data)


# ==============================================================================
def path_conv(path):
    return os.path.normpath(path)


# ==============================================================================
def mkdirs(name):
    path = path_conv(name)
    if not os.path.exists(path):
        os.makedirs(path)


# ==============================================================================
def nettoyage_dossier(log, dossier, ext=None, prefix=None):
    log.print_ligne("-")
    log.print_titre('-', _('Cleaning : ') +  # pylint: disable=undefined-variable
                    dossier)

    if not os.path.exists(dossier):
        log.print_titre('-', "directory doesn't exist ... skipping")
        return

    if prefix is None:
        prefix = [""]

    for pp in prefix:
        # detruit les fichiers dans la destination avant de faire la copie
        remove_file(log, dossier + os.sep + pp + "*.fit")
        remove_file(log, dossier + os.sep + pp + "*.fits")
        remove_file(log, dossier + os.sep + pp + "*.fts")
        remove_file(log, dossier + os.sep + pp + "*.seq")
        remove_file(log, dossier + os.sep + pp + "*.ser")
        remove_file(log, dossier + os.sep + pp + "*.lst")
        remove_file(log, dossier + os.sep + pp + "*.fz")
        if ext is not None:
            remove_file(log, dossier + os.sep + pp + "*" + ext)


# ==============================================================================
def remove_file(log, pattern):
    liste = glob.glob(pattern)
    liste.sort()
    for fic in liste:
        cmd = "Remove " + fic
        log.insert(cmd + '\n')
        try:
            # # fix : symbolic link
            # permission = os.stat(fic).st_mode
            # if not bool( permission & stat.S_IWUSR ) :
            #     os.chmod(fic, stat.S_IWUSR | permission )
            # os.remove(fic)
            if os.path.islink(fic):
                os.unlink(fic)
            else:
                os.remove(fic)
        except Exception as e:   # pylint: disable=broad-except
            log.insert("... aborted")
            log.insert("*** remove_file() :" + str(e) + '\n')


# ==============================================================================
class fit:
    SZ_NKW = 8                      # taille du NameKeyWord d'un mot clef
    SZ_IKW = 2                      # taille du Value Indicator d'un mot cle
    SZ_VKW = 70                     # taille du Value/Comment d'un mot cle
    N_KW_HDU = 36                   # nombre de mot clef par bloc HDU
    SZ_KW = (SZ_NKW + SZ_IKW + SZ_VKW)  # taille d'un mot cle
    SZ_HDU = (N_KW_HDU * SZ_KW)         # taille d'un bloc HDU
    # taille maximum d'une chaine du champ Value/Comment d'un mot cle
    SZ_VKW_STR = (SZ_VKW - 2)
    # fin du champs de valeur en mode fixe dans le Value/Comment d'un mot cle
    END_VALFIX_VKW = (30 - 1)
    # fin du champs de valeur en mode fixe dans le HDU
    END_VALFIX_HDU = (END_VALFIX_VKW - SZ_NKW - SZ_IKW)

    def __init__(self, filename):
        self.filename = filename
        self.raz_HDU()

    def raz_HDU(self):
        self.hdu = {"END": False}

    def GetHDU(self):
        # with open("c:/temp/entetes.txt","w") as fdhdu :
        #    fdhdu.write("")

        all_hdu = []
        with open(self.filename, "rb") as fd:
            fd.seek(0, 2)  # Jumps to the end
            EndOfFile = fd.tell()  # Give you the end location
            fd.seek(0)  # Jump to the beginning of the file again
            while fd.tell() < EndOfFile:
                buffer_kw_line = self.read_header(fd)
                # with open("c:/temp/entetes.txt","a") as fdhdu :
                #    fdhdu.write(buffer_kw_line)

                isCompressed = False
                if 'SIMPLE' in self.hdu:
                    if 'NAXIS' not in self.hdu:
                        return []
                    if self.hdu['NAXIS'][0] == 0:
                        continue
                else:
                    if not ('XTENSION' in self.hdu):
                        return []
                    isCompressed = self.hdu['XTENSION'][0] == 'BINTABLE'

                if isCompressed:
                    cr = self.next_compressed_image(fd)
                else:
                    cr = self.next_image(fd)

                all_hdu.append(buffer_kw_line)
                if cr:
                    print("**** all_hdu[] => ", all_hdu)
                    return all_hdu

        return all_hdu

    def read_header(self, fd):
        self.raz_HDU()
        cnt_cmnt = 0
        cnt_hist = 0
        cnt = 0
        kw_prec = ''
        buffer_kw_line = ""
        while self.hdu['END'] is False:
            for _ii in range(0, self.N_KW_HDU):
                kw_line = fd.read(self.SZ_KW).decode('utf8', "strict")
                buffer_kw_line = buffer_kw_line + kw_line + '\n'
                kw = kw_line[:self.SZ_NKW].strip()
                ikw = kw_line[self.SZ_NKW:self.SZ_NKW + self.SZ_IKW - 1].strip()
                vkw = kw_line[self.SZ_NKW + self.SZ_IKW:].strip()
                ckw = kw_line[len(kw) + 1:].strip()
                cnt = cnt + 1
                if kw == '':
                    continue
                if ikw != '=':
                    if kw == 'CONTINUE' and kw_prec != '':
                        self.hdu[kw_prec] = self.hdu[kw_prec] + vkw
                        continue
                    if kw == 'END':
                        self.hdu['END'] = True
                    if kw == 'COMMENT':
                        self.hdu['COMMENT' + str(cnt_cmnt)] = ckw
                        cnt_cmnt = cnt_cmnt + 1
                    if kw == 'HISTORY':
                        self.hdu['HISTORY' + str(cnt_hist)] = ckw
                        cnt_hist = cnt_hist + 1
                    kw_prec = ''
                    continue
                self.hdu[kw] = vkw
                kw_prec = kw
        for kw, vkw in self.hdu.items():
            if type(vkw) is not str:
                continue
            vkw += ""
            if vkw[0] == "'":
                # Long String
                vkw = vkw.replace("&''", "")
                vkw = vkw.replace("'&' /", "")
                vkw = vkw.replace("'' /", "")
                vkw = vkw.replace("&' /", "/")
            value = vkw.split(' / ')
            if len(value) == 1:
                value = vkw.split(' /')  # fix for MaximDL 5.08
            self.hdu[kw] = []
            try:
                self.hdu[kw] = [literal_eval(value[0])]
            except Exception:   # pylint: disable=broad-except
                if value[0] == 'T':
                    self.hdu[kw] = [True]
                else:
                    if value[0] == 'F':
                        self.hdu[kw] = [False]
                    else:
                        self.hdu[kw] = [value[0]]
            if len(value) == 2:
                self.hdu[kw].append(  # pylint: disable=maybe-no-member
                    value[1])

        return buffer_kw_line

    def next_image(self, fd):
        if not ('BITPIX' in self.hdu):
            return True
        nb_bytes = self.size_image()
        if nb_bytes == 0:
            return False
        return self.skip_data(fd, nb_bytes)

    def next_compressed_image(self, fd):
        nb_bytes = 0
        if 'BITPIX' in self.hdu:
            nb_bytes = self.size_image()
        nb_bytes += self.hdu['GCOUNT'][0] * self.hdu['PCOUNT'][0]
        return self.skip_data(fd, nb_bytes)

    def size_image(self):
        nb_pix = 0
        if 'NAXIS1' in self.hdu:
            nb_pix = self.hdu['NAXIS1'][0]
        if 'NAXIS2' in self.hdu:
            nb_pix = nb_pix * self.hdu['NAXIS2'][0]
        if 'NAXIS3' in self.hdu:
            nb_pix = nb_pix * self.hdu['NAXIS3'][0]

        bytepix = math.ceil(abs(self.hdu['BITPIX'][0]) / 8)
        return nb_pix * bytepix

    def skip_data(self, fd, nb_bytes):
        try:
            fd.seek(nb_bytes, 1)  # skip image
        except Exception as e:   # pylint: disable=broad-except
            print("abort (1) " + self.filename + " fd.seek( " +
                  str(nb_bytes) + " , 1 ) : " + str(e) + '\n')
            print("**** self.hdu[] => ", self.hdu)
            return True

        modulo = nb_bytes % self.SZ_HDU
        if modulo != 0:
            try:
                fd.seek(self.SZ_HDU - modulo, 1)
            except Exception as e:   # pylint: disable=broad-except
                print("abort (2) " + self.filename + " fd.seek( " +
                      str(self.SZ_HDU - modulo) + " , 1 ) : " + str(e) + '\n')
                print("**** self.hdu[] => ", self.hdu)
                return True
        return False

    def GetImageNumber(self):
        return len(self.GetHDU())
