/*
    CaPriCe Forever - Amstrad CPC 464/664/6128 emulator
    Copyright (C) 2014-2016 by Frédéric Coste

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "..\Native_CPC.h"
#include "..\..\Common\FDC.h"
#include "..\..\Common\MFM.h"


//===================
// PATCH begin
#ifdef _PATCH_ENABLE

#endif /* _PATCH_ENABLE */
// PATCH end
//===================



/*
** Make sure we can call this stuff from C++.
*/
#ifdef __cplusplus
extern "C" {
#endif

static tBool IsPowerOf128(tULong Value);

//
// Routines
//
static tVoid MemSet(tUChar* destP,
                    tULong numBytes,
                    tUChar value);
static tVoid MemMove(tUChar* destP,
                     const tUChar* sourceP,
                     tULong numBytes);


tVoid CreateDeclaredSizeSector(tTrack* trackP, tULong CurrentSector)
/***********************************************************************
 *
 *  CreateDeclaredSizeSector
 *
 ***********************************************************************/
{
tSector* CurrentSectorP = &trackP->sector[CurrentSector];
tSector* LastSectorP;
tSector* NextSectorP;
tUChar* DeclaredDataP = CurrentSectorP->declared_data;
tULong RemainingSize = CurrentSectorP->declared_size;
tULong AfterIndexLength;
tULong AfterIndexSynchroRemaining;
tULong AddedBytesLength = 0;
//tULong sector;
tULong size;
tUChar post_index_byte;
tUChar track_synchro_bitshift;
tUChar track_synchro_pulseshift;
tBool first_sector = cTrue;

  // Memory allocated ?
  if (DeclaredDataP == cNull)
    return;

  if (RemainingSize > (FDC_MAX_BYTES_PER_TRACK - CurrentSectorP->track_offset))
  {
    AfterIndexLength = RemainingSize - (FDC_MAX_BYTES_PER_TRACK - CurrentSectorP->track_offset);
  }
  else
  {
    AfterIndexLength = 0;
  }

  do
  {
    // Add sectors until end of track
    do
    {
      // First sector ?
			if ( (first_sector == cFalse)
					 && (IsPowerOf128(LastSectorP->data_size) == cTrue) )
      {
        // Add SYNC
        size = (FDC_BYTES_SYNC > RemainingSize) ? RemainingSize : FDC_BYTES_SYNC;
        MemSet(DeclaredDataP, size, FDC_BYTE_SYNC);
        DeclaredDataP += size;
        if ((RemainingSize -= size) == 0)
          break;

        // Add IDAM
        size = (FDC_BYTES_IDAM > RemainingSize) ? RemainingSize : FDC_BYTES_IDAM;
        MemMove(DeclaredDataP, fdc_idam, size);
        DeclaredDataP += size;
        if ((RemainingSize -= size) == 0)
          break;

        // Add CHRN
        size = ((FDC_BYTES_CHRN+FDC_BYTES_CRC) > RemainingSize) ? RemainingSize : (FDC_BYTES_CHRN+FDC_BYTES_CRC);
        MemMove(DeclaredDataP, CurrentSectorP->CHRN, size);
        DeclaredDataP += size;
        if ((RemainingSize -= size) == 0)
          break;

        // Add GAP2
        size = (FDC_BYTES_GAP2 > RemainingSize) ? RemainingSize : FDC_BYTES_GAP2;
        MemSet(DeclaredDataP, size, FDC_BYTE_GAP2);
        DeclaredDataP += size;
        if ((RemainingSize -= size) == 0)
          break;

        // Add SYNC
        size = (FDC_BYTES_SYNC > RemainingSize) ? RemainingSize : FDC_BYTES_SYNC;
        MemSet(DeclaredDataP, size, FDC_BYTE_SYNC);
        DeclaredDataP += size;
        if ((RemainingSize -= size) == 0)
          break;

        // Add DATAAM
        size = (FDC_BYTES_DATAAM > RemainingSize) ? RemainingSize : FDC_BYTES_DATAAM;
        if (CurrentSectorP->flags[1] & ST2_CM)
        {
          MemMove(DeclaredDataP, fdc_dataam_deleted, size);
        }
        else
        {
          MemMove(DeclaredDataP, fdc_dataam, size);
        }
        DeclaredDataP += size;
        if ((RemainingSize -= size) == 0)
          break;
      }

      // Add data
      size = (CurrentSectorP->data_size > RemainingSize) ? RemainingSize : CurrentSectorP->data_size;
      MemMove(DeclaredDataP, CurrentSectorP->data, size);
      DeclaredDataP += size;
      if ((RemainingSize -= size) == 0)
        break;

      // Sector GAP3 ?
      if (CurrentSectorP->data_gap3_length)
      {
        // Add Sector GAP3
        size = (CurrentSectorP->data_gap3_length > RemainingSize) ? RemainingSize : CurrentSectorP->data_gap3_length;
        MemMove(DeclaredDataP,
                CurrentSectorP->data + (CurrentSectorP->data_size * CurrentSectorP->weak_count),
                size);
        DeclaredDataP += size;
        if ((RemainingSize -= size) == 0)
          break;
      }
			else if (IsPowerOf128(CurrentSectorP->data_size) == cTrue)
      {
        // Add Data CRC
        size = (FDC_BYTES_CRC > RemainingSize) ? RemainingSize : FDC_BYTES_CRC;
        MemMove(DeclaredDataP, CurrentSectorP->data_crc, size);
        DeclaredDataP += size;
        if ((RemainingSize -= size) == 0)
          break;

        // Add GAP3
        size = (CurrentSectorP->info_gap3_length > RemainingSize) ? RemainingSize : CurrentSectorP->info_gap3_length;
        MemSet(DeclaredDataP, size, FDC_BYTE_GAP3);
        DeclaredDataP += size;
        if ((RemainingSize -= size) == 0)
          break;
      }
      else
      {
        NextSectorP = CurrentSectorP + 1;
        if ( (NextSectorP->data != cNull)
             && (CurrentSectorP->offset_info > 0)
             && (NextSectorP->offset_info > 0) )
        {
          // Add space between sectors
          size = NextSectorP->offset_info - CurrentSectorP->offset_info - CurrentSectorP->data_size;
          if (size > RemainingSize)
          {
            size = RemainingSize;
          }
          MemSet(DeclaredDataP, size, trackP->filler);
          DeclaredDataP += size;
          if ((RemainingSize -= size) == 0)
            break;
        }
      }

      // Next sector
      first_sector = cFalse;
			LastSectorP = CurrentSectorP;
			CurrentSectorP++;
      if (CurrentSectorP->data == cNull)
        break;
    }
    while ((CurrentSectorP->data != cNull) && (RemainingSize > 0));
    if (RemainingSize == 0)
      break;

    // Add GAP4b
    if (RemainingSize > AfterIndexLength)
    {
      AfterIndexSynchroRemaining = 0;
      size = RemainingSize - AfterIndexLength;
      MemSet(DeclaredDataP, size, FDC_BYTE_GAP4B);
      DeclaredDataP += size;
      if ((RemainingSize -= size) == 0)
        break;
    }
    else
    {
      AfterIndexSynchroRemaining = AfterIndexLength - RemainingSize;
    }

    // First sector
    CurrentSectorP = &trackP->sector[0];

    // Add GAP4a
    size = (FDC_BYTES_GAP4A > RemainingSize) ? RemainingSize : FDC_BYTES_GAP4A;
    if (AfterIndexSynchroRemaining)
    {
      if (AfterIndexSynchroRemaining > size)
      {
        AfterIndexSynchroRemaining -= size;
      }
      else
      {
        // Part copy
        tULong partsize = size - AfterIndexSynchroRemaining;
        AddedBytesLength = RemainingSize;
        AfterIndexSynchroRemaining = 0;
        MemSet(DeclaredDataP + AfterIndexSynchroRemaining, partsize, FDC_BYTE_GAP4A);
        DeclaredDataP += partsize;
        if ((RemainingSize -= partsize) == 0)
          break;
      }
    }
    else
    {
      AfterIndexLength = RemainingSize;
      MemSet(DeclaredDataP, size, FDC_BYTE_GAP4A);
      DeclaredDataP += size;
      if ((RemainingSize -= size) == 0)
        break;
    }

    // Add SYNC
    size = (FDC_BYTES_SYNC > RemainingSize) ? RemainingSize : FDC_BYTES_SYNC;
    if (AfterIndexSynchroRemaining)
    {
      if (AfterIndexSynchroRemaining > size)
      {
        AfterIndexSynchroRemaining -= size;
      }
      else
      {
        // Part copy
        tULong partsize = size - AfterIndexSynchroRemaining;
        AddedBytesLength = RemainingSize;
        AfterIndexSynchroRemaining = 0;
        MemSet(DeclaredDataP + AfterIndexSynchroRemaining, partsize, FDC_BYTE_SYNC);
        DeclaredDataP += partsize;
        if ((RemainingSize -= partsize) == 0)
          break;
      }
    }
    else
    {
      MemSet(DeclaredDataP, size, FDC_BYTE_SYNC);
      DeclaredDataP += size;
      if ((RemainingSize -= size) == 0)
        break;
    }

    // Add IAM
    size = (FDC_BYTES_IAM > RemainingSize) ? RemainingSize : FDC_BYTES_IAM;
    if (AfterIndexSynchroRemaining)
    {
      if (AfterIndexSynchroRemaining > size)
      {
        AfterIndexSynchroRemaining -= size;
      }
      else
      {
        // Part copy
        tULong partsize = size - AfterIndexSynchroRemaining;
        AddedBytesLength = RemainingSize;
        AfterIndexSynchroRemaining = 0;
        MemMove(DeclaredDataP + AfterIndexSynchroRemaining, fdc_iam, partsize);
        DeclaredDataP += partsize;
        if ((RemainingSize -= partsize) == 0)
          break;
      }
    }
    else
    {
      MemMove(DeclaredDataP, fdc_iam, size);
      DeclaredDataP += size;
      if ((RemainingSize -= size) == 0)
        break;
    }

    // Add GAP1
    size = (FDC_BYTES_GAP1 > RemainingSize) ? RemainingSize : FDC_BYTES_GAP1;
    if (AfterIndexSynchroRemaining)
    {
      AddedBytesLength = RemainingSize;
    }
    MemSet(DeclaredDataP, size, FDC_BYTE_GAP1);
    DeclaredDataP += size;
    if ((RemainingSize -= size) == 0)
      break;
  }
  while (RemainingSize > 0);


  //
  // Post-index area synchronization
  //
  if (AfterIndexLength)
  {
    CurrentSectorP = &trackP->sector[CurrentSector];
    
    // Read post index GAP4a byte
    post_index_byte = CurrentSectorP->declared_data[CurrentSectorP->declared_size - AfterIndexLength];

    // Detect bit shift and pulse shift 
    DetectMFMShift(post_index_byte, FDC_BYTE_GAP4A, &track_synchro_bitshift, &track_synchro_pulseshift);

    // Shift post-index area
    MFMShift(CurrentSectorP->declared_data + CurrentSectorP->declared_size - AddedBytesLength,
             AddedBytesLength,
             track_synchro_bitshift,
             track_synchro_pulseshift);
  }
}
/*----------------------------------------------------------------------------*/


static tBool IsPowerOf128(tULong Value)
/***********************************************************************
 *
 *  IsPowerOf128
 *
 ***********************************************************************/
{
  /*   128 */      if (Value == (128 << 0)) return cTrue;
  /*   256 */ else if (Value == (128 << 1)) return cTrue;
  /*   512 */ else if (Value == (128 << 2)) return cTrue;
  /*  1024 */ else if (Value == (128 << 3)) return cTrue;
  /*  2048 */ else if (Value == (128 << 4)) return cTrue;
  /*  4096 */ else if (Value == (128 << 5)) return cTrue;
  /*  8192 */ else if (Value == (128 << 6)) return cTrue;
  /* 16384 */ else if (Value == (128 << 7)) return cTrue;

  return cFalse;
}
/*----------------------------------------------------------------------------*/


//==============================================================================
//
// Routines
//
//==============================================================================

static tVoid MemMove(tUChar* destP,
                     const tUChar* sourceP,
                     tULong numBytes)
/***********************************************************************
 *
 *  MemMove
 *
 ***********************************************************************/
{
  while (numBytes--)
  {
    *(destP++) = *(sourceP++);
  }
}
/*----------------------------------------------------------------------------*/


static tVoid MemSet(tUChar* destP,
                    tULong numBytes,
                    tUChar value)
/***********************************************************************
 *
 *  MemSet
 *
 ***********************************************************************/
{
  while (numBytes--)
  {
    *(destP++) = value;
  }
}
/*----------------------------------------------------------------------------*/


#ifdef __cplusplus
}  /* End of the 'extern "C"' block */
#endif

