#include "common.h"

myfile* filelist=NULL;
int     nextchandle=65012;

myfile::myfile(int chandlenew,std::string dirnamenew,std::string filenamenew)
{
// TODO: Check if this is the same file again, return same handle if it is
dirname  = dirnamenew;
filename = filenamenew;
chandle  = chandlenew;
count    = 1;

next_file= filelist;
filelist = this;
}

myfile::~myfile()
{
if(rtn_string != NULL) free(rtn_string); rtn_string=NULL;
// TO DO: Check if file is open and close it
if(filelist) // Linked list removal
 {
 if(filelist == this)
  {
  filelist = next_file;
  next_file = NULL;
  }
 else
  {
  myfile* current = filelist;
  while(current->next_file && current->next_file != this)
   {
   current = current->next_file;
   }
  if(current != NULL && current->next_file == this) // found it
   {
   current->next_file = next_file;
   next_file = NULL;
   }
  }
 } // End linked list removal

}

int myfile::candelete(void)
{
if(count == 0) { return 1; }
return 0;
}

myfile* myfile::find(int h) // h is the chandle we should match
{
//if(!filelist) return NULL;

myfile* current=filelist;
while(current)
 {
 if(h == current->chandle)
  {
  return current;
  }
 current=current->next_file;
 }
return NULL;
}

void myfile::close(int h)
{
if(filelist)
 {
 myfile* closing = myfile::find(h);
 closing->close(closing);
 }
}

void myfile::close(myfile* closing)
{
if(closing)
 {
 closing->count--;
 if(closing->open != OPEN_C)
  {
  fclose(closing->fhandle);
  closing->open = OPEN_C;
  }
 }
}

void myfile::closeall(void) // Actually does safe, not close!
{
myfile* l=filelist;
while(l)
 {
 if(l->open != OPEN_C)
  {
  fclose(l->fhandle);
  l->open = OPEN_C;
  }
 l=l->next_file;
 }
}



void myfile::print(void)
{
myfile* current=filelist;
while(current)
 {
 printf( "---\nMCHandle  : %d\n",current->chandle);
 printf(      "Filename  : %s\n",current->filename.c_str());
 printf(      "Count     : %d\n",current->count);
 current=current->next_file;
 }
}

int myfile::matched(std::string dn,std::string fn) // dirname, filename. Return chandle if matches
{
myfile* current=filelist;
while(current != NULL)
 {
 if(current->filename == fn && current->dirname == dn)
  {
  current->count++;
  return current->chandle;
  }
 current=current->next_file;
 }
return 0;
}

int myfile::openw()
{
filelist->closeall(); // Something not right, limit to one open file at a time, for now
// Count open files and limit how many can be open at once
//int     countopen=0;
//int     count=0;
//myfile* check=filelist;
//while(check != NULL)
// {
// if(open != OPEN_C) countopen++;
// count++;
// check=check->next_file;
// }
//if(countopen>6)
// {
// this->closeall();
// }
//printf("\n\n\n");

if(open == OPEN_RW) { return 1; } // Already open

if(open == OPEN_R)
 {
 fclose(fhandle);
 open = OPEN_C;
 }

// Create the directory, if it doesn't exist and haven't checked yet
if(madedir==0)
 {
 regs r;
 r.r[0]=23;
 r.r[1]=(regint)dirname.c_str();
 if(os(OS_File,&r,&r) != NULL) return 0;
 if(r.r[0]==0)
  {
  r.r[0]=8;
  r.r[1]=(regint)dirname.c_str();
  r.r[4]=0;
  if(os(OS_File,&r,&r) != NULL) return 0;
  madedir=1;
  }
 }

// Try opening with update (r+b), if fails try creating the file (w+b)
// Both of these open for read and write
FILE* f=fopen((char*)filename.c_str(),"r+b");   // Open the file
if(!f) { f=fopen((char*)filename.c_str(),"w+b"); }
if(!f) { seterror("Cannot open"); } // TODO: IF any files are open could try closing all open and try again, ONLY WHEN MULTIPLE OPEN ARE PERMITTED

if(f)
 {
 fhandle=f;
 open   =OPEN_RW;
 fseek(f,0,SEEK_SET);
 }

return 1;
}


// Get choices directory, could be Choices$Dir or Choices$Write
std::string getchoices(void)
{
std::string rtn;
char cdir[]  ="Choices$Dir\0";
char cwrite[]="Choices$Write\0";

regs r;
r.r[0] = (regint)cwrite;
r.r[1] = 0;
r.r[2] = -1;
r.r[3] = 0;
r.r[4] = 0;
os(OS_ReadVarVal,&r,&r);

if(r.r[2] != 0) // variable Choices$Write existed
 rtn = cwrite;
else
 rtn = cdir;
return rtn;
}

// processfile - process the name of a file into a valid RISC OS filename
std::string processfile(char* name)
{
std::string rtn="";

int pos = 0;
while(name[pos] != 0)
 {
 char ch=name[pos];

 // Check chars are acceptable, whitelisted to ensure unexpected chars will still work
 int ok=0;
 if(ch>='A' && ch<='Z') ok=1;
 if(ch>='a' && ch<='z') ok=1;
 if(ch>='0' && ch<='9') ok=1;
 if(!ok) ch='_';

 rtn += ch;
 pos++;
 }

return rtn;
}

// R0=appname, R1=filename. Exit: R0=filehandle
int fileopen(regs &r)
{
reseterror();
if(r.r[0] == 0) { seterror("MyChoices_Open requires an application name in R0"); return 0; }
if(r.r[1] == 0) { seterror("MyChoices_Open requires an file name in R1"); return 0; }

std::string appname  = processfile((char*)r.r[0]);
std::string filename = processfile((char*)r.r[1]);

std::string dirname  = "<";
            dirname += getchoices();
            dirname += ">";
            dirname += '.';
            dirname += appname;
std::string fullname = dirname+"."+filename;

//std::cout << dirname << '\n';
//std::cout << fullname<< '\n';

// Check if these files already exist
int matched=filelist->matched(dirname,fullname);
if(matched != 0) { return matched; }

// Create a new myfile class and set data up
new myfile(nextchandle,dirname,fullname);
int thishandle=nextchandle;
//assert(f != NULL);
nextchandle++;
return thishandle;
}

// R0=handle supplied (currently a pointer - good idea?)
void fileclose(regs &r)
{
if(filelist)
 {
 myfile* scrap=filelist->find(r.r[0]);
 if(scrap != NULL)
  {
  scrap->close(scrap);
  if(scrap->candelete()) delete scrap;
  }
 }
}

void fileprint(void)
{
filelist->print();
}

void setfilelength(char* filename,int length)
{
regs r;
r.r[0]=11;
r.r[1]=(regint)filename;
r.r[2]=0xFFF;
r.r[3]=0;
r.r[4]=0;
r.r[4]=length;
os(OS_File,&r,&r);
}
















