import datetime
import glob
import os
import re
import shutil
import subprocess

from anypytools import AnyMacro
from anypytools import AnyPyProcess
from anypytools.macro_commands import (MacroCommand, Load, Dump,
                                       SaveData, OperationRun)

from AnyWriter import AnyWriter
from AnybodyResults import AnybodyResults
from config.Configuration import env


class AnyPy:
    LOAD = 'load'
    INITIAL_CONDITIONS = 'initial_conditions'
    KINEMATICS = 'kinematics'
    INVERSE_DYNAMICS = 'inverse_dynamics'
    # SET_ORDER = 'set_order'
    SAVE_H5 = 'save_h5'
    LOAD_H5 = 'load_h5'
    DUMP_JOINT_ANGLES = 'dump_angles'
    DUMP_STEPS = 'dump_steps'
    DUMP_LEAP_VECTORS = 'dump_leap_vectors'
    REPLAY = 'replay'
    LOG_FILE = 'AnyPy{}.log'.format(datetime.datetime.today().strftime('%Y%m%d_%H%M%S'))

    INTERPOL_DIR = '/Model/InterpolVec'

    def __init__(self, main_filepath, template_directory):
        self.any_path, self.any_model = os.path.split(main_filepath)
        self.main_filepath = main_filepath
        self.template_directory = template_directory
        self.operations = []
        self.macrolist = []
        self.output = None

        if env.args('any_interpol_files'):
            print('Using interpolation files from "{}"'.format(os.path.normpath(self.any_path + AnyPy.INTERPOL_DIR)))

        if env.args('any_bvh_file'):
            print("Convert bvh file to anybody interpolation files")
            from resources.pymo.pymo.parsers import BVHParser as Pymo_BVHParser
            any_writer = AnyWriter(template_directory='config/anybody_templates/',
                                   output_directory=os.path.normpath(self.any_path + AnyPy.INTERPOL_DIR) + '/')
            any_writer.write(Pymo_BVHParser().parse(env.config.any_bvh_file))

        if env.args('any_files_dir'):
            self.copy_files()

        # remove frames from start and end (cut)
        if env.config.start_frame or env.config.end_frame:
            start_frame = int(env.config.start_frame) - 1 if env.config.start_frame else 0
            end_frame = int(env.config.end_frame) - 1 if 'end' not in env.config.end_frame.lower() else None
            any_writer = AnyWriter(output_directory=os.path.normpath(self.any_path + AnyPy.INTERPOL_DIR) + '/')
            any_writer.extract_frames(start_frame, end_frame)
            any_writer.extract_frame_timeseries(start_frame, end_frame)

        self.output_path = ''
        if env.args('output_file_path'):
            self.output_path = os.path.normpath(
                os.path.join(os.path.split(env.config.output_file_path)[0],
                             os.path.split(env.config.output_file_path)[1].replace(".anydata.h5", "") + '.anydata.h5'))

        self.initialize_operations()

    def initialize_operations(self):
        """build the macrolist executed by AnyPyTools"""
        operation_cmd = {AnyPy.LOAD: Load(self.main_filepath),
                         AnyPy.INITIAL_CONDITIONS: OperationRun('Main.Study.InitialConditions'),
                         AnyPy.KINEMATICS: OperationRun('Main.Study.Kinematics'),
                         AnyPy.INVERSE_DYNAMICS: OperationRun('Main.Study.InverseDynamics'),
                         # AnyPy.SET_ORDER: SetValue('Main.HumanModel.Mannequin.InterpolationFunctions.intorder',
                         #                           env.config.order),
                         AnyPy.SAVE_H5: SaveData('Main.Study', self.output_path),
                         AnyPy.DUMP_JOINT_ANGLES: Dump('Main.Study.Output.JointAngleOutputs'),
                         AnyPy.DUMP_STEPS: Dump('Main.Study.nStep'),
                         AnyPy.DUMP_LEAP_VECTORS: Dump('Main.HumanModel.Mannequin.Posture.Right')}

        if env.config.load:
            self.add_operation(AnyPy.LOAD)
        if env.config.initial_conditions:
            self.add_operation(AnyPy.INITIAL_CONDITIONS)
        if env.config.kinematic:
            self.add_operation(AnyPy.KINEMATICS)
        if env.config.inverse_dynamics:
            self.add_operation(AnyPy.INVERSE_DYNAMICS)
        if env.config.nstep:
            self.set_step()
        # if env.config.order:
        #     self.add_operation(AnyPy.SET_ORDER)
        if env.config.plot:
            # requirement for plot is run of kinematic analysis
            self.add_operation(AnyPy.LOAD)
            self.add_operation(AnyPy.KINEMATICS)
            # dump interpolated joint angles
            self.add_operation(AnyPy.DUMP_JOINT_ANGLES)
            # dump nStep
            self.add_operation(AnyPy.DUMP_STEPS)
            # dump Mannequin vectors including the joint angles from the bvh file
            self.add_operation(AnyPy.DUMP_LEAP_VECTORS)

        if self.output_path:
            # save study output to hdf5, to view and replay analysis later
            self.add_operation(AnyPy.SAVE_H5)

        for operation in self.operations:
            self.macrolist.append(operation_cmd[operation])

    def post_operations(self):
        macro_output_path = 'classoperation Main.Study.Output "Load data" --file="{}"'.format(self.output_path)

        """build the macrolist executed by AnyPyTools"""
        operation_cmd = {AnyPy.LOAD: Load(self.main_filepath),
                         AnyPy.LOAD_H5: MacroCommand(macro_output_path),
                         AnyPy.REPLAY: OperationRun("Main.Study.ReplayKinematics")}

        self.macrolist = []
        for operation in operation_cmd:
            self.macrolist.append(str(operation_cmd[operation]))

        print('Starting Anybody with the macros:\n{}'.format(self.macrolist))
        print('Executing "{}" in "{}"'.format(self.any_path, self.any_model))

        # save current working directory and change to Anybody project folder
        cwd = os.getcwd()
        os.chdir(self.any_path)

        # write macro file to be opened by AnyBody GUI
        macro_replay_path = os.path.join(self.any_path, 'replay.anymcr')
        with open(macro_replay_path, 'wb') as macro_file:
            macro_file.write("\n".join(self.macrolist).encode("UTF-8"))
            macro_file.flush()
            anybodycmd = [os.path.realpath('C:/Program Files/AnyBody Technology/AnyBody.7.1/AnyBody.exe'),
                          "-m", macro_file.name]

        # execute AnyBody GUI with the command from anybodycmd
        subprocess.Popen(anybodycmd)

        # change back to original folder
        os.chdir(cwd)

    def add_operation(self, operation):
        """add operation to a list if not already in the list (unique)"""
        if operation not in self.operations:
            self.operations.append(operation)

    def copy_files(self):
        """"copy interpolation files"""
        for file in glob.glob(self.template_directory + r'/*.any'):
            print('copying "{}" to "{}"'.format(file,
                                                os.path.normpath(self.any_path +
                                                                 AnyPy.INTERPOL_DIR + "/" + os.path.split(file)[-1])))
            shutil.copy(file, self.any_path + AnyPy.INTERPOL_DIR)

    def run(self):
        if not self.macrolist:
            print("No operation for AnyBody was selected -> will terminate now")
            return False

        # print('Starting Anybody with the operations: {}'.format(self.operations))
        print('Starting Anybody with the macros:\n{}'.format(AnyMacro(self.macrolist)))
        print('Executing "{}" in "{}"'.format(self.any_path, self.any_model))

        # save current working directory and change to Anybody project folder
        cwd = os.getcwd()
        os.chdir(self.any_path)
        app = AnyPyProcess()

        self.output = app.start_macro(macrolist=self.macrolist,
                                      logfile=AnyPy.LOG_FILE)

        # change back to original folder
        os.chdir(cwd)
        return True

    def plot(self):
        """open the plot for the joint angles"""
        print('Loading the plot ...')
        AnybodyResults(self.output).plot()

    def set_step(self):
        """replace the nstep value with the new selected value"""
        regex_step = re.compile(r'nStep\s*=.*\d+.*;')
        step_setting = 'nStep = {};'.format(env.config.nstep)
        with open(self.main_filepath) as file:
            old_file = file.read()
        new_file = re.sub(regex_step, step_setting, old_file)
        with open(self.main_filepath, 'w') as file:
            file.write(new_file)
        print('"{}" written to "{}"'.format(step_setting, self.main_filepath))

import datetime
import glob
import os
import re
import shutil
import subprocess

from anypytools import AnyMacro
from anypytools import AnyPyProcess
from anypytools.macro_commands import (MacroCommand, Load, Dump,
                                       SaveData, OperationRun)

from AnyWriter import AnyWriter
from AnybodyResults import AnybodyResults
from config.Configuration import env


class AnyPy:
    LOAD = 'load'
    INITIAL_CONDITIONS = 'initial_conditions'
    KINEMATICS = 'kinematics'
    INVERSE_DYNAMICS = 'inverse_dynamics'
    # SET_ORDER = 'set_order'
    SAVE_H5 = 'save_h5'
    LOAD_H5 = 'load_h5'
    DUMP_JOINT_ANGLES = 'dump_angles'
    DUMP_STEPS = 'dump_steps'
    DUMP_LEAP_VECTORS = 'dump_leap_vectors'
    REPLAY = 'replay'
    LOG_FILE = 'AnyPy{}.log'.format(datetime.datetime.today().strftime('%Y%m%d_%H%M%S'))

    INTERPOL_DIR = '/Model/InterpolVec'

    def __init__(self, main_filepath, template_directory):
        self.any_path, self.any_model = os.path.split(main_filepath)
        self.main_filepath = main_filepath
        self.template_directory = template_directory
        self.operations = []
        self.macrolist = []
        self.output = None

        if env.args('any_interpol_files'):
            print('Using interpolation files from "{}"'.format(os.path.normpath(self.any_path + AnyPy.INTERPOL_DIR)))

        if env.args('any_bvh_file'):
            print("Convert bvh file to anybody interpolation files")
            from resources.pymo.pymo.parsers import BVHParser as Pymo_BVHParser
            any_writer = AnyWriter(template_directory='config/anybody_templates/',
                                   output_directory=os.path.normpath(self.any_path + AnyPy.INTERPOL_DIR) + '/')
            any_writer.write(Pymo_BVHParser().parse(env.config.any_bvh_file))

        if env.args('any_files_dir'):
            self.copy_files()

        # remove frames from start and end (cut)
        if env.config.start_frame or env.config.end_frame:
            start_frame = int(env.config.start_frame) - 1 if env.config.start_frame else 0
            end_frame = int(env.config.end_frame) - 1 if 'end' not in env.config.end_frame.lower() else None
            any_writer = AnyWriter(output_directory=os.path.normpath(self.any_path + AnyPy.INTERPOL_DIR) + '/')
            any_writer.extract_frames(start_frame, end_frame)
            any_writer.extract_frame_timeseries(start_frame, end_frame)

        self.output_path = ''
        if env.args('output_file_path'):
            self.output_path = os.path.normpath(
                os.path.join(os.path.split(env.config.output_file_path)[0],
                             os.path.split(env.config.output_file_path)[1].replace(".anydata.h5", "") + '.anydata.h5'))

        self.initialize_operations()

    def initialize_operations(self):
        """build the macrolist executed by AnyPyTools"""
        operation_cmd = {AnyPy.LOAD: Load(self.main_filepath),
                         AnyPy.INITIAL_CONDITIONS: OperationRun('Main.Study.InitialConditions'),
                         AnyPy.KINEMATICS: OperationRun('Main.Study.Kinematics'),
                         AnyPy.INVERSE_DYNAMICS: OperationRun('Main.Study.InverseDynamics'),
                         # AnyPy.SET_ORDER: SetValue('Main.HumanModel.Mannequin.InterpolationFunctions.intorder',
                         #                           env.config.order),
                         AnyPy.SAVE_H5: SaveData('Main.Study', self.output_path),
                         AnyPy.DUMP_JOINT_ANGLES: Dump('Main.Study.Output.JointAngleOutputs'),
                         AnyPy.DUMP_STEPS: Dump('Main.Study.nStep'),
                         AnyPy.DUMP_LEAP_VECTORS: Dump('Main.HumanModel.Mannequin.Posture.Right')}

        if env.config.load:
            self.add_operation(AnyPy.LOAD)
        if env.config.initial_conditions:
            self.add_operation(AnyPy.INITIAL_CONDITIONS)
        if env.config.kinematic:
            self.add_operation(AnyPy.KINEMATICS)
        if env.config.inverse_dynamics:
            self.add_operation(AnyPy.INVERSE_DYNAMICS)
        if env.config.nstep:
            self.set_step()
        # if env.config.order:
        #     self.add_operation(AnyPy.SET_ORDER)
        if env.config.plot:
            # requirement for plot is run of kinematic analysis
            self.add_operation(AnyPy.LOAD)
            self.add_operation(AnyPy.KINEMATICS)
            # dump interpolated joint angles
            self.add_operation(AnyPy.DUMP_JOINT_ANGLES)
            # dump nStep
            self.add_operation(AnyPy.DUMP_STEPS)
            # dump Mannequin vectors including the joint angles from the bvh file
            self.add_operation(AnyPy.DUMP_LEAP_VECTORS)

        if self.output_path:
            # save study output to hdf5, to view and replay analysis later
            self.add_operation(AnyPy.SAVE_H5)

        for operation in self.operations:
            self.macrolist.append(operation_cmd[operation])

    def post_operations(self):
        macro_output_path = 'classoperation Main.Study.Output "Load data" --file="{}"'.format(self.output_path)

        """build the macrolist executed by AnyPyTools"""
        operation_cmd = {AnyPy.LOAD: Load(self.main_filepath),
                         AnyPy.LOAD_H5: MacroCommand(macro_output_path),
                         AnyPy.REPLAY: OperationRun("Main.Study.ReplayKinematics")}

        self.macrolist = []
        for operation in operation_cmd:
            self.macrolist.append(str(operation_cmd[operation]))

        print('Starting Anybody with the macros:\n{}'.format(self.macrolist))
        print('Executing "{}" in "{}"'.format(self.any_path, self.any_model))

        # save current working directory and change to Anybody project folder
        cwd = os.getcwd()
        os.chdir(self.any_path)

        # write macro file to be opened by AnyBody GUI
        macro_replay_path = os.path.join(self.any_path, 'replay.anymcr')
        with open(macro_replay_path, 'wb') as macro_file:
            macro_file.write("\n".join(self.macrolist).encode("UTF-8"))
            macro_file.flush()
            anybodycmd = [os.path.realpath('C:/Program Files/AnyBody Technology/AnyBody.7.1/AnyBody.exe'),
                          "-m", macro_file.name]

        # execute AnyBody GUI with the command from anybodycmd
        subprocess.Popen(anybodycmd)

        # change back to original folder
        os.chdir(cwd)

    def add_operation(self, operation):
        """add operation to a list if not already in the list (unique)"""
        if operation not in self.operations:
            self.operations.append(operation)

    def copy_files(self):
        """"copy interpolation files"""
        for file in glob.glob(self.template_directory + r'/*.any'):
            print('copying "{}" to "{}"'.format(file,
                                                os.path.normpath(self.any_path +
                                                                 AnyPy.INTERPOL_DIR + "/" + os.path.split(file)[-1])))
            shutil.copy(file, self.any_path + AnyPy.INTERPOL_DIR)

    def run(self):
        if not self.macrolist:
            print("No operation for AnyBody was selected -> will terminate now")
            return False

        # print('Starting Anybody with the operations: {}'.format(self.operations))
        print('Starting Anybody with the macros:\n{}'.format(AnyMacro(self.macrolist)))
        print('Executing "{}" in "{}"'.format(self.any_path, self.any_model))

        # save current working directory and change to Anybody project folder
        cwd = os.getcwd()
        os.chdir(self.any_path)
        app = AnyPyProcess()

        self.output = app.start_macro(macrolist=self.macrolist,
                                      logfile=AnyPy.LOG_FILE)

        # change back to original folder
        os.chdir(cwd)
        return True

    def plot(self):
        """open the plot for the joint angles"""
        print('Loading the plot ...')
        AnybodyResults(self.output).plot()

    def set_step(self):
        """replace the nstep value with the new selected value"""
        regex_step = re.compile(r'nStep\s*=.*\d+.*;')
        step_setting = 'nStep = {};'.format(env.config.nstep)
        with open(self.main_filepath) as file:
            old_file = file.read()
        new_file = re.sub(regex_step, step_setting, old_file)
        with open(self.main_filepath, 'w') as file:
            file.write(new_file)
        print('"{}" written to "{}"'.format(step_setting, self.main_filepath))

# SET_ORDER = 'set_order' # remove frames from start and end (cut) build the macrolist executed by AnyPyTools # AnyPy.SET_ORDER: SetValue('Main.HumanModel.Mannequin.InterpolationFunctions.intorder', #                           env.config.order), # if env.config.order: #     self.add_operation(AnyPy.SET_ORDER) # requirement for plot is run of kinematic analysis # dump interpolated joint angles # dump nStep # dump Mannequin vectors including the joint angles from the bvh file # save study output to hdf5, to view and replay analysis later build the macrolist executed by AnyPyTools # save current working directory and change to Anybody project folder # write macro file to be opened by AnyBody GUI # execute AnyBody GUI with the command from anybodycmd # change back to original folder add operation to a list if not already in the list (unique) "copy interpolation files # print('Starting Anybody with the operations: {}'.format(self.operations)) # save current working directory and change to Anybody project folder # change back to original folder open the plot for the joint angles replace the nstep value with the new selected value