/*********************************************************************
 * @fn          checkDL
 *
 * @brief       Check validity of the downloaded image.
 *
 * input parameters
 *
 * None.
 *
 * output parameters
 *
 * None.
 *
 * @return      TRUE or FALSE for image valid.
 */
static uint8_t checkDL(void)
{
  bool ret;
  uint16_t crc[2];

  ret = extFlashOpen();
  if (ret)
  {
    ret = extFlashRead((size_t)APP_IMAGE_START, sizeof(crc), (uint8_t*)crc);
    
    if (ret) 
    {
      if ((crc[0] == 0xFFFF) || (crc[0] == 0x0000))
      {
        ret = false;
      }
      
      if (crc[1] == 0xFFFF || crc[1] == 0x0000)
      {
        crc[1] = crcCalcDL();
      }
      
      ret = crc[0] == crc[1];
    }
    
    extFlashClose();
  }
  
  return ret ? TRUE : FALSE;
}
/*********************************************************************
 * @fn      OADTarget_getCurrentImageHeader
 *
 * @brief   Get the header of the running image.
 *
 * @param   pValue - pointer to store running image header.
 *
 * @return  none
 */
void OADTarget_getCurrentImageHeader(img_hdr_t *pHdr)
{ 
  if (extFlashOpen())
  {
    //N.B: it is assumed that OADTarget_storeImageHeader() has been called prior
    // to this function.
    uint32_t metaDataAddr;
    ExtImageInfo_t tempHdr;
  
    if (imgInfo.imgType == EFL_OAD_IMG_TYPE_STACK || imgInfo.imgType == EFL_OAD_IMG_TYPE_NP)
    {
      metaDataAddr = EFL_IMAGE_INFO_ADDR_BLE;
    }
    else // Assume imgInfo.imgType == EFL_OAD_IMG_TYPE_APP
    {
      metaDataAddr = EFL_IMAGE_INFO_ADDR_APP;
    }
    
    extFlashRead(metaDataAddr, sizeof(ExtImageInfo_t), (uint8_t*)&tempHdr);
    extFlashClose();

    pHdr->len = tempHdr.len;
    pHdr->ver = tempHdr.ver == 0xFF ? 0x00 : tempHdr.ver; // In case metadata does not exist.
    memcpy(pHdr->uid,tempHdr.uid,sizeof(tempHdr.uid));
    pHdr->res[0] = HI_UINT16(tempHdr.addr);
    pHdr->res[1] = LO_UINT16(tempHdr.addr);
    pHdr->res[2] = tempHdr.imgType;
    pHdr->res[3] = 0xFF;   
  }
}
/*******************************************************************************
 * @fn      SensorTag_saveFactoryImage
 *
 * @brief   Save the current image to external flash as a factory image
 *
 * @return  none
 */
static bool SensorTag_saveFactoryImage(void)
{
  bool success;

  success = extFlashOpen();

  if (success)
  {
    uint32_t address;

    // Erase external flash
    for (address= 0; address<EFL_FLASH_SIZE; address+=EFL_PAGE_SIZE)
    {
      extFlashErase(address,EFL_PAGE_SIZE);
    }

    // Install factory image
    for (address=0; address<EFL_SIZE_RECOVERY && success; address+=EFL_PAGE_SIZE)
    {
      success = extFlashErase(EFL_ADDR_RECOVERY+address, EFL_PAGE_SIZE);
      if (success)
      {
        size_t offset;
        static uint8_t buf[256]; // RAM storage needed due to SPI/DMA limitation

        for (offset=0; offset<EFL_PAGE_SIZE; offset+=sizeof(buf))
        {
          const uint8_t *pIntFlash;

          // Copy from internal to external flash
          pIntFlash = (const uint8_t*)address + offset;
          memcpy(buf,pIntFlash,sizeof(buf));
          success = extFlashWrite(EFL_ADDR_RECOVERY+address+offset,
                                  sizeof(buf), buf);

          // Verify first few bytes
          if (success)
          {
            extFlashRead(EFL_ADDR_RECOVERY+address+offset, sizeof(buf), buf);
            success = buf[2] == pIntFlash[2] && buf[3] == pIntFlash[3];
          }
        }
      }
    }

    extFlashClose();
  }

  return success;
}
/*********************************************************************
 * @fn      OADTarget_imgIdentifyWrite
 *
 * @brief   Process the Image Identify Write.  Determined if the image
 *          header identified here should or should not be downloaded by
 *          this application.
 *
 * @param   connHandle - connection message was received on
 * @param   pValue - pointer to image header data
 *
 * @return  status
 */
bStatus_t OADTarget_imgIdentifyWrite(uint16_t connHandle, uint8_t *pValue)
{
  static img_hdr_t rxHdr;
  static img_hdr_t ImgHdr;
  
  oadBlkNum = 0;

  rxHdr.ver = BUILD_UINT16(pValue[0], pValue[1]);
  rxHdr.len = BUILD_UINT16(pValue[2], pValue[3]);
  
  (void)memcpy(rxHdr.uid, pValue+4, sizeof(rxHdr.uid));

  // Read out running image's header.
  uint8_t *flashAddr = (uint8_t *)(APP_IMAGE_START + OAD_IMG_HDR_OSET);
  memcpy(&ImgHdr,flashAddr,sizeof(img_hdr_t));
  
  // Calculate block total of the new image.
  // total Blocks = (length in HAL Flash words) / ((16 Byte Block)/(4 Byte Word))
  oadBlkTot = rxHdr.len / (OAD_BLOCK_SIZE / HAL_FLASH_WORD_SIZE);

  /* Requirements to begin OAD:
   * 1) LSB of image version cannot be the same, this would imply a code overlap
   *    between currently running image and new image, which is illegal.
   * 2) Total blocks of new image must not exceed maximum blocks supported, else
   *    the new image cannot fit.
   * 3) New image must not be empty header.
   * 4) Optional: Add customer criteria for initiating OAD here.
   */
  if ( (oadBlkTot <= OAD_BLOCK_MAX) && (oadBlkTot != 0))
  {
    oadBlkNum = 0;
    
    // Image accepted, request image.
    OADTarget_getNextBlockReq(connHandle, 0);
  }
  else
  {
    // Image rejected, identify current image to OAD manager.
    OADTarget_rejectImage(connHandle, &ImgHdr);
  }

  // Ready to go: open flash driver
  flashOk = extFlashOpen();
  
  return (flashOk ? SUCCESS : FAILURE);
}
/*******************************************************************************
 * @fn      SensorTag_hasFactoryImage
 *
 * @brief   Determine if the SensorTag has a pre-programmed factory image
 *          in external flash. Criteria for deciding if a factory image is
 *          present is that the reset vector stored in external flash is valid.
 *
 * @return  none
 */
static bool SensorTag_hasFactoryImage(void)
{
  bool valid;

  valid = extFlashOpen();

  if (valid)
  {
    uint16_t buffer[2];

    // 1. Check reset vector
    valid = extFlashRead(EFL_ADDR_RECOVERY,sizeof(buffer),(uint8_t*)buffer);
    if (valid)
    {
      valid = (buffer[0] != 0xFFFF && buffer[1] != 0xFFFF) &&
        (buffer[0] != 0x0000 && buffer[1] != 0x0000);
    }

    extFlashClose();
  }

  return valid;
}
/*********************************************************************
 * @fn      OADTarget_open
 *
 * @brief   Open an OAD target for download. 
 *
 * @param   none
 *
 * @return  TRUE if OAD target successfully opened
 */
uint8_t OADTarget_open(void)
{
  isOpen = extFlashOpen();
  
  return isOpen;
}