#include <iostream>
#include <fstream>
#include <string>
#include <vector>

#include <cctype>
#include <cstring>
#include <cstdlib>

using namespace std;

typedef vector< string > string_vector_type;

class J2C {
   string input;
   string output;

public:
   J2C(const string & input,
       const string & output) {
      this->input = input;
      this->output = output;
   }

   ~J2C() {

   }

   bool read_line(string & str, ifstream & fin) {
      str = "";
      char ch = fin.get();
      bool ret = false;
      while(fin) {
         ret = true;
         if(ch == '\n') break;
         str += ch;
         ch = fin.get();
      }
      return ret;
   }

   void get_vec(string_vector_type & vec, string & line) {
      char * buffer = new char [ line.size() + 1 ];
      strcpy(buffer, line.c_str());
      char * ptr = strtok(buffer, "\r\n\t, ");
      vec.clear();
      while(ptr != NULL) {
         vec.push_back(string(ptr)); 
         ptr = strtok(NULL, "\r\n\t, ");
      }
      delete [] buffer;
   }

   string toupper(const string & str) {
      string ret;
      for(size_t i = 0; i < str.size(); i++) {
         ret += ::toupper(str[i]);
      }
      return ret;
   }

   string get_string(const string & str) {
      string ret;
      bool in_str = false;
      for(size_t i = 0; i < str.size(); i++) {
         if(str[i] == '\"') {
            ret += '\"';
            in_str = !in_str;
         }
         else if(in_str) {  
            ret += str[i];
         }
      }
      return ret;
   }

   string to_cxx(const string & str) {
      string ret;
      for(size_t i = 0; i < str.size(); i++) {
         if(str[i] == '.')
            ret += "::";
         else
            ret += str[i];
      }
      return ret;
   }

   string to_c(const string & str) {
      string ret;
      for(size_t i = 0; i < str.size(); i++) {
         if(str[i] == '<') {
            ret += "_";
         }
         else if(str[i] == '>') {
            ret += "_";
         }
         else {
            ret += str[i];
         }
      }
      return ret;
   }

   string get_class_name(const string & t) {
      string ret;
      size_t index = 0;
      for(size_t i = 0; i < t.size(); i++) {
         if(t[i] == '.') {
            index = i;
         }
      }
      for(size_t i = 0; i < index; i++) {
         if(t[i] == '.') 
             ret += "::";
         else 
             ret += t[i];
      }
      if(ret.size() == 0) ret = t;
      return ret;
   }
 
   string get_method_name(const string & t) {
      string ret;
      size_t index = 0;
      for(size_t i = 0; i < t.size(); i++) {
         if(t[i] == '.') {
            index = i;
         }
      }
      if(t[index] == '.') index++;
      for(size_t i = index; i < t.size(); i++)
         ret += t[i];
      return ret;
   }

   void run() {
      string class_name;
      ifstream fin(input.c_str(), ios::in);
      ofstream fout(output.c_str(), ios::trunc | ios::ate | ios::out);
      if(fin && fout) {
         fout << "#include \"byte_code.h\"" << endl << endl;
         string line;
         string_vector_type vec;
         bool in_code = false;
         bool in_data = false;
         bool is_static = false;
         while(read_line(line, fin)) {
            get_vec(vec,line);
            if(vec.size()) {
               if(vec[0] == ".code") {
                  in_code = true;
                  continue;
               }
               else if(vec[0] == ".data") {
                  in_data = true;
                  continue;
               }
               else if((vec[0] == "int" || vec[0] == "float") && in_data) {
                  fout << "\t" << vec[0] << " " << vec[1] << ";" << endl;
               }
               else if(vec[0] == ".end") {
                  in_code = false;
                  in_data = false;
                  fout << "}" << endl << endl;
                  continue;
               }
               else if(vec[0] == ".field") {
                  string field_name = vec[1];
                  fout << "   int " << field_name << ";" << endl;
               }
               else if(vec[0] == ".method") {
                  if(vec[1] == "main") {
                     fout << "static void " << to_c("java_" + vec[1]) << "(string_vector_type & args) {" << endl;
                     is_static = true;
                  }
                  else {
                     fout << "virtual void " << to_c(vec[1]) << "() {" << endl;
                     is_static = false;
                  }
                  fout << "\tint arg1,arg2;" << endl;
                  fout << "\tconst char * ptr;" << endl;
                  fout << "\tvoid * ptr1;" << endl;
                  continue;
               }
               else if(vec[0] == ".maxStack") {
                  int maxSize = atoi(vec[1].c_str());
                  if(maxSize > 0) {
                     fout << "\tvoid * var[" << maxSize << "];" << endl;
                  }
                  if(!is_static) {
                     fout << "\tvar[0] = (void*)this;" << endl;
                  }
               }
               else if(vec[0] == ".class") {
                  class_name = vec[1];
                  fout << "class " << class_name << " {" << endl;
                  fout << "public:" << endl;
                  continue;
               }
               if(in_code) {
                  string label = toupper(vec[0]); 
                  int inc = 0;
                  string opcode;
                  if(vec.size() >= 2) {
                     opcode = toupper(vec[1]);
                  }
                  if((label[0] != 'L' && in_code) || (label == "LDC" && in_code)) {
                     opcode = label;
                     label = "";
                  }
                  else {
                     inc = 1;
                  }
                  string arg1,arg2,arg3;

                  if((vec.size() - inc) == 2) {
                     arg1 = vec[1+inc];
                  }
                  else if((vec.size() - inc) == 3) {
                     arg1 = vec[1+inc];
                     arg2 = vec[2+inc];
                  }
                  else if((vec.size() - inc) == 4) {
                     arg1 = vec[1+inc];
                     arg2 = vec[2+inc];
                     arg3 = vec[3+inc];
                  }

                  if(opcode == "LDC") {
                     arg1 = get_string(line); 
                  }
                  if(label.size()) {
                     fout << label << ":" << endl;
                  }
                  if(opcode == "LDC") {
                     fout << "\t" << opcode << "(" << get_string(line) << ")" << endl;
                  }
                  else if(opcode == "GETSTATIC") {
                     fout << "\t" << opcode << "(" << get_class_name(arg1) << "," << get_method_name(arg1) << ",\"" << arg2 << "\")" << endl;
                  }
                  else if(opcode == "PUTSTATIC") {
                     fout << "\t" << opcode << "(" << get_class_name(arg1) << "," << get_method_name(arg1) << ",\"" << arg2 << "\")" << endl;
                  }
                  else if(opcode == "PUTFIELD") {
                     fout << "\t" << opcode << "(" << get_class_name(arg1) << "," << get_method_name(arg1) << ",\"" << arg2 << "\")" << endl;
                  }
                  else if(opcode == "GETFIELD") {
                     fout << "\t" << opcode << "(" << get_class_name(arg1) << "," << get_method_name(arg1) << ",\"" << arg2 << "\")" << endl;
                  }
                  else if(opcode == "IINC") {
                     fout << "\t" << opcode << "(" << arg1.substr(4) << "," << arg2 << ")" << endl;
                  }
                  else if(opcode == "ALOAD_0") {
                     fout << "\tALOAD(0)" << endl;
                  }
                  else if(opcode == "ALOAD_1") {
                     fout << "\tALOAD(1)" << endl;
                  }
                  else if(opcode == "ASTORE_0") {
                     fout << "\tASTORE(0)" << endl;
                  }
                  else if(opcode == "ASTORE_1") {
                     fout << "\tASTORE(1)" << endl;
                  }
                  else if(opcode == "ASTORE") {
                     fout << "\tASTORE(" << atoi(arg1.substr(4).c_str()) << ")" << endl;
                  }
                  else if(opcode == "ISTORE") {
                     fout << "\tISTORE(" << atoi(arg1.substr(4).c_str()) << ")" << endl;
                  }
                  else if(opcode == "ILOAD") {
                     fout << "\tILOAD(" << atoi(arg1.substr(4).c_str()) << ")" << endl;
                  }
                  else if(opcode == "INVOKESTATIC") {
                     fout << "\t" << opcode << "(" << to_c(to_cxx(arg1)) << ",\"" << arg2 << "\")" << endl;
                  }
                  else if(opcode == "INVOKEVIRTUAL") {
                     fout << "\t" << opcode << "(" << class_name << "," << get_method_name(to_c(arg1)) << ","
                          << "\"" << arg2 << "\")" << endl;
                  }
                  else if(opcode == "INVOKESPECIAL") {
                     if(arg1 == "this") {
                         fout << "\t" << opcode << "(" << class_name << ",_init_" 
                              << ",\"" << arg2 << "\")" << endl;
                     }
                     else {
                         fout << "\t" << opcode << "(" << get_class_name(to_c(arg1)) << ",_init_" 
                              << ",\"" << arg2 << "\")" << endl;
                     }
                  }
                  else if(arg1 != "" && arg2 != "" && arg3 != "") {
                     fout << "\t" << opcode << "(" << arg1 << "," << arg2 << "," << arg3 << ")" << endl;
                  }
                  else if(arg1 != "" && arg2 != "") {
                     fout << "\t" << opcode << "(" << arg1 << "," << arg2 << ")" << endl;
                  }
                  else if(arg1 != "") {
                     fout << "\t" << opcode << "(" << arg1 << ")" << endl;
                  }
                  else {
                     fout << "\t" << opcode << "()" << endl;
                  }
               }
            }
         }
         fout << "};" << endl;
         fout << endl;
         fout << "int" << endl
              << "main(int argc, char ** argv)" << endl;
         fout << "{" << endl;
         fout << "   try {" << endl;
         fout << "      string_vector_type args;" << endl;
         fout << "      stack_type rt_stack;" << endl;
         fout << "      the_stack = &rt_stack;" << endl;
         fout << "      for(int i = 1; i < argc; i++)" << endl;
         fout << "         args.push_back(argv[i]);" << endl;
         fout << "      " << class_name << "::java_main(args);" << endl;
         fout << "   }" << endl;
         fout << "   catch(...) {" << endl;
         fout << "      cerr << \"exception...\" << endl;" << endl;
         fout << "      exit(1);" << endl;
         fout << "   }" << endl;
         fout << "   return 0;" << endl;
         fout << "}" << endl;
         fout << flush;
         fin.close();
         fout.close();
      }
   }
};

int
main(int argc, char ** argv)
{
   if(argc == 3) {
      J2C(argv[1], argv[2]).run();
   }
   else {
      cerr << "usage: " << argv[0] << " <input>.asm <c++_source_file>.cpp" << endl;
      exit(1);
   }
   return 0;
}
