from __future__ import annotations

from pathlib import Path
import struct

from ..Qt import QtCore
from .asciireader import QAsciiReader
from .hdfreader import QHdfViewer
from .bdsreader import QBDSReader
from .gracereader import QGraceReader
from .dscreader import QDSCReader
from .nmrreader import QNMRReader


class QFileReader(QtCore.QObject):
    data_read = QtCore.pyqtSignal([list], [dict])

    def __init__(self, manager=None):
        QtCore.QObject.__init__(self)

        self.select = 'all'
        self.data = []
        self.filenames = None
        self.extensions = set()

        self.reader = {}

        for ext, reader in [
            ('txt', QAsciiReader), ('dsc', QDSCReader), ('agr', QGraceReader),
            ('bds', QBDSReader), ('hdf', QHdfViewer),  ('nmr', QNMRReader)
        ]:
            self.register(ext, reader)

    def __call__(self, files: list[str] | str) -> list:
        self.data = []
        if isinstance(files, str):
            self.filenames = [files]
        else:
            self.filenames = files

        return self.readfiles(files)

    def readfiles(self, fname: list[str] | str) -> list:
        # clear old data
        self.data = []

        if not isinstance(fname, list):
            fname = [fname]

        for f in fname:
            f = Path(f)
            dtype = self.guess_type(f)
            if dtype in self.reader:
                r = self.reader[dtype]
            else:
                raise ValueError(f'Unknown type for file {f}')

            try:
                # If QAsciiReader.skip = True it accepts automatically and returns None
                r(f).exec()
            except AttributeError:
                pass

        self.data_read.emit(self.data)

        self.reader['txt'].skip = False

        return self.data

    def readtnt(self, fname):
        """ Special treatment for tnt. If data is a single measurement, skip dialog """
        raise NotImplementedError
        # tntreader = self.reader[2]
        # tntreader(fname)
        # if not tntreader.reader.onedimensional:
        #     tntreader.exec()

    @QtCore.pyqtSlot(list)
    def _update_dic(self, other: list):
        self.data.extend(other)

    def register(self, key, new_reader):
        if key in self.reader:
            return self.reader[key]
        r = new_reader()
        self.reader[key] = r
        r.data_read.connect(self._update_dic)
        self.extensions.update(r.file_ext)

        return r

    def guess_type(self, fname: Path) -> str:
        ext = fname.suffix.lower()

        if ext in self.extensions:
            if ext in ('.dat', '.txt'):
                with fname.open('r', encoding='iso-8859-15') as fp:
                    line = fp.readline()
                    if line.strip().startswith('Filename:	C:\\'):
                        return 'dsc'

                return 'txt'

            if ext in ('.h5', '.hdf', '.hdf5'):
                return 'hdf'

            if ext == '.eps':
                return 'bds'

            return ext[1:]  # remove dot

        else:
            with fname.open('rb') as fp:
                s16 = fp.read(16)

                # copied from whichdb (Python 2.7) to look for magic value of dbhash
                (magic,) = struct.unpack("=l", s16[-4:])
                if magic in (0x00061561, 0x61150600):
                    return 'nmr'

                else:
                    ret_types = ('nmr', 'bds', 'tnt', 'agr', 'dsc')
                    strings = (b'NMREVAL', b'NOVOCONTROL', b'TNT1.005', b'# Grace project', b'Filename:\tC:')

                    (magic,) = struct.unpack('<16s', s16)
                    for dtype, startstring in zip(ret_types, strings):
                        if magic.startswith(startstring):
                            return dtype

                    # nothing matched, text file is best guess
                    return 'txt'
