#include "common.h"

extern myfile* filelist;

int nameok(int s)
{
if(s==0) { seterror("Missing name in MyChoices SWI"); return 0; }
char* c=(char*)s;
if(c[0] == '|') { seterror("Bad name in MyChoices SWI. Cannot start with the pipe symbol"); return 0; }
return 1;
}

int readint(regs &r)
{
myfile* myf=filelist->find(r.r[0]);
if(myf == NULL) { seterror("Unknown handle in MyChoices SWI"); return r.r[2]; }

if(nameok(r.r[1])==0) { return r.r[2]; }

std::string name=(char*)(r.r[1]);

if(myf->openw() == 0) { seterror("Unable to open file in MyChoices SWI"); return r.r[2]; } // Open file for writing, TODO: Open for read only

int rtn =  myf->readint4(name,r.r[2]);
return rtn;
}

// R0=chandle, R1=name, R2=memory, R3=length
void readmem(regs &r)
{
myfile* myf=filelist->find(r.r[0]);
if(myf == NULL) { seterror("Unknown handle in MyChoices SWI"); return; }

if(nameok(r.r[1])==0) { return; }
if(r.r[2]==0) { seterror("Memory pojnter cannot be null in MyChoices SWI"); return; }

std::string name=(char*)(r.r[1]);

if(myf->openw() == 0) { seterror("Unable to open file in MyChoices SWI"); return; } // Open file for writing

myf->readmem(name,r.r[2],r.r[3]);
}

static const char* nullstring="\0";

char* readstring(regs &r)
{
myfile* myf=filelist->find(r.r[0]);
if(myf == NULL) { seterror("Unknown handle in MyChoices SWI"); return (char*)nullstring; }

if(nameok(r.r[1])==0) { return (char*)nullstring; }

std::string name=(char*)(r.r[1]);
std::string defaultvalue="";
if(r.r[2] != 0) { defaultvalue = (char*)(r.r[2]); }

if(myf->openw() == 0) { seterror("Unable to open file in MyChoices SWI"); return (char*)nullstring; } // Open file for writing, TODO: Open for read only

char* rtn =  myf->reads(name,defaultvalue);
return rtn;
}

// R0=chandle, R1=name, R2=value(PTR)
void readfloat(regs &r,FloatFormat ff)
{
myfile* myf=filelist->find(r.r[0]);
if(myf == NULL) { seterror("Unknown handle in MyChoices SWI"); return; }

if(nameok(r.r[1])==0) { return; }
if(r.r[2]==0) { seterror("Missing pointer in MyChoices SWI"); return; }

std::string name=(char*)(r.r[1]);
void* mem=(void*)(r.r[2]);

if(myf->openw() == 0) { seterror("Unable to open file in MyChoices SWI"); return; } // Open file for writing

myf->readf(name,mem,ff);
}

int exist(regs &r)
{
myfile* myf=filelist->find(r.r[0]);
if(myf == NULL) { seterror("Unknown handle in MyChoices SWI"); return 0; }

if(nameok(r.r[1])==0) { return 0; }

std::string name=(char*)(r.r[1]);

if(myf->openw() == 0) { seterror("Unable to open file in MyChoices SWI"); return 0; } // Open file for writing, TODO: Open for read only

return myf->exist(name);
}

int size(regs &r)
{
myfile* myf=filelist->find(r.r[0]);
if(myf == NULL) { seterror("Unknown handle in MyChoices SWI"); return 0; }

if(nameok(r.r[1])==0) { return 0; }

std::string name=(char*)(r.r[1]);

if(myf->openw() == 0) { seterror("Unable to open file in MyChoices SWI"); return 0; } // Open file for writing, TODO: Open for read only

return myf->size(name);
}

void enumerate(regs &r)
{
myfile* myf=filelist->find(r.r[0]);
if(myf == NULL) { seterror("Unknown handle in MyChoices SWI"); return; }

if(myf->openw() == 0) { seterror("Unable to open file in MyChoices SWI"); return; } // Open file for writing, TODO: Open for read only

myf->enumerate(r.r[1],r);
}

char* enumeratertn=NULL;
const char* enumerateempty="\0";

void myfile::enumerate(int where,regs &r)
{
// check if valid
if(where == -1)
 {
 r.r[3] = 0;
 r.r[2] = (regint)enumerateempty;
 r.r[1] = -1;
 }
// where is zero or position in file, run through file to check that is still valid
fseek(fhandle,0,SEEK_SET);
int counter=0;
int found=0;
int more=0;
while(feof(fhandle)==0)
 {
 long int pos=ftell(fhandle);
 int blocksize=fread4(fhandle);
 if(blocksize == 0) { break; } // break if zero, must be end of file
 int blocktype=fread2(fhandle);
 int blocknamesize=fread4(fhandle);
 int blockdatasize=fread4(fhandle);
 IGNORE(blockdatasize);
 if(blocktype != NON)
  {
  std::string blockname=freads(fhandle,blocknamesize);
  // Check if name is | or * start and skip if it is
  if(blockname[0] != '|' && blockname[0] != '*')
   {
   // Check if already found, is there more?
   if(found == 1)
    {
    more=1;
    break;
    }
   // Here we are at the right position
   if(where == pos)
    {
    found = 1;
    r.r[1]=pos+blocksize; // next call start
    if(enumeratertn) free(enumeratertn);
    enumeratertn = (char*)malloc(blockname.length()+1);
    strcpy(enumeratertn,blockname.c_str());
    r.r[2]=(regint)enumeratertn;
    r.r[3]=blocktype;
    }
   }
  }

 fseek(fhandle,pos+blocksize,SEEK_SET);
 counter++;
 if(counter>=0x7FFFFFFF) { break; } // emergency
 }

if(more == 0 || r.r[1] == 0) // Enusre clean end if no more
 {
 r.r[3] = 0;
 r.r[2] = (regint)enumerateempty;
 r.r[1] = -1;
 }

return;
}

int myfile::readint4(std::string name, int defaultvalue)
{
fseek(fhandle,0,SEEK_SET);
int counter=0;
while(feof(fhandle)==0)
 {
 long int pos=ftell(fhandle);
 int blocksize=fread4(fhandle);
 if(blocksize == 0) { break; } // break if zero, must be end of file
 //long int typepos=ftell(fhandle);
 int blocktype=fread2(fhandle);
 int blocknamesize=fread4(fhandle);
 int blockdatasize=fread4(fhandle);
 if(blocktype != NON)
  {
  std::string blockname=freads(fhandle,blocknamesize);
  if(blockname == name)
   {
   if(blocktype == INT) return fread4(fhandle);
   if(blocktype == STR) { std::string s=freads(fhandle, blockdatasize);
                          int n=strtol((char*)s.c_str(),NULL,10);
                          return n;
                        }
   }
  }

 fseek(fhandle,pos+blocksize,SEEK_SET);
 counter++;
 if(counter>=0x7FFFFFFF) { break; } // emergency
 }


return defaultvalue;
}

void myfile::readmem(std::string name, regint start,regint length)
{
memset((void*)start,0,length);
fseek(fhandle,0,SEEK_SET);
int counter=0;
while(feof(fhandle)==0)
 {
 long int pos=ftell(fhandle);
 int blocksize=fread4(fhandle);
 if(blocksize == 0) { break; } // break if zero, must be end of file
 //long int typepos=ftell(fhandle);
 int blocktype=fread2(fhandle);
 int blocknamesize=fread4(fhandle);
 int blockdatasize=fread4(fhandle);
 if(blockdatasize>length) blockdatasize=length; // Limit read to file block length max
 if(blocktype != NON)
  {
  std::string blockname=freads(fhandle,blocknamesize);
  if(blockname == name)
   {
   if(blocktype == MEM) { freadm(fhandle,fileno(fhandle),start,length); }
   }
  }

 fseek(fhandle,pos+blocksize,SEEK_SET);
 counter++;
 if(counter>=0x7FFFFFFF) { break; } // emergency
 }
}

char* myfile::reads(std::string name, std::string defaultvalue)
{
if(rtn_string != NULL) free(rtn_string); rtn_string=NULL;

fseek(fhandle,0,SEEK_SET);
int counter=0;
while(feof(fhandle)==0)
 {
 long int pos=ftell(fhandle);
 int blocksize=fread4(fhandle);
 if(blocksize == 0) { break; } // break if zero, must be end of file
 //long int typepos=ftell(fhandle);
 int blocktype=fread2(fhandle);
 int blocknamesize=fread4(fhandle);
 int blockdatasize=fread4(fhandle);
 if(blocktype != NON)
  {
  std::string blockname=freads(fhandle,blocknamesize);
  if(blockname == name)
   {
   if(blocktype == INT) { int n = fread4(fhandle);char buffer[16]; sprintf(buffer,"%d",n); defaultvalue = buffer; }
   if(blocktype == STR) { defaultvalue = freads(fhandle,blockdatasize); break; }
   }
  }

 fseek(fhandle,pos+blocksize,SEEK_SET);
 counter++;
 if(counter>=0x7FFFFFFF) { break; } // emergency
 }

rtn_string = (char*)malloc(defaultvalue.length()+1);
strcpy(rtn_string,defaultvalue.c_str());

return rtn_string;
}



int myfile::exist(std::string name)
{
fseek(fhandle,0,SEEK_SET);
int counter=0;
while(feof(fhandle)==0)
 {
 long int pos=ftell(fhandle);
 int blocksize=fread4(fhandle);
 if(blocksize == 0) { break; } // break if zero, must be end of file
 //long int typepos=ftell(fhandle);
 int blocktype=fread2(fhandle);
 int blocknamesize=fread4(fhandle);
 int blockdatasize=fread4(fhandle);
 IGNORE(blockdatasize);
 if(blocktype != NON)
  {
  std::string blockname=freads(fhandle,blocknamesize);
  if(blockname == name)
   {
   return 1;
   }
  }

 fseek(fhandle,pos+blocksize,SEEK_SET);
 counter++;
 if(counter>=0x7FFFFFFF) { break; } // emergency
 }


return 0;
}

int myfile::size(std::string name)
{
fseek(fhandle,0,SEEK_SET);
int counter=0;
while(feof(fhandle)==0)
 {
 long int pos=ftell(fhandle);
 int blocksize=fread4(fhandle);
 if(blocksize == 0) { break; } // break if zero, must be end of file
 //long int typepos=ftell(fhandle);
 int blocktype=fread2(fhandle);
 int blocknamesize=fread4(fhandle);
 int blockdatasize=fread4(fhandle);
 IGNORE(blockdatasize);
 if(blocktype != NON)
  {
  std::string blockname=freads(fhandle,blocknamesize);
  if(blockname == name)
   {
   if(blocktype == INT) return 4;
   if(blocktype == STR) return blockdatasize+1;
   if(blocktype == MEM) return blockdatasize;
   if(blocktype == FLT) return 8;
   return 0;
   }
  }

 fseek(fhandle,pos+blocksize,SEEK_SET);
 counter++;
 if(counter>=0x7FFFFFFF) { break; } // emergency
 }


return 0;
}

void myfile::readf(std::string name,void* mem,FloatFormat ff)
{
fseek(fhandle,0,SEEK_SET);
int counter=0;
while(feof(fhandle)==0)
 {
 long int pos=ftell(fhandle);
 int blocksize=fread4(fhandle);
 if(blocksize == 0) { break; } // break if zero, must be end of file
 //long int typepos=ftell(fhandle);
 int blocktype=fread2(fhandle);
 int blocknamesize=fread4(fhandle);
 int blockdatasize=fread4(fhandle);
 IGNORE(blockdatasize);
 if(blocktype != NON)
  {
  std::string blockname=freads(fhandle,blocknamesize);
  if(blockname == name)
   {
   if(blocktype == FLT)
    {
    int sign=fread4(fhandle);
    int exponent=fread4(fhandle);
    int hi=fread4(fhandle);
    int lo=fread4(fhandle);
    int special=fread4(fhandle);
    uint64_t mantissa;
    mantissa = ((uint64_t)hi<<32) | lo;
    int er=float_variables_to_memory(mem,ff,sign,exponent,mantissa,special);
    if(er) seterror("Number too big/Invalid type. MyChoices error");
    break;
    }
   }
  }

 fseek(fhandle,pos+blocksize,SEEK_SET);
 counter++;
 if(counter>=0x7FFFFFFF) { break; } // emergency
 }
}