#include "utils.h"

/* Defines */
#define WRITE_KEY1  0x45670123
#define WRITE_KEY2  0xCDEF89AB

/* Functions */
flash_result_t Flash_Write(uint64_t * data) {
  nt64_t val;
  flash_result_t res = Flash_Ok;
  uint32_t address = FLASH_PAGE_START;

  val.u64 = *data;

  // search first free cell
  while ((address < FLASH_PAGE_END) && ((*(__IO uint32_t*)address) != 0xffffffff)) {
    address += 8;
  }

  /* Authorize the FLASH Registers access */
  FLASH->KEYR = WRITE_KEY1;
  FLASH->KEYR = WRITE_KEY2;

  /* verify Flash is unlock */
  if (READ_BIT(FLASH->CR, FLASH_CR_LOCK) != 0x00U) {
    res = Flash_Error;
  } else {

    if (address > FLASH_PAGE_END) {
      // no space, format flash page
      uint32_t tmp;

      /* Get configuration register, then clear page number */
      tmp = (FLASH->CR & ~FLASH_CR_PNB);

      /* Set page number, Page Erase bit & Start bit */
      FLASH->CR = (tmp | (FLASH_CR_STRT | (FLASH_PAGE_NMB <<  FLASH_CR_PNB_Pos) | FLASH_CR_PER));

      /* Wait for end of page erase */
      while ((FLASH->CR & FLASH_CR_STRT) != 0) { __NOP(); }

      address = FLASH_PAGE_START;
    }

    // write data from address
    /* Set PG bit */
    FLASH->CR |= FLASH_CR_PG;

    /* Program first word */
    *(uint32_t *)address = val.u32[0];

    /* Barrier to ensure programming is performed in 2 steps, in right order
      (independently of compiler optimization behavior) */
    __ISB();

    /* Program second word */
    *(uint32_t *)(address + 4U) = val.u32[1];

    /* Set the LOCK Bit to lock the FLASH Registers access */
    SET_BIT(FLASH->CR, FLASH_CR_LOCK);

  }

  return res;
}

/**
 * @brief Read fom flash latest actual data.
 * @param data 
 * @return flash_result_t 
 */
flash_result_t Flash_Read(uint64_t * data) {
  nt64_t val;
  flash_result_t res = Flash_Ok;
  uint32_t address = FLASH_PAGE_START;

  while ((address < FLASH_PAGE_END) && ((*(__IO uint32_t*)address) != 0xffffffff)) {
    val.u32[0] = (*(__IO uint32_t*)address);
    address += 4;
    val.u32[1] = (*(__IO uint32_t*)address);
    address += 4;
  }

  *data = val.u64;

  if (address == FLASH_PAGE_START) {
    res = Flash_PG_Clear;
  } else if (address > FLASH_PAGE_END) {
    res = Flash_PG_End;
  }

  return res;
}
