#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <vector>
#include "csv.h"

using namespace std;
using namespace csv_reader;

class CodeTree {
public:
   unsigned char the_byte;
   string the_asm;
   map< unsigned char, CodeTree * > code;
   bool is_terminal() {
      bool ret = true;
      for(map< unsigned char, CodeTree* >::iterator p = code.begin(); p != code.end(); p++) {
         if(p->second) {
            ret = false;
            break;
         }
      }
      return ret && the_asm.size();
   }
};

class FindIF {
public:
   CodeTree tree;

   bool is_end(const size_t index, vector< unsigned char > & code) {
      bool ret = false;
      if(code.size() >= (index + 4)) {
         //120,86,52,18,
         if(code[index] == 120 
            && code[index+1] == 86 
            && code[index+2] == 52 
            && code[index+3] == 18) {
            ret = true;
         }
      }
      return ret;
   }

   void add_code(const string & code, const string & the_asm) {
      unsigned char * buffer = new unsigned char [ code.size() + 1 ];
      strcpy((char*)buffer, code.c_str());
      unsigned char * ptr = (unsigned char*)strtok((char*)buffer, ",");
      vector< unsigned char > temp;
      while(ptr != NULL) {
         temp.push_back(atoi((char*)ptr));
         ptr = (unsigned char*)strtok(NULL, ",");
      }
      delete [] buffer;
      CodeTree * pCode = &tree, * pLast = 0;
      size_t i = 0;
      bool found = false;
      while(pCode) {
         pLast = pCode;
         pCode = pCode->code[temp[i]];
         if(pCode == 0) {
            if(i != temp.size()-1 && !is_end(i,temp)) {
               cout << "/" << (size_t)temp[i] << flush;
               found = true;
               CodeTree * ct = new CodeTree();
               ct->the_byte = temp[i];
               pLast->code[ct->the_byte] = ct;
               pCode = ct;
            }
            else {
               if(is_end(i,temp)) {
                  pLast->the_asm = the_asm;
                  break;
               }
               else {
                  cout << "/" << (size_t)temp[i] << flush;
                  found = true;
                  CodeTree * ct = new CodeTree();
                  ct->the_byte = temp[i];
                  ct->the_asm = the_asm;
                  pLast->code[ct->the_byte] = ct;
                  break;
               }
            }
         }      
         i++;
         if(i >= temp.size()) {
            break;
         }
      }
      if(found) {
         cout << endl;
      }
   }
   string indent(const size_t i) {
      string ret;
      for(size_t j = 0; j < i; j++) {
         ret += "   ";
      }  
      return ret;
   }
   void emit(ostream & out, CodeTree * pCode, size_t i) {
      if(pCode) {
         out << indent(i+1) << "if(code[" << i << "] == " << (unsigned int)(pCode->the_byte) << ") {" << endl;
         out << indent(i+2) << "n++;" << endl;
         bool flag = false;
         if(pCode->the_asm != "" /*&& pCode->is_terminal()*/) {
            out << indent(i+2) << "return \"" << pCode->the_asm << "\";" << endl;
            flag = true;
         }
         if(!flag) {
            for(map< unsigned char, CodeTree* >::iterator ptr = pCode->code.begin(); ptr != pCode->code.end(); ptr++) {
               emit(out, ptr->second, i+1);
            }
         }
         out << indent(i+1) << "}" << endl;
      }
   }
   void emit(ostream & out) {
      CodeTree * pCode = &tree;
      for(map< unsigned char, CodeTree* >::iterator ptr = pCode->code.begin(); ptr != pCode->code.end(); ptr++) {
         emit(out, ptr->second, 0); 
      }
   }
   void run() {
      ifstream fin("code.csv", ios::in);
      if(fin) {
         csv_row_type row;
         string code;
         while(fin >> row) { 
            code = row[1];
            add_code(code, row[0]);
         }
         fin.close();
      }
      ofstream fout("findif.h", ios::out | ios::trunc | ios::ate);
      if(fout) {
         emit(fout);
         fout.close();
      }
   }
};

int
main(int argc, char ** argv)
{
   FindIF findif;
   findif.run();
   return 0;
}
