/****************************************************************************
plantCompiler.c -- Generates the c code for the plant PNs
This is the 2010 version. The 2010 version was written by M. V. Iordache.
Created: May 2010.
Last updated: Nov 2011.
****************************************************************************/

// TO DO:
// For each transition ti create "#define ti___data(x)  {(x).t = ...;(x).l = ...; ...}" allowing to initialize ___TR[j] to the data of ti. This is for the select function  

#include<stdio.h>
#include<stdarg.h>
#include<time.h>
#include"pns.h"
#include"codegen.h"
#include"insert.h"

#define TEMPL "ProcessTemplate.c"

extern char ProcessTemplate_c[];
extern char *inc_name;

FILE* searchTemplate(char* s) {
  // In future versions this should do a search in some default paths. 

  FILE* f;

  // Look in current directory

  f = fopen(s, "r");
  if(!f) 
    merror(0,"Unable to find the template file %s in the current directory. Copy spcommon.h and the template files from newcodegen/ to the working directory.", s);

  return f;
}


struct myinsert genDefineBlock(specs *sp, process* pr, TrData* TrInfo, int* actlabels) {
  struct myinsert ins;
  int i, j, k, ntr, t, no_place, out_place, label;
  pns *pn;

  ins = InitInsert(512); 
  pn = pr->pn;

  // calculate ntr, the maximum number of output transitions

  for(ntr = 0, j = 0; j < pn->pnum; j++) {
    for(i = 0, k = 0; i < pn->tnum; i++) 
      if(GetMatrixEl(&(pn->out), j, i))
	k++;
    if(k > ntr)
      ntr = k;
  }

  ins = FSInsert(ins, "\n#define ___NTR %d", ntr);
  ins = SInsert(" //This is the max number of output transitions", ins);
  //ins = SInsert("\nstatic struct ___trans ___TR[___NTR];", ins);
  ins = SInsert("\nstatic const char ___TRinfo[] = {", ins);
  for(i = 0, j = 0; i < pn->tnum; i++, j++) {
    if(i)
      ins = SInsert(", ", ins);
    if(j == 10) {
      j = 0;
      ins = CInsert('\n', ins);
    }
    ins = FSInsert(ins, "%3d", (int)TrInfo[i].flags);
  }
  ins = SInsert("};", ins);

  if(pr->dfns)
    ins = FSInsert(ins, "\n\n%s", pr->dfns);

  if(pr->thread) {
    ins = FSInsert(ins, "\n\n\
void* ___start_%s(void* ___v)  {\n\
  return ___main_function(___v);\n\
}\n", pr->name);
  }

  if(sp->signals)
    ins = SInsert("#define ___SIGNALS\n", ins);
  if(sp->singlechannel)
    ins = SInsert("#define SINGLE_READ_CHANNEL\n", ins);

  //ins = SInsert("\n\n//----- BEGINNING OF USER DEFINITIONS -----\n\n", ins);
  //ins = SInsert(pr->include, ins); // get include data of process
  //ins = SInsert("\n\n//----- END OF USER DEFINITIONS -----\n\n", ins);
  //if(DBG3) // if yes, then generated executables should also be in debug mode
  //  incl = SInsert("\n#define ___DEBUG\n", incl);


  // Determine whether it is useful to generate the TRdata array
  // (This unlikely to be necessary apart from select functions.)

  i = pn->pnum;
  if(pn)
    if(pn->select)
      for(i = 0; i < pn->pnum; i++)
	if(pn->select[i].text != 0)
	  break;
  // Select functions have been defined if i != pn->pnum
  // Then write the TRdata array
  if(i != pn->pnum) {
    ins = SInsert("\nstatic const struct ___trans ___TRData[] = {", ins);
    for(t = 0; t < pn->tnum; t++) {
      out_place = -1;
      no_place = 1;
      for(j = 0; j < pn->pnum; j++)
	if(GetMatrixEl(&(pn->in), j, t)) { // if arc from t to j
	  out_place = j;
	  no_place = 0;
	  break;
	}
      label = 0;
      if(pn->t)
	label = pn->t[t].l;
      if(t)
	ins = SInsert(", ", ins);
      ins = FSInsert(ins,"{%d, %d, %d, %d, %d}", no_place, out_place, label, actlabels[t], t);
    }
    ins = SInsert("};\n", ins);
  }

  return ins;
}



struct myinsert genMainBlock(process* pr, int* actlabels) {
  struct myinsert ins;
  int i, j, t, out_place;
  int flag, no_place, label, fl;
  pns *pn;
  arcs* arcp;
  //char cc[sizeof(pn->pnum)*3 + 1];

  ins = InitInsert(512); 
  pn = pr->pn;

  for(i = 0; i < pn->pnum; i++) {
    ins = FSInsert(ins, "\ncase %d:\n", i);
    if(pn->segment)
      if(pn->segment[i].text) {
	if(pr->file)
	  ins = FSInsert(ins,"#line %d \"%s\"\n", pn->segment[i].line, pr->file);
        ins = SInsert(pn->segment[i].text, ins);
	if(pr->file)
	  ins = SInsert(LINEDIR, ins);
      }

    // Inserting/creating select functions
    // The output of the select function is the ___TR list.
      
    ins = SInsert("\nfor(___i = 0, ___j = 0; !___i; ___j++) {", ins);
    ins = AddToIndent(ins, INDENT);
    ins = SInsert("\nif(___j == 1)\n\
  debugInfo(\"Waiting at state %d for one of the transitions to get enabled\",\
___place);", ins); 
    // The code will not progress until a transition is enabled
    fl = 0;
    if(pn->select)
      fl = (pn->select[i].text != 0);
    if(fl) { // if select function already provided
      if(pr->file)
	ins = FSInsert(ins,"\n#line %d \"%s\"", pn->select[i].line, pr->file);
      ins = FSInsert(ins, "\n___i = %s;", pn->select[i].text);
      if(pr->file)
	ins = SInsert(LINEDIR, ins);
    }
    else {  // write the operations involved in selecting the next transition
      for(t = 0; t < pn->tnum; t++) {
	if(GetMatrixEl(&(pn->out), i, t)) { // if there is an arc (p,t)
	  flag = 0; // set to 1 if transition arc has a condition in pn->arcs
	  if(pn->arc_list) {
	    for(arcp = pn->arc_list[t]; arcp; arcp = arcp->next)
	      if(arcp->in_place == i) {
		if(arcp->condition.text)
		  flag = 1;
		break;
	      }
	  } // end if pn->arcs
	  out_place = -1;
	  no_place = 1;
	  for(j = 0; j < pn->pnum; j++)
	    if(GetMatrixEl(&(pn->in), j, t)) { // if arc from t to j
	      out_place = j;
	      no_place = 0;
	      break;
	    }
	  label = 0;
	  if(pn->t)
	    label = pn->t[t].l;
	  if(flag) {  // if the transition is conditionally enabled
	    if(pr->file)
	      ins = FSInsert(ins,"\n#line %d \"%s\"", arcp->condition.line, pr->file);
	    ins = FSInsert(ins, "\nif(%s)", arcp->condition.text);
	    if(pr->file)
	      ins = SInsert(LINEDIR, ins);
	    ins = FSInsert(ins, "\n___i = ___AddToTR(___TR, ___i, %d, %d, %d, %d, %d);",\
			   no_place, out_place, label, t, actlabels[t]);
	  }
	  else // if transition has no conditions
	    ins = FSInsert(ins, "\n___i = ___AddToTR(___TR, ___i, %d, %d, %d, %d, %d);",\
			   no_place, out_place, label, t, actlabels[t]);
	}
      } // end of for
      ins = SInsert("\n___TRShuffle(___TR, ___i);", ins); 
      // Transition lists obtained using functions provided by the user are
      // not shuffled, because transitions might be placed in the list in a 
      // significant order.

    } //end of else and of block dealing with the selection of next transition 

    ins = AddToIndent(ins, -INDENT);
    ins = SInsert("\n}\nbreak;\n", ins);
  }
  return ins; 
}


char* GetFileName(const char* s, const char *ext) {
  char *fileName;

  fileName = tmalloc((strlen(s)+strlen(ext)+2)*sizeof(char));
  sprintf(fileName, "%s.%s", s, ext);

  return fileName;
}


void compileProcessArray(specs *sp, process **procArray, int nbrElements, \
			 TrData** TrInfo, int** actlabels) {
  // Note that TrInfo[i][j] indicates whether transition j of process i is
  // controlled and/or observed by the supervisor.

  int i, j;
  char *fileName, *tmp; 
  FILE* plantFile;
  time_t currentTime;
  struct myinsert incl, mblock, pre;
  cblock bl1, bl2;
  struct input in;

  if(sp->templ) {
    in.file = searchTemplate(TEMPL); in.buf = 0;
    rewind(in.file);
  }
  else {
    in.file = 0; in.buf = ProcessTemplate_c;
  }
  
  for(i = 0; i < nbrElements; i++) {
    fileName = GetFileName(procArray[i]->name, "c");
    plantFile = fopen(fileName, "w");    
    if(!plantFile) 
      merror(0, "CODEGEN: Error creating plant file %s", plantFile);
    else if(DBG3)
      printf("Created plant file %s\n", fileName);

    // Write header of file
 
    pre = InitInsert(128);
    time(&currentTime);                 

    pre = FSInsert(pre, "\
// This is %s generated from template %s.\n\
// %s\n\n", fileName, TEMPL, asctime(localtime(&currentTime)));

    if(procArray[i]->thread) 
      pre = SInsert("\n#define ___THREAD\n", pre);
    else 
      pre = SInsert("\n#define ___PROCESS\n", pre);
    pre = FSInsert(pre,"#define ___NAME \"%s\"\n", procArray[i]->name);
    pre = FSInsert(pre,"#define ___INSTANCE \"%s\"\n", procArray[i]->instance);
        
    // generate declarations/definitions
    incl = genDefineBlock(sp, procArray[i], TrInfo[i], actlabels[i]); 

    // generate block for main()
    mblock = genMainBlock(procArray[i], actlabels[i]); 

    // Write output file

    if(sp->templ) rewind(in.file);
    bl1.file = 0; bl1.text = GetInsertBuf(&incl);
    bl2.file = 0; bl2.text = GetInsertBuf(&mblock);
    FillTemplate(in, plantFile, fileName, inc_name, GetInsertBuf(&pre), bl1, \
		 procArray[i]->include, procArray[i]->main, bl2);

    free(fileName); 
    free(GetInsertBuf(&incl)); 
    free(GetInsertBuf(&mblock)); free(GetInsertBuf(&pre));
    fclose(plantFile);
  }//End for

  if(sp->templ)
    fclose(in.file);

}//End compilePlantArray



