# Authors: The MNE-Python contributors.
# License: BSD-3-Clause
# Copyright the MNE-Python contributors.

import calendar
import datetime
from os import path

import numpy as np

from ..._fiff.constants import FIFF
from ..._fiff.meas_info import _empty_info
from ..._fiff.utils import _create_chs, _find_channels, _read_segments_file
from ...utils import fill_doc, logger
from ..base import BaseRaw


@fill_doc
def read_raw_nicolet(
    input_fname, ch_type, eog=(), ecg=(), emg=(), misc=(), preload=False, verbose=None
) -> "RawNicolet":
    """Read Nicolet data as raw object.

    .. note:: This reader takes data files with the extension ``.data`` as an
             input. The header file with the same file name stem and an
             extension ``.head`` is expected to be found in the same
             directory.

    Parameters
    ----------
    input_fname : path-like
        Path to the data file (ending with ``.data`` not ``.head``).
    ch_type : str
        Channel type to designate to the data channels. Supported data types
        include ``'eeg'``, ``'dbs'``.
    eog : list | tuple | ``'auto'``
        Names of channels or list of indices that should be designated
        EOG channels. If ``'auto'``, the channel names beginning with
        ``EOG`` are used. Defaults to empty tuple.
    ecg : list or tuple | ``'auto'``
        Names of channels or list of indices that should be designated
        ECG channels. If ``'auto'``, the channel names beginning with
        ``ECG`` are used. Defaults to empty tuple.
    emg : list or tuple | ``'auto'``
        Names of channels or list of indices that should be designated
        EMG channels. If ``'auto'``, the channel names beginning with
        ``EMG`` are used. Defaults to empty tuple.
    misc : list or tuple
        Names of channels or list of indices that should be designated
        MISC channels. Defaults to empty tuple.
    %(preload)s
    %(verbose)s

    Returns
    -------
    raw : instance of Raw
        A Raw object containing the data.

    See Also
    --------
    mne.io.Raw : Documentation of attributes and methods.
    """
    return RawNicolet(
        input_fname,
        ch_type,
        eog=eog,
        ecg=ecg,
        emg=emg,
        misc=misc,
        preload=preload,
        verbose=verbose,
    )


def _get_nicolet_info(fname, ch_type, eog, ecg, emg, misc):
    """Extract info from Nicolet header files."""
    fname, extension = path.splitext(fname)

    if extension != ".data":
        raise ValueError(f'File name should end with .data not "{extension}".')

    header = fname + ".head"

    logger.info("Reading header...")
    header_info = dict()
    with open(header) as fid:
        for line in fid:
            var, value = line.split("=")
            if var == "elec_names":
                value = value[1:-2].split(",")  # strip brackets
            elif var == "conversion_factor":
                value = float(value)
            elif var in ["num_channels", "rec_id", "adm_id", "pat_id", "num_samples"]:
                value = int(value)
            elif var != "start_ts":
                value = float(value)
            header_info[var] = value

    ch_names = header_info["elec_names"]
    if eog == "auto":
        eog = _find_channels(ch_names, "EOG")
    if ecg == "auto":
        ecg = _find_channels(ch_names, "ECG")
    if emg == "auto":
        emg = _find_channels(ch_names, "EMG")

    date, time = header_info["start_ts"].split()
    date = date.split("-")
    time = time.split(":")
    sec, msec = time[2].split(".")
    date = datetime.datetime(
        int(date[0]),
        int(date[1]),
        int(date[2]),
        int(time[0]),
        int(time[1]),
        int(sec),
        int(msec),
    )
    info = _empty_info(header_info["sample_freq"])
    info["meas_date"] = (calendar.timegm(date.utctimetuple()), 0)

    if ch_type == "eeg":
        ch_coil = FIFF.FIFFV_COIL_EEG
        ch_kind = FIFF.FIFFV_EEG_CH
    elif ch_type == "seeg":
        ch_coil = FIFF.FIFFV_COIL_EEG
        ch_kind = FIFF.FIFFV_SEEG_CH
    else:
        raise TypeError(
            "Channel type not recognized. Available types are 'eeg' and 'seeg'."
        )
    cals = np.repeat(header_info["conversion_factor"] * 1e-6, len(ch_names))
    info["chs"] = _create_chs(ch_names, cals, ch_coil, ch_kind, eog, ecg, emg, misc)
    info["highpass"] = 0.0
    info["lowpass"] = info["sfreq"] / 2.0
    info._unlocked = False
    info._update_redundant()
    return info, header_info


class RawNicolet(BaseRaw):
    """Raw object from Nicolet file.

    Parameters
    ----------
    input_fname : path-like
        Path to the Nicolet file.
    ch_type : str
        Channel type to designate to the data channels. Supported data types
        include ``'eeg'``, ``'seeg'``.
    eog : list | tuple | ``'auto'``
        Names of channels or list of indices that should be designated
        EOG channels. If ``'auto'``, the channel names beginning with
        ``EOG`` are used. Defaults to empty tuple.
    ecg : list or tuple | ``'auto'``
        Names of channels or list of indices that should be designated
        ECG channels. If ``'auto'``, the channel names beginning with
        ``ECG`` are used. Defaults to empty tuple.
    emg : list or tuple | ``'auto'``
        Names of channels or list of indices that should be designated
        EMG channels. If ``'auto'``, the channel names beginning with
        ``EMG`` are used. Defaults to empty tuple.
    misc : list or tuple
        Names of channels or list of indices that should be designated
        MISC channels. Defaults to empty tuple.
    %(preload)s
    %(verbose)s

    See Also
    --------
    mne.io.Raw : Documentation of attributes and methods.
    """

    def __init__(
        self,
        input_fname,
        ch_type,
        eog=(),
        ecg=(),
        emg=(),
        misc=(),
        preload=False,
        verbose=None,
    ):
        input_fname = path.abspath(input_fname)
        info, header_info = _get_nicolet_info(input_fname, ch_type, eog, ecg, emg, misc)
        last_samps = [header_info["num_samples"] - 1]
        super().__init__(
            info,
            preload,
            filenames=[input_fname],
            raw_extras=[header_info],
            last_samps=last_samps,
            orig_format="int",
            verbose=verbose,
        )

    def _read_segment_file(self, data, idx, fi, start, stop, cals, mult):
        """Read a chunk of raw data."""
        _read_segments_file(self, data, idx, fi, start, stop, cals, mult, dtype="<i2")
