/****************************************************************************
 * This file is part of the project AqHome.
 * AqHome (c) by 2023 Martin Preuss, all rights reserved.
 *
 * The license for this file can be found in the file COPYING which you
 * should have received along with this file.
 ****************************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif



#include "./flashrecord_p.h"

#include <gwenhywfar/debug.h>
#include <gwenhywfar/misc.h>



GWEN_LIST_FUNCTIONS(AQH_FLASHRECORD, AQH_FlashRecord)


const AQH_HEXFILERECORD *_sampleAdjacentHexfileRecords(AQH_FLASHRECORD *fr, const AQH_HEXFILERECORD *hrStart);





AQH_FLASHRECORD *AQH_FlashRecord_new()
{
  AQH_FLASHRECORD *fr;

  GWEN_NEW_OBJECT(AQH_FLASHRECORD, fr);
  GWEN_LIST_INIT(AQH_FLASHRECORD, fr);
  return fr;
}



void AQH_FlashRecord_free(AQH_FLASHRECORD *fr)
{
  if (fr) {
    GWEN_LIST_FINI(AQH_FLASHRECORD, fr);
    if (fr->dataPointer)
      free(fr->dataPointer);
    fr->dataPointer=NULL;
    GWEN_FREE_OBJECT(fr);
  }
}



uint32_t AQH_FlashRecord_GetAddress(const AQH_FLASHRECORD *fr)
{
  if (fr)
    return fr->address;
  return 0;
}



void AQH_FlashRecord_SetAddress(AQH_FLASHRECORD *fr, uint32_t a)
{
  if (fr)
    fr->address=a;
}



uint32_t AQH_FlashRecord_GetDataLength(const AQH_FLASHRECORD *fr)
{
  if (fr)
    return fr->dataLength;
  return 0;
}



uint8_t *AQH_FlashRecord_GetDataPointer(const AQH_FLASHRECORD *fr)
{
  if (fr)
    return fr->dataPointer;
  return NULL;
}



void AQH_FlashRecord_DirectlySetData(AQH_FLASHRECORD *fr, uint32_t dataLength, uint8_t *dataPtr)
{
  if (fr->dataPointer)
    free(fr->dataPointer);
  fr->dataPointer=dataPtr;
  fr->dataLength=dataLength;
}



void AQH_FlashRecord_CopyAndSetData(AQH_FLASHRECORD *fr, uint32_t dataLength, const uint8_t *dataPtr)
{
  if (fr) {
    if (fr->dataPointer)
      free(fr->dataPointer);
    if (dataLength) {
      fr->dataPointer=(uint8_t*) malloc(dataLength);
      if (fr->dataPointer) {
        if (dataPtr)
          memmove(fr->dataPointer, dataPtr, dataLength);
        else
          memset(fr->dataPointer, 0, dataLength);
      }
      else {
        fr->dataPointer=NULL;
        fr->dataLength=0;
      }
    }
    else {
      fr->dataPointer=NULL;
      fr->dataLength=0;
    }
  }
}



AQH_FLASHRECORD_LIST *AQH_FlashRecord_fromHexfileRecords(const AQH_HEXFILERECORD_LIST *hexFileRecordList)
{
  AQH_FLASHRECORD_LIST *flashRecordList;
  const AQH_HEXFILERECORD *hr;
  uint32_t currentOffset=0;

  flashRecordList=AQH_FlashRecord_List_new();
  hr=AQH_HexfileRecord_List_First(hexFileRecordList);
  while(hr) {
    uint8_t t;

    t=AQH_HexfileRecord_GetRecordType(hr);
    if (t==AQH_HEXFILERECORD_TYPE_DATA) {
      AQH_FLASHRECORD *fr;

      fr=AQH_FlashRecord_new();
      AQH_FlashRecord_SetAddress(fr, currentOffset+AQH_HexfileRecord_GetAddress(hr));
      hr=_sampleAdjacentHexfileRecords(fr, hr);
      AQH_FlashRecord_List_Add(fr, flashRecordList);
    }
    else {
      if (t==AQH_HEXFILERECORD_TYPE_EXTSEG) {
        const uint8_t *ptr;

        if (AQH_HexfileRecord_GetByteCount(hr)<2) {
          DBG_ERROR(AQH_LOGDOMAIN, "Invalid EXTSEG record (02) in file (too few data bytes)");
          AQH_FlashRecord_List_free(flashRecordList);
          return NULL;
        }
        ptr=AQH_HexfileRecord_GetDataPointer(hr);
        currentOffset=((ptr[1]<<8)+ptr[0])<<4;
      }
      else if (t==AQH_HEXFILERECORD_TYPE_EXTLINEARADDR) {
        const uint8_t *ptr;

        if (AQH_HexfileRecord_GetByteCount(hr)<2) {
          DBG_ERROR(AQH_LOGDOMAIN, "Invalid EXTLINEARADDR record (04) in file (too few data bytes)");
          AQH_FlashRecord_List_free(flashRecordList);
          return NULL;
        }
        ptr=AQH_HexfileRecord_GetDataPointer(hr);
        currentOffset=((ptr[1]<<8)+ptr[0])<<16;
      }
      else if (t==AQH_HEXFILERECORD_TYPE_EOF) {
        break;
      }

      hr=AQH_HexfileRecord_List_Next(hr);
    }
  } /* while */

  if (AQH_FlashRecord_List_GetCount(flashRecordList)<1) {
    AQH_FlashRecord_List_free(flashRecordList);
    return NULL;
  }
  return flashRecordList;
}



const AQH_HEXFILERECORD *_sampleAdjacentHexfileRecords(AQH_FLASHRECORD *fr, const AQH_HEXFILERECORD *hrStart)
{
  const AQH_HEXFILERECORD *hrNext;
  const AQH_HEXFILERECORD *hr;
  uint32_t len=0;

  hrNext=AQH_HexfileRecord_List_FindNonAdjacentDataRecord(hrStart);
  /* sample number of contiguous bytes */
  hr=hrStart;
  while(hr && hr!=hrNext) {
    len+=AQH_HexfileRecord_GetByteCount(hr);
    hr=AQH_HexfileRecord_List_Next(hr);
  }

  if (len) {
    uint8_t *ptr;
    uint32_t offs=0;

    ptr=(uint8_t*) malloc(len);
    hr=hrStart;
    while(hr && hr!=hrNext) {
      memmove(ptr+offs, AQH_HexfileRecord_GetDataPointer(hr), AQH_HexfileRecord_GetByteCount(hr));
      offs+=AQH_HexfileRecord_GetByteCount(hr);
      hr=AQH_HexfileRecord_List_Next(hr);
    }
    AQH_FlashRecord_DirectlySetData(fr, len, ptr);
  }

  return hrNext;
}










