#!/usr/bin/env python
# -*- coding: utf-8 -*-


'''
    ##################################################
    ## GAIT EVENTS DETECTION                        ##
    ##################################################
    
    Determine gait events according to Zeni et al. (2008).
    Write them in gaitevents.txt (append results if file already exists).

    t_HeelStrike = max(XHeel - Xsacrum)
    t_ToeOff = min(XToe - XSacrum)

    Reference:
    “Two simple methods for determining gait events during treadmill and 
    overground walking using kinematic data.” 
    Gait & posture vol. 27,4 (2008): 710-4. doi:10.1016/j.gaitpost.2007.07.007

    Usage: 
        Replace constants with the appropriate marker names.
        If direction is negative, you need to include an equal sign in the argument, 
        eg -d=-Z or --gait_direction=-Z
        
        from Pose2Sim.Utilities import trc_gaitevents; trc_gaitevents.trc_gaitevents_func(r'<input_trc_file>', '<gait_direction>')
        OR python -m trc_gaitevents -i "<input_trc_file>"
        OR python -m trc_gaitevents -i "<input_trc_file>" --gait_direction=-Z
'''


## CONSTANTS
R_SACRUM_MARKER = 'RHip'
R_HEEL_MARKER = 'RHeel'
R_TOE_MARKER = 'RBigToe'
L_SACRUM_MARKER = 'LHip'
L_HEEL_MARKER = 'LHeel'
L_TOE_MARKER = 'LBigToe'


## INIT
import os
import argparse
import pandas as pd
import numpy as np
from scipy import signal


## AUTHORSHIP INFORMATION
__author__ = "David Pagnon"
__copyright__ = "Copyright 2021, Pose2Sim"
__credits__ = ["David Pagnon"]
__license__ = "BSD 3-Clause License"
__version__ = "0.1"
__maintainer__ = "David Pagnon"
__email__ = "contact@david-pagnon.com"
__status__ = "Development"


## FUNCTIONS
def df_from_trc(trc_path):
    '''
    Retrieve header and data from trc path.
    '''

    # DataRate	CameraRate	NumFrames	NumMarkers	Units	OrigDataRate	OrigDataStartFrame	OrigNumFrames
    df_header = pd.read_csv(trc_path, sep="\t", skiprows=1, header=None, nrows=2, encoding="ISO-8859-1")
    header = dict(zip(df_header.iloc[0].tolist(), df_header.iloc[1].tolist()))
    
    # Label1_X  Label1_Y    Label1_Z    Label2_X    Label2_Y
    df_lab = pd.read_csv(trc_path, sep="\t", skiprows=3, nrows=1)
    labels = df_lab.columns.tolist()[2:-1:3]
    labels_XYZ = np.array([[labels[i]+'_X', labels[i]+'_Y', labels[i]+'_Z'] for i in range(len(labels))], dtype='object').flatten()
    labels_FTXYZ = np.concatenate((['Frame#','Time'], labels_XYZ))
    
    data = pd.read_csv(trc_path, sep="\t", skiprows=5, index_col=False, header=None, names=labels_FTXYZ)
    
    return header, data


def gait_events(trc_path, gait_direction):
    '''
    Determine gait events according to Zeni et al. (2008).
    t_HellStrike = max(XHeel - Xsacrum)
    t_ToeOff = min(XToe - XSacrum)
    '''

    # Read trc
    header, data = df_from_trc(trc_path)

    # In case of a sign in direction (eg -Z)
    sign = ''
    if any(x in gait_direction for x in ['-', '+']):
        sign = gait_direction[0]
        gait_direction = gait_direction[-1]
        
    # Retrieve data of interest
    XRSacrum = data['_'.join((R_SACRUM_MARKER, gait_direction))]
    XRHeel = data['_'.join((R_HEEL_MARKER, gait_direction))]
    XRToe = data['_'.join((R_TOE_MARKER, gait_direction))]
    XLSacrum = data['_'.join((L_SACRUM_MARKER, gait_direction))]
    XLHeel = data['_'.join((L_HEEL_MARKER, gait_direction))]
    XLToe = data['_'.join((L_TOE_MARKER, gait_direction))]

    # Prominence of the peaks
    unit = header['Units']
    peak_prominence = .1 if unit=='m' else 1 if unit=='dm' else 10 if unit=='cm' else 100 if unit=='mm' else np.inf

    # Right and left heel strikes
    frame_RHS = signal.find_peaks(eval(sign+'(XRHeel-XRSacrum)'),prominence=peak_prominence)[0]
    t_RHS = data.loc[frame_RHS, 'Time'].tolist()

    frame_LHS = signal.find_peaks(eval(sign+'(XLHeel-XLSacrum)'),prominence=peak_prominence)[0]
    t_LHS = data.loc[frame_LHS, 'Time'].tolist()

    # Right and left toe offs
    frame_RTO = signal.find_peaks(eval(sign+'-(XRToe-XRSacrum)'),prominence=peak_prominence)[0]
    t_RTO = data.loc[frame_RTO, 'Time'].tolist()

    frame_LTO = signal.find_peaks(eval(sign+'-(XLToe-XLSacrum)'),prominence=peak_prominence)[0]
    t_LTO = data.loc[frame_LTO, 'Time'].tolist()

    return t_RHS, t_LHS, t_RTO, t_LTO


def trc_gaitevents_func(*args):
    '''
    Determine gait events according to Zeni et al. (2008).
    Write them in gaitevents.txt (append results if file already exists).

    t_HeelStrike = max(XHeel - Xsacrum)
    t_ToeOff = min(XToe - XSacrum)

    Reference:
    “Two simple methods for determining gait events during treadmill and 
    overground walking using kinematic data.” 
    Gait & posture vol. 27,4 (2008): 710-4. doi:10.1016/j.gaitpost.2007.07.007
    
    Usage: 
        Replace constants with the appropriate marker names in trc_gaitevents.py.
        If direction is negative, you need to include an equal sign in the argument, 
        eg -d=-Z or --gait_direction=-Z
        
        import trc_gaitevents; trc_gaitevents.trc_gaitevents_func(r'<input_trc_file>', '<gait_direction>')
        OR trc_gaitevents -i "<input_trc_file>" --gait_direction Z
        OR trc_gaitevents -i "<input_trc_file>" --gait_direction=-Z
    '''

    try:
        trc_path = args[0].get('input_file') # invoked with argparse
        gait_direction = args[0]['gait_direction']
    except:
        trc_path = args[0] # invoked as a function
        try:
            gait_direction = args[1]
        except:
            gait_direction = 'Z'

    trc_dir = os.path.dirname(trc_path)
    trc_name = os.path.basename(trc_path)

    t_RHS, t_LHS, t_RTO, t_LTO = gait_events(trc_path, gait_direction)

    with open(os.path.join(trc_dir, 'gaitevents.txt'), 'a') as gaitevents:
        L = trc_name + '\n'
        L += 'Right Heel strikes: ' + str(t_RHS) + '\n'
        L += 'Left Heel strikes: ' + str(t_LHS) + '\n'
        L += 'Right Toe off: ' + str(t_RTO) + '\n'
        L += 'Left Toe off: ' + str(t_LTO) + '\n\n'

        gaitevents.write(L)


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('-i', '--input_file', required = True, help='trc input file')
    parser.add_argument('-d', '--gait_direction', default = 'Z', required = False, help='direction of the gait. If negative, you need to include an equal sign in the argument, eg -d=-Z')
    args = vars(parser.parse_args())
    
    trc_gaitevents_func(args)
    