import pandas as pd
import os
import warnings

from ..base import BaseRaw
from pyActigraphy.light import LightRecording


class RawMESA(BaseRaw):
    r"""Raw object from MESA files

    Parameters
    ----------
    input_fname: str
        Path to the MESA file.
    time_origin: datetime-like
        Time origin of the timestamps.
        Required as the MESA files do not contain date informations.
        Default is '2000-01-01'
    start_time: datetime-like, optional
        Read data from this time.
        Default is None.
    period: str, optional
        Length of the read data.
        Cf. #timeseries-offset-aliases in
        <https://pandas.pydata.org/pandas-docs/stable/timeseries.html>.
        Default is None (i.e all the data).
    intervals: dict, optional
        Map manually annotated periods to specific scores.
        If set to None, the names of the annotated periods is returned instead.
        Default is {'EXCLUDED': -1, 'ACTIVE': 1, 'REST': 0.5, 'REST-S': 0}.
    check_dayofweek: bool, optional
        If set to True, check if the day of the week reported in the original
        recoring is aligned with the reconstructed index.
        Default is False.
    """

    def __init__(
        self,
        input_fname,
        time_origin='2000-01-01',
        start_time=None,
        period=None,
        intervals={'EXCLUDED': -1, 'ACTIVE': 1, 'REST': 0.5, 'REST-S': 0},
        check_dayofweek=False
    ):

        # get absolute file path
        input_fname = os.path.abspath(input_fname)

        # read file
        data = pd.read_csv(input_fname, index_col='line')

        # extract informations from the header
        name = data.loc[1, 'mesaid']

        # set additional informations manually
        uuid = None
        freq = pd.Timedelta(30, unit='s')

        # day of the week
        self.__dayofweek = data['dayofweek']

        # reconstruct MESA datetime index
        date = pd.to_datetime(
            data['daybymidnight'] - 1 + data.loc[1, 'dayofweek'],
            unit='D',
            origin=time_origin
        ).astype(str)
        time = data['linetime']

        index = pd.DatetimeIndex(date + ' ' + time, freq='infer')

        data.set_index(index, inplace=True)

        if check_dayofweek:
            # Shift day of the week to match Pandas' convention (0=Monday, etc)
            dw = self.__dayofweek - 2
            if (data.index.dayofweek - dw.where(dw >= 0, dw + 7)).sum() != 0:
                warnings.warn((
                    "Specified time_origin is such that the day of the week in"
                    " the reconstructed time index is *not* aligned with the"
                    " day of the week reported in the recording."
                ))

        # set start and stop times
        if start_time is not None:
            start_time = pd.to_datetime(start_time)
        else:
            start_time = data.index[0]

        if period is not None:
            period = pd.Timedelta(period)
            stop_time = start_time+period
        else:
            stop_time = data.index[-1]
            period = stop_time - start_time

        data = data[start_time:stop_time]

        # no wear indicator
        self.__nowear = data['offwrist']

        # event marker indicator
        self.__marker = data['marker']

        # LIGHT
        self.__white_light = data['whitelight']
        self.__red_light = data['redlight']
        self.__green_light = data['greenlight']
        self.__blue_light = data['bluelight']

        # wake indicator
        self.__wake = data['wake']

        # intervals
        if intervals is not None:
            self.__intervals = data['interval'].map(intervals)
        else:
            self.__intervals = data['interval']

        # call __init__ function of the base class
        super().__init__(
            fpath=input_fname,
            name=name,
            uuid=uuid,
            format='MESA',
            axial_mode='tri-axial',
            start_time=start_time,
            period=period,
            frequency=freq,
            data=data['activity'],
            light=LightRecording(
                name=name,
                uuid=uuid,
                data=data.loc[:, [
                    'whitelight', 'redlight', 'greenlight', 'bluelight'
                    ]
                ],
                frequency=data.index.freq
            )
        )

    @property
    def marker(self):
        r"""Event marker indicator."""
        return self.__marker

    @property
    def wake(self):
        r"""Awake indicator."""
        return self.__wake

    @property
    def nowear(self):
        r"""Off-wrist indicator."""
        return self.__nowear

    @property
    def intervals(self):
        r"""Interval type (manual rest-activty scoring)."""
        return self.__intervals

    @property
    def dayofweek(self):
        r"""Day of the week (1=Sunday, 2=Monday, etc)."""
        return self.__dayofweek

    @property
    def white_light(self):
        r"""Value of the white light illuminance in lux."""
        return self.__white_light

    @property
    def red_light(self):
        r"""Value of the light intensity in µw/cm²."""
        return self.__red_light

    @property
    def green_light(self):
        r"""Value of the light intensity in µw/cm²."""
        return self.__green_light

    @property
    def blue_light(self):
        r"""Value of the light intensity in µw/cm²."""
        return self.__blue_light


def read_raw_mesa(
    input_fname,
    time_origin='2000-01-01',
    start_time=None,
    period=None,
    intervals={'EXCLUDED': -1, 'ACTIVE': 1, 'REST': 0.5, 'REST-S': 0},
    check_dayofweek=False
):
    r"""Reader function for MESA files

    Parameters
    ----------
    input_fname: str
        Path to the ActTrust file.
    time_origin: datetime-like
        Time origin of the timestamps.
        Required as the MESA files do not contain date informations.
        Default is '2000-01-01'
    start_time: datetime-like, optional
        Read data from this time.
        Default is None.
    period: str, optional
        Length of the read data.
        Cf. #timeseries-offset-aliases in
        <https://pandas.pydata.org/pandas-docs/stable/timeseries.html>.
        Default is None (i.e all the data).
    intervals: dict, optional
        Map manually annotated periods to specific scores.
        If set to None, the names of the annotated periods is returned instead.
        Default is {'EXCLUDED': -1, 'ACTIVE': 1, 'REST': 0.5, 'REST-S': 0}.
    check_dayofweek: bool, optional
        If set to True, check if the day of the week reported in the original
        recoring is aligned with the reconstructed index.
        Default is False.

    Returns
    -------
    raw : Instance of RawMESA
        An object containing raw MESA data
    """

    return RawMESA(
        input_fname=input_fname,
        time_origin=time_origin,
        start_time=start_time,
        period=period,
        intervals=intervals,
        check_dayofweek=check_dayofweek
    )
