import re
import os
import datetime
import numpy as np
import pandas as pd
from glob import glob

CAMELCASE_PATTERN = r'(?:[A-Z][A-Za-z0-9]+)+'
VERSIONCODE_PATTERN = r'(?:NA|[0-9x]+)'
SID_PATTERN = r'[A-Z0-9]+'
ANNOTATOR_PATTERN = r'[A-Za-z0-9]+'
FILE_TIMESTAMP_PATTERN = r'[0-9]{4}(?:\-[0-9]{2}){5}-[0-9]{3}-(?:P|M)[0-9]{4}'
FILE_EXTENSION_PATTERN = r'''
(?:sensor|event|log|annotation|feature|class|prediction|model|classmap)\.csv
'''
MHEALTH_FLAT_FILEPATH_PATTERN = r'(\w+)[\/\\]{1}(?:(?:MasterSynced[\/\\]{1})|(?:Derived[\/\\]{1}(?:\w+[\/\\]{1})*))[0-9A-Za-z\-\.]+\.csv'
MHEALTH_FILEPATH_PATTERN = r'(\w+)[\/\\]{1}(?:(?:MasterSynced[\/\\]{1})|(?:Derived[\/\\]{1}(?:\w+[\/\\]{1})*))\d{4}[\/\\]{1}\d{2}[\/\\]{1}\d{2}[\/\\]{1}\d{2}'


def is_mhealth_filepath(filepath):
    filepath = os.path.abspath(filepath)
    matched = re.search(
        MHEALTH_FILEPATH_PATTERN,
        filepath)
    return matched is not None


def is_mhealth_flat_filepath(filepath):
    matched = re.search(
        MHEALTH_FLAT_FILEPATH_PATTERN,
        os.path.abspath(filepath)
    )
    return matched is not None


def get_mhealth_root(filepath):
    assert is_mhealth_filepath(filepath) or is_mhealth_flat_filepath(filepath)
    pid = get_pid(filepath)
    return filepath.split(pid)[0]


def get_pid_root(filepath):
    assert is_mhealth_filepath(filepath) or is_mhealth_flat_filepath(filepath)
    pid = get_pid(filepath)
    return os.path.join(filepath.split(pid)[0], pid)


def find_location_mapping(filepath):
    pid_root = get_pid_root(filepath)
    candicates = glob(os.path.join(
        pid_root, '**', 'location_mapping.csv'), recursive=True)
    if len(candicates) == 1:
        return candicates[0]
    else:
        mhealth_root = get_mhealth_root(filepath)
        result = os.path.join(
            mhealth_root, 'DerivedCrossParticipants', 'location_mapping.csv')
        if os.path.exists(result):
            return result
        else:
            return False


def find_offset_mapping(filepath):
    pid_root = get_pid_root(filepath)
    candicates = glob(os.path.join(
        pid_root, '**', 'offset_mapping.csv'), recursive=True)
    if len(candicates) == 1:
        return candicates[0]
    else:
        mhealth_root = get_mhealth_root(filepath)
        result = os.path.join(
            mhealth_root, 'DerivedCrossParticipants', 'offset_mapping.csv')
        if os.path.exists(result):
            return result
        else:
            return False


def find_pid_exceptions(filepath):
    mhealth_root = get_mhealth_root(filepath)
    result = os.path.join(
        mhealth_root, 'DerivedCrossParticipants', 'pid_exceptions.csv')
    if os.path.exists(result):
        return result
    else:
        return False


def find_orientation_corrections(filepath):
    pid_root = get_pid_root(filepath)
    candicates = glob(os.path.join(
        pid_root, '**', 'orientation_corrections.csv'), recursive=True)
    if len(candicates) == 1:
        return candicates[0]
    else:
        mhealth_root = get_mhealth_root(filepath)
        result = os.path.join(
            mhealth_root, 'DerivedCrossParticipants', 'orientation_corrections.csv')
        if os.path.exists(result):
            return result
        else:
            return False


def is_mhealth_filename(filepath):
    filename = os.path.basename(filepath)

    sensor_filename_pattern = '^' + CAMELCASE_PATTERN + '\-' + \
        CAMELCASE_PATTERN + \
        '\-' + VERSIONCODE_PATTERN + '\.' + \
        SID_PATTERN + '\-' + CAMELCASE_PATTERN + '\.' + \
        FILE_TIMESTAMP_PATTERN + '\.sensor\.csv$'

    annotation_filename_pattern = '^' + CAMELCASE_PATTERN + '\.' + \
        ANNOTATOR_PATTERN + '\-' + CAMELCASE_PATTERN + '\.' + \
        FILE_TIMESTAMP_PATTERN + '\.annotation\.csv$'

    sensor_matched = re.search(
        sensor_filename_pattern,
        filename
    )
    annotation_matched = re.search(
        annotation_filename_pattern,
        filename
    )
    return sensor_matched is not None or annotation_matched is not None


def get_pid(filepath):
    if is_mhealth_filepath(filepath):
        matched = re.search(MHEALTH_FILEPATH_PATTERN, filepath)
    elif is_mhealth_flat_filepath(filepath):
        matched = re.search(MHEALTH_FLAT_FILEPATH_PATTERN, filepath)
    else:
        return None
    return matched.group(1) if matched is not None else None


def get_sensor_type(filepath):
    assert is_mhealth_filename(filepath)
    filename = os.path.basename(filepath)
    result = filename.split('.')[0].split('-')[0]
    return result


def get_data_type(filepath):
    assert is_mhealth_filename(filepath)
    filename = os.path.basename(filepath)
    tokens = filename.split('.')[0]
    tokens = tokens.split('-')
    if len(tokens) >= 2:
        return tokens[1]
    else:
        return None


def get_version_code(filepath):
    assert is_mhealth_filename(filepath)
    filename = os.path.basename(filepath)
    tokens = filename.split('.')[0]
    tokens = tokens.split('-')
    if len(tokens) >= 3:
        return tokens[2]
    else:
        return None


def get_sid(filepath):
    assert is_mhealth_filename(filepath)
    filename = os.path.basename(filepath)
    return filename.split('.')[1].split('-')[0]


def get_file_type(filepath):
    assert is_mhealth_filename(filepath)
    filename = os.path.basename(filepath)
    return filename.split('.')[-2]


def get_file_timestamp(filepath):
    assert is_mhealth_filename(filepath)
    filename = os.path.basename(filepath)
    timestamp_str = filename.split('.')[-3]
    timestamp_str = timestamp_str[:-6]
    return datetime.datetime.strptime(timestamp_str, '%Y-%m-%d-%H-%M-%S-%f')


def get_session_start_time(filepath, filepaths):
    pid = get_pid(filepath)
    smallest = datetime.datetime.now()
    for path in filepaths:
        if get_pid(path) == pid:
            timestamp = get_file_timestamp(path)
            if timestamp < smallest:
                smallest = timestamp
    smallest = smallest.replace(microsecond=0, second=0, minute=0)
    return smallest


def get_session_end_time(filepath, filepaths):
    pid = get_pid(filepath)
    largest = datetime.datetime.fromtimestamp(100000)
    for path in filepaths:
        if get_pid(path) == pid:
            timestamp = get_file_timestamp(path)
            if timestamp > largest:
                largest = timestamp
    largest = largest.replace(microsecond=0, second=0,
                              minute=0) + datetime.timedelta(hours=1)
    return largest


def get_timezone(filepath):
    dt = get_file_timestamp(filepath)
    return dt.tzinfo


def get_timezone_name(filepath):
    dt = get_file_timestamp(filepath)
    return dt.strftime('%Z')


def get_init_placement(filepath, mapping_file):
    assert is_mhealth_filename(filepath)
    mapping = pd.read_csv(mapping_file)
    sid = get_sid(filepath)
    pid = get_pid(filepath)
    loc = mapping.loc[(mapping['PID'] == pid) & (
        mapping['SENSOR_ID'] == sid), 'LOCATION'].values[0]
    return loc


def auto_init_placement(filepath):
    assert is_mhealth_filename(filepath)
    mapping_file = find_location_mapping(filepath)
    if mapping_file:
        mapping = pd.read_csv(mapping_file)
    else:
        return None
    sid = get_sid(filepath)
    pid = get_pid(filepath)
    if 'PID' in mapping:
        mask = (mapping['PID'] == pid) & (mapping['SENSOR_ID'] == sid)
    else:
        mask = mapping['SENSOR_ID'] == sid
    loc = mapping.loc[mask, 'LOCATION']
    if loc.empty:
        return None
    else:
        return loc.values[0]
    return loc
