#include <stdio.h>
#include <string.h>
#include "eeprom.h"
#include "pwm.h"
extern UART_HandleTypeDef huart1;
/*
 * Record format (4 bytes):
 *  [0] VirtAddress (uint16_t)
 *  [2] Data        (uint16_t)
 *
 * Page layout:
 *  [0]  PageStatus (uint16_t)
 *  [2..] Records...
 */

extern uint16_t m1pwmap;
extern uint16_t m1pwmch;
extern uint16_t m2pwmap;
extern uint16_t m2pwmch;
extern uint16_t m3pwmap;
extern uint16_t m3pwmch;
extern uint16_t m4pwmap;
extern uint16_t m4pwmch;
extern uint16_t m1TimeoutMan;
extern uint16_t m2TimeoutMan;
extern uint16_t m3TimeoutMan;
extern uint16_t m4TimeoutMan;
extern uint16_t twap;
extern uint16_t tramp;
extern uint16_t t1ch;
extern uint16_t t2ch;
extern uint16_t t3ch;
extern uint16_t t4ch;
extern uint16_t twch;
extern uint16_t tanem;
extern uint16_t thanem;
extern uint16_t trampman;
extern uint16_t m1pwmMan;
extern uint16_t m2pwmMan;
extern uint16_t m3pwmMan;
extern uint16_t m4pwmMan;
extern uint16_t mrampstart[];


extern uint16_t apM1start;
extern uint16_t apM1stop;
extern uint16_t apM2start;
extern uint16_t apM2stop;
extern uint16_t apM3start;
extern uint16_t apM3stop;
extern uint16_t apM4start;
extern uint16_t apM4stop;
extern uint16_t chM1start;
extern uint16_t chM1stop;
extern uint16_t chM2start;
extern uint16_t chM2stop;
extern uint16_t chM3start;
extern uint16_t chM3stop;
extern uint16_t chM4start;
extern uint16_t chM4stop;

extern uint16_t flagPar;

const uint16_t deftab[EE_NUM_VIRTUAL_ADDR]={
	40,	//m1pwmap
	60, //m1pwmch
	70, //m2pwmap
	70, //m2pwmch
	35, //m3pwmap
	40, //m3pwmch
	40, //m4pwmap
	40,	//m4pwmch
	20, //m1rampstart
	20, //m2rampstart
	20, //m3rampstart
	20, //m4rampstart
	230,//m1timeoutman
	100,//m2timeoutman
	130,//m3timeoutman
	300,//m4timeoutman
	1,  //twap
	27,  //t1ch
	10,  //t2ch
	30,  //t3ch
	1,  //t4ch
	1,  //twch
	0,
	0,
	0x150,//thanem
	20, //tanem
	60,  //m1pwmMan
	70,  //m2pwmMan
	40,	 //m3pwmMan
	40,	 //m4pwmMan
	50,  //trampman
	10,  //tramp
	310,   //apM1start;
	450,   //apM1stop;
	30,   //apM2start;
	310,  //apM2stop;
	340,   //apM3start;
	430,   //apM3stop;
	30,   //apM4start;
	430,   //apM4stop;
	10,   //chM1start;
	320,   //chM1stop;
	140,   //chM2start;
	440,   //chM2stop;
	20,   //chM3start;
	280,   //chM3stop;
	40,   //chM4start;
	440,   //chM4stop;
	1,
	27,
	10,
	30,
	1,
	1,
	0,
	0,
	0,
	100,
	60,
	70,
	40,
	40,
	50,
	VERSIONE,//flagpar caricamento parametri
};

typedef struct
{
    uint16_t VirtAddress;
    uint16_t Data;
} EE_Record_t;

/* Active page base address (runtime selected in EE_Init) */
static uint32_t EE_ActivePageBase = EE_PAGE0_BASE;

/* 32 sequential virtual addresses */
const uint16_t EE_VirtAddrs[EE_NUM_VIRTUAL_ADDR] =
{
	    0x0001, 0x0002, 0x0003, 0x0004,
	    0x0005, 0x0006, 0x0007, 0x0008,
	    0x0009, 0x000A, 0x000B, 0x000C,
	    0x000D, 0x000E, 0x000F, 0x0010,
	    0x0011, 0x0012, 0x0013, 0x0014,
	    0x0015, 0x0016, 0x0017, 0x0018,
	    0x0019, 0x001A, 0x001B, 0x001C,
	    0x001D, 0x001E, 0x001F, 0x0020,

	    0x0021, 0x0022, 0x0023, 0x0024,
	    0x0025, 0x0026, 0x0027, 0x0028,
	    0x0029, 0x002A, 0x002B, 0x002C,
	    0x002D, 0x002E, 0x002F, 0x0030,
	    0x0031, 0x0032, 0x0033, 0x0034,
	    0x0035, 0x0036, 0x0037, 0x0038,
	    0x0039, 0x003A, 0x003B, 0x003C,
	    0x003D, 0x003E, 0x003F, 0x0040
};
/* ========================================================================= */

/* --- Internal helpers ---------------------------------------------------- */

static uint16_t EE_GetPageStatus(uint32_t pageBase)
{
    return *(__IO uint16_t *)pageBase;
}

static HAL_StatusTypeDef EE_FlashProgramHalfWord(uint32_t Address, uint16_t Data)
{
    HAL_StatusTypeDef status;

    HAL_FLASH_Unlock();
    status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, Address, Data);
    HAL_FLASH_Lock();

    return status;
}

static HAL_StatusTypeDef EE_FlashErasePage(uint32_t PageAddress)
{
    HAL_StatusTypeDef status;
    FLASH_EraseInitTypeDef EraseInit;
    uint32_t PageError = 0;

    HAL_FLASH_Unlock();

    EraseInit.TypeErase   = FLASH_TYPEERASE_PAGES;
    EraseInit.PageAddress = PageAddress;
    EraseInit.NbPages     = 1;

    status = HAL_FLASHEx_Erase(&EraseInit, &PageError);

    HAL_FLASH_Lock();
    return status;
}

/* Find first free record address in given page (returns 0 if full) */
static uint32_t EE_FindFreeAddress(uint32_t pageBase)
{
    uint32_t addr = pageBase + 2U;                /* Skip status word */
    uint32_t pageEnd = pageBase + EE_PAGE_SIZE;

    while (addr < (pageEnd - sizeof(EE_Record_t) + 1U))
    {
        uint16_t vaddr = *(__IO uint16_t *)addr;

        if (vaddr == 0xFFFFU)
        {
            /* Empty slot */
            return addr;
        }
        addr += sizeof(EE_Record_t);
    }

    return 0U; /* No space */
}

/* Find latest value of VirtAddress in a specific page (internal, uses EE_Status) */
/* Find latest value of VirtAddress in a specific page (scan forward) */
static EE_Status EE_FindInPage(uint32_t pageBase, uint16_t VirtAddress, uint16_t *Data)
{
    uint32_t addr     = pageBase + 2U;            // skip status halfword
    uint32_t pageEnd  = pageBase + EE_PAGE_SIZE;
    EE_Status result  = EE_NOT_FOUND;
    uint16_t lastVal  = 0;

    if (Data == NULL)
        return EE_ERROR;

    while (addr <= (pageEnd - sizeof(EE_Record_t)))
    {
        uint16_t vaddr = *(__IO uint16_t *)addr;

        if (vaddr == 0xFFFFU)
        {
            // First empty slot => no more records in this page
            break;
        }

        uint16_t value = *(__IO uint16_t *)(addr + 2U);

        if (vaddr == VirtAddress)
        {
            lastVal = value;    // keep most recent
            result  = EE_OK;
        }

        addr += sizeof(EE_Record_t);  // move 4 bytes forward
    }

    if (result == EE_OK)
        *Data = lastVal;

    return result;
}



/* Format both pages: erase and set PAGE0 as VALID */
static EE_Status EE_Format(void)
{
    if (EE_FlashErasePage(EE_PAGE0_BASE) != HAL_OK)
        return EE_STATUS_ERROR;

    if (EE_FlashErasePage(EE_PAGE1_BASE) != HAL_OK)
        return EE_STATUS_ERROR;

    if (EE_FlashProgramHalfWord(EE_PAGE0_BASE, EE_PAGE_STATUS_VALID) != HAL_OK)
        return EE_STATUS_ERROR;

    /* PAGE1 will remain erased (status = 0xFFFF) */
    EE_ActivePageBase = EE_PAGE0_BASE;

    return EE_STATUS_OK;
}

/* Get the base of the other page */
static uint32_t EE_GetOtherPageBase(uint32_t pageBase)
{
    return (pageBase == EE_PAGE0_BASE) ? EE_PAGE1_BASE : EE_PAGE0_BASE;
}

/* Page transfer (garbage collection + new write) */
static EE_Status EE_PageTransfer(uint16_t VirtAddress, uint16_t Data)
{
    uint32_t oldBase = EE_ActivePageBase;
    uint32_t newBase = EE_GetOtherPageBase(oldBase);
    uint32_t addr;
    uint16_t value;
    EE_Status st;

    /* Erase new page */
    if (EE_FlashErasePage(newBase) != HAL_OK)
        return EE_STATUS_ERROR;

    /* Mark new page as RECEIVE */
    if (EE_FlashProgramHalfWord(newBase, EE_PAGE_STATUS_RECEIVE) != HAL_OK)
        return EE_STATUS_ERROR;

    /* Start writing records just after status */
    addr = newBase + 2U;

    /* For each known virtual variable */
    for (uint16_t i = 0; i < EE_NUM_VIRTUAL_ADDR; i++)
    {
        uint16_t vaddr = EE_VirtAddrs[i];

        if (vaddr == VirtAddress)
        {
            /* Use the new data passed into PageTransfer */
            value = Data;
        }
        else
        {
            /* Read latest value from old active page */
            st = EE_FindInPage(oldBase, vaddr, &value);
            if (st != EE_STATUS_OK)
            {
                /* Variable never written -> skip */
                continue;
            }
        }

        /* Write record to new page */
        if (EE_FlashProgramHalfWord(addr, vaddr) != HAL_OK)
            return EE_STATUS_ERROR;
        if (EE_FlashProgramHalfWord(addr + 2U, value) != HAL_OK)
            return EE_STATUS_ERROR;

        addr += sizeof(EE_Record_t);
        if (addr >= (newBase + EE_PAGE_SIZE))
            return EE_STATUS_NO_SPACE;
    }

    /* Erase old page */
    if (EE_FlashErasePage(oldBase) != HAL_OK)
        return EE_STATUS_ERROR;

    /* Mark new page as VALID */
    if (EE_FlashProgramHalfWord(newBase, EE_PAGE_STATUS_VALID) != HAL_OK)
        return EE_STATUS_ERROR;

    /* Update active page */
    EE_ActivePageBase = newBase;

    return EE_STATUS_OK;
}

/* --- Public API ----------------------------------------------------------- */

/*
 * Initialize the EEPROM emulation.
 * - Checks page statuses and chooses the active page.
 * - If inconsistent or blank, formats pages.
 * PUBLIC RETURN TYPE: uint16_t (EE_OK / EE_ERROR / ...)
 */
uint16_t EE_Init(void)
{
    uint16_t status0 = EE_GetPageStatus(EE_PAGE0_BASE);
    uint16_t status1 = EE_GetPageStatus(EE_PAGE1_BASE);

    if ((status0 == EE_PAGE_STATUS_ERASED) && (status1 == EE_PAGE_STATUS_ERASED))
    {
        /* Fresh device -> format */
        return (uint16_t)EE_Format();
    }
    else if ((status0 == EE_PAGE_STATUS_VALID) && (status1 == EE_PAGE_STATUS_ERASED))
    {
        EE_ActivePageBase = EE_PAGE0_BASE;
        return EE_OK;
    }
    else if ((status1 == EE_PAGE_STATUS_VALID) && (status0 == EE_PAGE_STATUS_ERASED))
    {
        EE_ActivePageBase = EE_PAGE1_BASE;
        return EE_OK;
    }
    else
    {
        /* Any weird or inconsistent state -> reformat */
        return (uint16_t)EE_Format();
    }
}

/*
 * Read a 16-bit variable by its virtual address.
 * PUBLIC RETURN: EE_OK / EE_NOT_FOUND / EE_ERROR (as uint16_t)
 */
uint16_t EE_ReadVariable(uint16_t VirtAddress, uint16_t *Data)
{
    EE_Status st;

    if (Data == NULL)
        return EE_ERROR;

    st = EE_FindInPage(EE_ActivePageBase, VirtAddress, Data);
    return (uint16_t)st;
}

/*
 * Write (append) a 16-bit variable.
 * - Writes a new record in the active page.
 * - If the page is full, triggers a page transfer (GC).
 * PUBLIC RETURN: EE_OK / EE_ERROR / EE_NO_SPACE (as uint16_t)
 */
uint16_t EE_WriteVariable(uint16_t VirtAddress, uint16_t Data)
{
    uint32_t freeAddr;
    EE_Status st;

    /* Find free space in active page */
    freeAddr = EE_FindFreeAddress(EE_ActivePageBase);
    if (freeAddr != 0U)
    {
        /* Write new record */
        if (EE_FlashProgramHalfWord(freeAddr, VirtAddress) != HAL_OK)
            return EE_ERROR;
        if (EE_FlashProgramHalfWord(freeAddr + 2U, Data) != HAL_OK)
            return EE_ERROR;

        return EE_OK;
    }

    /* No space -> page transfer */
    st = EE_PageTransfer(VirtAddress, Data);
    return (uint16_t)st;
}

void loadDefault(void){
	uint8_t i;
	char s[100];
	uint16_t st;
	for(i=0;i<EE_NUM_VIRTUAL_ADDR;i++){
	  st=EEW_Write(i,deftab[i]);
	  if (st == EE_OK)sprintf((char*)s,"\ndone %d=%d",i,deftab[i]);
	  else sprintf((char*)s,"\nee write error %d", st);
	  HAL_UART_Transmit(&huart1,(uint8_t*) s, strlen(s), HAL_MAX_DELAY);

	}
}

void loadEE(void){
	char s [200];
	EEW_Read(M1PWMAP, &m1pwmap);
	EEW_Read(M1PWMCH, &m1pwmch);
	EEW_Read(M2PWMAP, &m2pwmap);
	EEW_Read(M2PWMCH, &m2pwmch);
	EEW_Read(M3PWMAP, &m3pwmap);
	EEW_Read(M3PWMCH, &m3pwmch);
	EEW_Read(M4PWMAP, &m4pwmap);
	EEW_Read(M4PWMCH, &m4pwmch);

	EEW_Read(M1RAMPSTART, &mrampstart[M1-1]);
	EEW_Read(M2RAMPSTART, &mrampstart[M2-1]);
	EEW_Read(M3RAMPSTART, &mrampstart[M3-1]);
	EEW_Read(M4RAMPSTART, &mrampstart[M4-1]);


	EEW_Read(M1TIMEOUTMAN, &m1TimeoutMan);
	EEW_Read(M2TIMEOUTMAN, &m2TimeoutMan);
	EEW_Read(M3TIMEOUTMAN, &m3TimeoutMan);
	EEW_Read(M4TIMEOUTMAN, &m4TimeoutMan);

	EEW_Read(THANEM, &thanem);
	EEW_Read(TANEM, &tanem);
	EEW_Read(M1PWMMAN, &m1pwmMan);
	EEW_Read(M2PWMMAN, &m2pwmMan);
	EEW_Read(M3PWMMAN, &m3pwmMan);
	EEW_Read(M4PWMMAN, &m4pwmMan);
	EEW_Read(TRAMPMAN, &trampman);
	EEW_Read(TRAMP, &tramp);

	EEW_Read(APM1START,&apM1start);
	EEW_Read(APM1STOP ,&apM1stop);
	EEW_Read(APM2START,&apM2start);
	EEW_Read(APM2STOP ,&apM2stop);
	EEW_Read(APM3START,&apM3start);
	EEW_Read(APM3STOP ,&apM3stop);
	EEW_Read(APM4START,&apM4start);
	EEW_Read(APM4STOP ,&apM4stop);
	EEW_Read(CHM1START,&chM1start);
	EEW_Read(CHM1STOP ,&chM1stop);
	EEW_Read(CHM2START,&chM2start);
	EEW_Read(CHM2STOP ,&chM2stop);
	EEW_Read(CHM3START,&chM3start);
	EEW_Read(CHM3STOP ,&chM3stop);
	EEW_Read(CHM4START,&chM4start);
	EEW_Read(CHM4STOP ,&chM4stop);

	EEW_Read(FLAGPAR ,&flagPar);

	sprintf((char*)s,"LoadEE\n");
	HAL_UART_Transmit(&huart1,(uint8_t*) s, strlen(s), HAL_MAX_DELAY);
	sprintf((char*)s,"\nm1pwmap=%d m1pwmch=%d m2pwmap=%d m2pwmch=%d m3pwmap=%d m3pwmch=%d m4pwmap=%d m4pwmch=%d",m1pwmap,m1pwmch,m2pwmap,m2pwmch,m3pwmap,m3pwmch,m4pwmap, m4pwmch);
	HAL_UART_Transmit(&huart1,(uint8_t*) s, strlen(s), HAL_MAX_DELAY);
	sprintf((char*)s,"\nm1TimeoutMan=%d m2TimeoutMan=%d m3TimeoutMan=%d m4TimeoutMan=%d ",m1TimeoutMan,m2TimeoutMan,m3TimeoutMan,m4TimeoutMan);
	HAL_UART_Transmit(&huart1,(uint8_t*) s, strlen(s), HAL_MAX_DELAY);
	sprintf((char*)s,"\ntramp=%d trampman=%d",tramp,trampman);
	HAL_UART_Transmit(&huart1,(uint8_t*) s, strlen(s), HAL_MAX_DELAY);
	sprintf((char*)s,"\nm1pwmMan=%d m2pwmMan=%d m3pwmMan=%d m4pwmMan=%d",m1pwmMan,m2pwmMan,m3pwmMan,m4pwmMan);
	HAL_UART_Transmit(&huart1,(uint8_t*) s, strlen(s), HAL_MAX_DELAY);
	sprintf((char*)s,"\napM1start=%d apM1stop=%d chM1start=%d chM1stop=%d",apM1start,apM1stop,chM1start,chM1stop);
	HAL_UART_Transmit(&huart1,(uint8_t*) s, strlen(s), HAL_MAX_DELAY);
	sprintf((char*)s,"\napM2start=%d apM2stop=%d chM2start=%d chM2stop=%d",apM2start,apM2stop,chM2start,chM2stop);
	HAL_UART_Transmit(&huart1,(uint8_t*) s, strlen(s), HAL_MAX_DELAY);
	sprintf((char*)s,"\napM3start=%d apM3stop=%d chM3start=%d chM3stop=%d",apM3start,apM3stop,chM3start,chM3stop);
	HAL_UART_Transmit(&huart1,(uint8_t*) s, strlen(s), HAL_MAX_DELAY);
	sprintf((char*)s,"\napM4start=%d apM4stop=%d chM4start=%d chM4stop=%d",apM4start,apM4stop,chM4start,chM4stop);
	HAL_UART_Transmit(&huart1,(uint8_t*) s, strlen(s), HAL_MAX_DELAY);

}
