# -*- coding: utf-8 -*-
# Module for loading .nxs files from SIXS beamline @ SOLEIL
# To be used together with the dictionnary 'alias_dict.txt'
# Code from Andrea Resta @ Soleil Synchrotron
# Modified 02052019 by Jerome Carnis @ CNRS IM2NP: PEP8 + removed unused functions
"""
nxsReady.

This module contains Classes and functions to load data at SIXS beamline.
"""

import tables
import numpy
import pickle

print("importing nxsReady")
print("'alias_dict.txt' path expected in 'specfile_name' parameter")
print("You can copy it from /Lib/site-packages/bcdi/preprocessing/")


class PrefParameters:
    """Class for preference parameters."""

    def __init__(self):
        self.namedisplays = [
            "my suggestion",
            "short name",
            "short and family name",
            "full name",
            "nxs name",
        ]
        self.inamedisplay = 0


class DataSet:
    """
    Class dealing with datasets.

    It reads the file and stores it in an object, from this object we can retrieve
    the data to use it: MyFileObject=nxsRead.DataSet(path/filename, filename)
    """

    # define a classe to enter .nxs fiel-related parameters
    # the long name is the pathename
    # short name should be the filenmane
    # alias_dict: the alias dictionnary, which should be located in the root directory
    # of the experiment
    def __init__(
        self, longname, shortname, alias_dict, datafilter=None, pref=None, scan="FLY"
    ):
        alias_dict = pickle.load(open(alias_dict, "rb"))
        if pref is None:
            pref = PrefParameters()
        self.shortname = shortname
        self.THRESHOLD = 0
        shift = 0
        if scan == "FLY":
            shift = 1
        if scan == "SBS":
            shift = 0
        if scan == "HCS":
            shift = 1
        try:
            self.file = tables.open_file(longname, "r")
            self.nodedatasizes = []  # list of data array lengths
            for leaf in self.file.list_nodes("/")[0].scan_data:
                self.nodedatasizes.append(leaf.shape[0])
            self.npts = max(self.nodedatasizes)

            # we select only nodes of the same size, smaller arrays (e.g. of size 1)
            # are let aside here it generate the attributes of the DataSet class by
            # defining their type
            self.nodenames = []  # node names (ex data_01)
            self.nodelongnames = []  # node comprehensible name AKA complete
            # (ex: i14-c-cx2/ex/diff-uhv-k/position)
            self.nodenicknames = []
            # shortening of the long name AKA the last part of longname
            self.alias = []
            self.data = numpy.empty(0)  # empty table creation
            self.waveL = self.file.list_nodes("/")[0].SIXS.Monochromator.wavelength[0]
            self.energymono = self.file.list_nodes("/")[0].SIXS.Monochromator.energy[0]
            if self.file.list_nodes("/")[0].end_time.shape == ():
                self.end_time = self.file.list_nodes("/")[0].end_time.read().tostring()
            if self.file.list_nodes("/")[0].end_time.shape == (1,):
                self.end_time = self.file.list_nodes("/")[0].end_time[0]

            # here we assign the values to the attributes previously generated
            for leaf in self.file.list_nodes("/")[0].scan_data:
                nodelongname = ""
                nodenickname = ""
                if len(leaf.shape) == 1:
                    if leaf.shape[0] == self.npts:
                        self.nodenames.append(leaf.name)

                        try:
                            nodelongname = leaf.attrs.long_name.decode("UTF-8")
                        except tables.exceptions.NoSuchNodeError:
                            nodelongname = (
                                str(leaf)
                                .split()[0]
                                .split("/")[-1]
                                .split("_")[-1]
                                .lower()
                            )

                        if len(nodelongname) == 0:
                            nodelongname = leaf.name  # if no name keep nxs file name
                        self.nodelongnames.append(nodelongname)
                        self.data = numpy.concatenate((self.data, leaf.read()[1:]))
                        # add data to numpy array and remove the first point

                        if pref.inamedisplay <= 1:
                            nodenickname = nodelongname.split("/")[-1]
                            # take just the last part of the longname
                            self.nodenicknames.append(nodenickname)

                        elif pref.inamedisplay == 2:
                            try:
                                namesplit = nodelongname.split("/")
                                nodenickname = namesplit[-2] + "/" + namesplit[-1]
                                # take the two last if possible
                                self.nodenicknames.append(nodenickname)
                            except IndexError:
                                self.nodenicknames.append(nodelongname)

                        elif pref.inamedisplay == 3:
                            self.nodenicknames.append(nodelongname)
                            # take the full long name

                        elif pref.inamedisplay == 4:
                            self.nodenicknames.append(leaf.name)  # take nxs file name

                    if alias_dict:
                        try:
                            alias = alias_dict[nodelongname.lower()]
                            if alias in self.alias:
                                alias += "#"
                            self.alias.append(alias)
                            self.__dict__[alias] = leaf.read()[shift:]
                        except KeyError:
                            self.alias.append(nodenickname)
                            self.__dict__[nodenickname] = leaf.read()[shift:]

                elif len(leaf.shape) == 3:
                    if leaf.shape[1] == 1065:
                        if shift:
                            self.efilm = leaf[:-shift]
                        else:
                            self.efilm = leaf[:]
                            # Careful: process is different for HCS, SBS and FLY
                    if leaf.shape[1] == 240:
                        if shift:
                            self.xfilm = leaf[:-shift]
                        else:
                            self.xfilm = leaf[:]
                    if leaf.shape[1] == 516:
                        if shift:
                            self.mfilm = leaf[:-shift]
                        else:
                            self.mfilm = leaf[:]

        except ValueError:
            print("probleme le fichier ", longname, "est corrompu")
            self.npts = 0
            self.nmotors = 0
            self.mins = numpy.empty(0)
            self.maxs = numpy.empty(0)
            self.data = numpy.empty(0)
            self.file.close()
            return

        except tables.exceptions.NoSuchNodeError:
            print("probleme le fichier ", longname, "est corrompu")
            self.npts = 0
            self.nmotors = 0
            self.mins = numpy.empty(0)
            self.maxs = numpy.empty(0)
            self.data = numpy.empty(0)
            self.file.close()
            return

        else:
            self.file.close()

        self.npts = self.npts - 1  # remove the 1st point that is uncorrect due to
        # the operation strategy of simplescan
        self.nmotors = len(self.nodenames)  # number of columns kept

        # if display preferences are "my suggestion",
        # we look for a name appearing several times
        # in this case we choose the longest name

        if pref.inamedisplay == 0:
            for i in range(self.nmotors - 1):  # pas la peine de faire le dernier point!

                nickname = self.nodenicknames[i]

                if nickname in self.nodenicknames[i + 1 :]:  # item in double
                    nodelongname = self.nodelongnames[i]
                    namesplit = nodelongname.split("/")
                    try:
                        nodenickname = namesplit[-2] + "/" + namesplit[-1]
                        # take the two last
                    except IndexError:
                        nodenickname = nodelongname  # take the two last

                    self.nodenicknames[i] = nodenickname

                    j = i
                    try:
                        while 1:
                            j = self.nodenicknames.index(j + 1)
                            self.nodenicknames[j] = nodenickname
                            # careful, it is not garanteed that nodenickname!=nickname
                    except ValueError:
                        pass

        self.data = self.data.reshape((self.nmotors, self.npts))
        test = numpy.any(self.data != 0, axis=0)
        # if non-zero value, the condition is verified
        self.data = numpy.compress(test, self.data, axis=1)

        if datafilter is not None:
            # filter values while looking at the condition on the filter
            if datafilter.role != "none" and datafilter.ifil > -1:
                if datafilter.irole == 1:
                    self.data = numpy.compress(
                        self.data[datafilter.ifil] > datafilter.value, self.data, axis=1
                    )
                elif datafilter.irole == 2:
                    self.data = numpy.compress(
                        self.data[datafilter.ifil] < datafilter.value, self.data, axis=1
                    )
                elif datafilter.irole == 3:
                    self.data = numpy.compress(
                        self.data[datafilter.ifil] != datafilter.value,
                        self.data,
                        axis=1,
                    )

        self.npts = self.data.shape[1]  # number of points not totally null

        if self.npts == 0:  # no more points after filtering
            self.mins = numpy.zeros(self.nmotors)
            self.maxs = numpy.ones(self.nmotors)
        else:
            self.mins = numpy.amin(self.data, axis=1)  # boundary for each parameter
            self.maxs = numpy.amax(self.data, axis=1)

        self.namelist = self.alias

    def close(self):
        """Close the data file."""
        self.file.close()
