#ifndef _SEQUENCE_H
#define _SEQUENCE_H

#define _FILE_OFFSET_BITS 64
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include "profile.h"

typedef struct s_Data {
  off_t Offset;
  size_t HeaderLength;
  size_t SequenceLength;
} s_Data;

typedef struct FASTAStructure {
  char FileName[256];
  s_Data  *DataPtr;
  off_t FileSize;
  size_t SequenceCount;
  size_t MaxSequenceSize; // this includes carriage returns
} FASTAStructure;

typedef struct Sequence {
  union {
    char * Header;
    void * Memory;
  } Data;
  PFSequence ProfileData;
} Sequence;


static inline PFSequence * ReadSequenceIndex(Sequence * const Seq, const size_t index, FILE * const stream, const s_Data * const DataPtr)
{
  /* Position into file */
  fseek(stream, (long) DataPtr[index].Offset, SEEK_SET);

  /* Read into memory */
  register const size_t Size = DataPtr[index].HeaderLength + DataPtr[index].SequenceLength + 1; // + 1 accounts for \n in between
  if (fread(Seq->Data.Header, sizeof(char), Size, stream) != Size) return NULL;

  /* Bound text */
  Seq->Data.Header[DataPtr[index].HeaderLength] = '\0';
  Seq->Data.Header[Size] = '\0';

  /* Set Sequence data start */
  Seq->ProfileData.ProfileIndex = (unsigned char*) &(Seq->Data.Header[DataPtr[index].HeaderLength+1]);

  /* Set Sequence length */
  Seq->ProfileData.Length = DataPtr[index].SequenceLength;

  return &(Seq->ProfileData);
}

#ifdef __USE_MMAP__
static inline PFSequence * MMAPReadSequenceIndex(Sequence * const Seq, const size_t index, const char * const restrict Array,
						 const s_Data * const DataPtr, const off_t InitialArrayOffset
#ifdef MMAP_DEBUG
	, const size_t ThreadId, const size_t NodeId, const size_t length
#endif						
)
{
  /* Position into Array */
  register const size_t ArrayOffset = (size_t) ( DataPtr[index].Offset - InitialArrayOffset);
//   fseek(stream, (long) DataPtr[index].Offset, SEEK_SET);

  /* Read into memory */
  register const size_t Size = DataPtr[index].HeaderLength + DataPtr[index].SequenceLength + 1; // + 1 accounts for \n in between
#ifdef MMAP_DEBUG
  if ( Size + ArrayOffset > length) {
    fprintf(stderr,"Thread %lu from Node %lu Seq: %lu will read beyond mmap %lu > %lu\n", ThreadId, NodeId, index, Size + ArrayOffset, length);
  }
#endif
  memcpy(Seq->Data.Header, &Array[ArrayOffset], sizeof(char)*Size);
  
//   if (fread(Seq->Data.Header, sizeof(char), Size, stream) != Size) return NULL;

  /* Bound text */
  Seq->Data.Header[DataPtr[index].HeaderLength] = '\0';
  Seq->Data.Header[Size] = '\0';

  /* Set Sequence data start */
  Seq->ProfileData.ProfileIndex = (unsigned char*) &(Seq->Data.Header[DataPtr[index].HeaderLength+1]);

  /* Set Sequence length */
  Seq->ProfileData.Length = DataPtr[index].SequenceLength;

  return &(Seq->ProfileData);
}
#endif

static inline void ReadSequenceNameIndex(char * const Name, const size_t index, FILE * const stream, const s_Data * const DataPtr)
{
  /* Position into file */
  fseek(stream, (long int) DataPtr[index].Offset, SEEK_SET);

  /* Read into memory */
  if (fscanf(stream, ">%s",Name) != 1) {
    fprintf(stderr, "Read error for sequence name %lu\n", index);
  }
}


static inline void FreeFASTAStructure(FASTAStructure * Info)
{
    free(Info->DataPtr);
}
int AnalyzeFASTAStructure(char * const FileName, FASTAStructure * const Info);
int ExportFASTAStructure(FILE* stream, const FASTAStructure * const Info);
int ImportFASTAStructure(FILE* stream, FASTAStructure * const Info);

#endif