/*
 * multi-splitflap.c
 *
 *  Created on: 07.10.2022
 *      Author: jonas
 */

#include <stdio.h>
#include <stdbool.h>
#include "multi-splitflap.h"
#include "McuRTOS.h"
#include "McuLog.h"
#include "splitflap.h"

/* vars */
static dict_t **flapDict;
static uint8_t addedFlaps = 0;
static const uint8_t BYTES_PER_KEY = 3;
static char* flapKeys[NUM_FLAPS];  // reserve memory

/* function declarations */
void initFlapKeys(uint8_t numberOfFlaps);

/**********************/
/* INIT / DEINIT      */
/**********************/
void MultiSplitFlap_Init(){
	addedFlaps = 0;
	flapDict = dictAlloc();
	((dict_t*)flapDict)->key=NULL;
	((dict_t*)flapDict)->value=NULL;
	((dict_t*)flapDict)->next=NULL;
	initFlapKeys(NUM_FLAPS);
}

void MultiSplitFlap_Deinit(void){
	dictDealoc(flapDict);
	addedFlaps = 0;
}

/**********************/
/* PUBLIC FUNCTIONS   */
/**********************/
void MultiSplitFlap_AddFlap(SF_Handle_t splitflap){
	McuLog_info("Adding split flap to multi splitflap combination...");
	addItem(flapDict, flapKeys[addedFlaps], (SF_Handle_t*)splitflap);
	McuLog_info("Added flap nr. %i/%i.", addedFlaps+1, NUM_FLAPS);
	addedFlaps++;
}

bool MultiSplitFlap_MoveAllToZeroPosition(void){
	McuLog_info("MultiSplitFlap_MoveAllToZeroPosition: Initializing split flap motors.");

	// start all inits
	for (uint8_t num = 0; num < NUM_FLAPS; ++num) {
			// get handle
			SF_Handle_t sfHandle = (SF_Handle_t)(getItem(*flapDict, flapKeys[num]));
			// start init
			SF_MoveMotorToZeroPositionAsync(sfHandle);
	}

	// timeout of 35sec
	bool initSuccess = false;
	for (int i = 0; i < 700; ++i) {
		initSuccess = true;
		vTaskDelay(pdMS_TO_TICKS(50));
		// check each splitflap if it is finished with init
		for (uint8_t num = 0; num < NUM_FLAPS; ++num) {
			// get handle
			SF_Handle_t sfHandle = (SF_Handle_t)(getItem(*flapDict, flapKeys[num]));
			// check if init done
			if(SF_IS_RDY_TO_MOVE(sfHandle) == false){
				initSuccess = false;
				break;   // end loop when one is not yet initialized
			}
		}
		if(initSuccess == true){
			break;  // end loop when all split flaps are initialized
		}
	}

	McuLog_info("MultiSplitFlap_MoveAllToZeroPosition: Init of splitflaps done. success=%s\n\n", initSuccess ? "true" : "false");
	return initSuccess;
}

bool MultiSplitFlap_Display(char sentence[]){
	// Start movements
	for (uint8_t num = 0; num < NUM_FLAPS; ++num) {
		// get handle
		SF_Handle_t sfHandle = (SF_Handle_t)(getItem(*flapDict, flapKeys[num]));
		char letter = sentence[num];
		SF_MoveToFlapAsync(sfHandle, letter);
		McuLog_info("Multi splitflap: Commanded Flap nr. %i to letter '%c'.", num+1, letter);
		vTaskDelay(pdMS_TO_TICKS(100)); // delay loop
	}

	// wait until finished
	bool done = false;
	while(done == false){
		vTaskDelay(pdMS_TO_TICKS(100)); // delay loop

		done = true;  // default is successful
		// go through all splitflaps
		for (uint8_t num = 0; num < NUM_FLAPS; ++num) {
			SF_Handle_t sfHandle = (SF_Handle_t)(getItem(*flapDict, flapKeys[num]));
			if(SF_GetCurrentState(sfHandle) == SF_STATE_MOVING){
				done = false;
			}
		}
	}

	return done;
}

bool MultiSplitFlap_SetHardwareIdentifier(SetupIdentifier_t id, HardwareIdentifier_t hwId){
	// number too high (not that many split flaps can be added to the combination)
	if(id >= NUM_FLAPS){
		McuLog_error("MultiSplitFlap_SetHardwareIdentifier failed because splitflap with setup id <%i> cannot exist.", id);
		return false;
	}
	// number too high (not that many split flaps were added added to the combination)
	if(id >= MultiSplitFlap_GetAmountOfAddedSplitFlaps()){
		McuLog_error("MultiSplitFlap_SetHardwareIdentifier failed because splitflap with setup id <%i> was not yet added.", id);
		return false;
	}
	// number negative
	if(id < 0){
		McuLog_error("MultiSplitFlap_SetHardwareIdentifier failed because given setup id <%i> is negative.", id);
		return false;
	}

	// get handle
	SF_Handle_t sfHandle = (SF_Handle_t)(getItem(*flapDict, flapKeys[id]));
	// set hardware identifier
	SF_SetHardwareIdentifier(sfHandle, hwId);
	// log
	McuLog_info("MultiSplitFlap_SetHardwareIdentifier successful. SetupId=%i HardwareId=%i.", id, hwId);

	return true;
}

uint8_t MultiSplitFlap_GetAmountOfAddedSplitFlaps(void){
	return addedFlaps;
}

/**********************/
/* INTERNAL FUNCTIONS */
/**********************/
void initFlapKeys(uint8_t numberOfFlaps){
	for (uint8_t i = 0; i < numberOfFlaps; ++i) {
		/* get a memory place for the key */
#if SPLITFLAP_CONFIG_USE_FREERTOS_HEAP
		flapKeys[i] = pvPortMalloc(sizeof(BYTES_PER_KEY));
#else
		if((flapKeys[i] = malloc(BYTES_PER_KEY)) == NULL){
			McuLog_error("Reserving memory for flap num. %i failed!:", i);
		}
		sprintf(flapKeys[i], "%i", i);
#endif
	}
}
