#ifndef _CLASS_FILE_H
#define _CLASS_FILE_H

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

#include "java.h"

namespace java {

using namespace std;

typedef unsigned char u1;
typedef unsigned short u2;
typedef unsigned int u4;

class cp_info;
class field_info;
class method_info;
class attribute_info;

typedef vector< cp_info* > cp_info_vector_type;
typedef vector< field_info* > filed_info_vector_type;
typedef vector< method_info* > method_info_vector_type;
typedef vector< attribute_info* > attribute_info_vector_type;

class ClassFileIO {
public:
   virtual bool read(istream & in) { return true; }
   virtual bool write(ostream & out) { return true; }
};

class Code {
public:
   string code;
   string arg1;
   string arg2;

   Code(const string & c = "",
        const string & a1 = "",
        const string & a2 = "")
   : code(c), arg1(a1), arg2(a2) {

   }

   const string & get_code() {
      return code;
   }

   const string & get_arg1() {
      return arg1;
   }

   const string & get_arg2() {
      return arg2;
   }
};
   
typedef vector< Code* > code_vector_type;

class Method {
   string name;
   code_vector_type code_vec;

public:
   const string & get_name() {
      return name;
   }

   Code * get_code(const size_t i) {
      Code * p = code_vec[i];
      return p;
   }
      
   size_t get_size() {
      return code_vec.size();
   }

   void add_code(Code * p_code) {
      code_vec.push_back(p_code);
   }

   void set_name(const string & n) {
      name = n;
   }
};
   
typedef vector< Method* > method_vector_type;

class cp_info : public ClassFileIO {
public:
   u1 tag;
   u2 count;
   u1 * info;
};

class attribute_info : public ClassFileIO {
public:
   u2 attribute_name_index;
   u4 attribute_length;
   u1 * info;

   bool read(istream & stream) {
      bool ret = true;

      stream.read((char*)&attribute_name_index, sizeof(u2));
      stream.read((char*)&attribute_length, sizeof(u4));

      swap_endian(attribute_name_index);
      swap_endian(attribute_length);

      cout << "----" << endl;
      cout << "attribute_name_index: " << attribute_name_index << endl << flush;
      cout << "attribute_length: " << attribute_length << endl << flush;

      info = new u1 [ attribute_length ];

      stream.read((char*)info, attribute_length);
      cout << "info == \"" << info << "\"" << endl;
      cout << "----" << endl;

      return ret;
   }
};

class Code_attribute : public ClassFileIO {
public:
   u2 attribute_name_index;
   u4 attribute_length;
   u2 max_stack;
   u2 max_locals;
   u4 code_length;
   u1 * code;
   u2 exception_tabele_length;
   class MyCode : public ClassFileIO {
   public:
       u2 start_pc;
       u2 end_pc;
       u2 handler_pc;
       u2 catch_type;

       bool read(istream & stream) {
          stream.read((char*)&start_pc, sizeof(u2));
          stream.read((char*)&end_pc, sizeof(u2));
          stream.read((char*)&handler_pc, sizeof(u2));
          stream.read((char*)&catch_type, sizeof(u2));

          swap_endian(start_pc);
          swap_endian(end_pc);
          swap_endian(handler_pc);
          swap_endian(catch_type);

          return stream;
       }
   };
   typedef vector< MyCode* > code_vector_type;
   u2 exception_table_length;
   code_vector_type exception_table;
   u2 attributes_count;
   //typedef vector< Code_attribute* > Code_attribute_info_vector_type;
   //Code_attribute_info_vector_type attributes;
   attribute_info_vector_type attributes;
   Method * p_method;

   void set_method(Method * p_m) { p_method = p_m; }

   Method * get_method() { return p_method; }

   bool read(istream & stream, cp_info_vector_type & constant_pool, vector< Code* > & ldc) {
      bool ret = true;

      stream.read((char*)&attribute_name_index, sizeof(u2));
      stream.read((char*)&attribute_length, sizeof(u4));
      stream.read((char*)&max_stack, sizeof(u2));
      stream.read((char*)&max_locals, sizeof(u2));
      stream.read((char*)&code_length, sizeof(u4));

      swap_endian(attribute_name_index);
      swap_endian(attribute_length);
      swap_endian(max_stack);
      swap_endian(max_locals);
      swap_endian(code_length);

      cout << "attribute_name_index: " << attribute_name_index << endl << flush;
      cout << "attribute_length: " << attribute_length << endl << flush;
      cout << "max_stack: " << max_stack << endl << flush;
      cout << "max_locals: " << max_locals << endl << flush;
      cout << "code_length: " << code_length << endl << flush;

      code = new u1 [ code_length ];

      stream.read((char*)code, code_length);

      Code * p_code;
      enum { CH_MAX = 256 };
      char ch[CH_MAX];
  
      ch[0] = ch[1] = '\0';
   
      for(u4 i = 0; i < code_length; i++) {
         cout << "code[" << i << "] == " << dec << (int)code[i] << endl << flush;
         switch(code[i]) {
         case 177:
         p_code = new Code("return");
         p_method->add_code(p_code);
         break;

         case 87:
         p_code = new Code("pop");
         p_method->add_code(p_code);
         break;

         case 0x12:
         i++;
         if(i < code_length) {
            int index = ((int)(code[i]));
            cp_info * p_info = constant_pool[index];
            memset(ch, 0, CH_MAX);
            sprintf(ch, "%d", index);
            p_code = new Code("ldc", ch);
            ldc.push_back(p_code);
            p_method->add_code(p_code);
         }
         break;

         case 16:
         i++;
         if(i < code_length) {
            memset(ch, 0, CH_MAX);
            sprintf(ch, "%d", (int)code[i]);
            p_code = new Code("ipush", ch);
            p_method->add_code(p_code);
         }
         break;

         case 96:
         p_code = new Code("iadd");
         p_method->add_code(p_code);
         break;

         case 4:
         p_code = new Code("iconst", "1");
         p_method->add_code(p_code);
         break;

         case 0xB8:
         if(i < code_length) {
            memset(ch, 0, CH_MAX);
            unsigned int c1 = code[i+1];
            unsigned int c2 = code[i+2];
            //int index = (c1 << 8) + c2;
            int index = (c1 * 64) + c2;
cout << "ARG1: " << (int)code[i+1] << endl;
cout << "ARG2: " << (int)code[i+2] << endl;
cout << "INVOKESTATIC: " << index << endl;
            sprintf(ch, "%d", index);
            p_code = new Code("invokestatic", ch);
            p_method->add_code(p_code);
         }
         i += 2;
         break;

         case 183: {
         memset(ch, 0, CH_MAX);
         unsigned int c1 = code[i+1];
         unsigned int c2 = code[i+2];
         //int index = (c1 << 8) + c2;
         int index = (c1 * 64) + c2;
cout << "ARG1: " << (int)code[i+1] << endl;
cout << "ARG2: " << (int)code[i+2] << endl;
cout << "INVOKESPECIAL: " << index << endl;
         sprintf(ch, "%d", index);
         p_code = new Code("invokespecial", ch);
         p_method->add_code(p_code);
         }
         i += 2;
         break;

         case 182:
         memset(ch, 0, CH_MAX);
         sprintf(ch, "%d", (int)code[i]);
         p_code = new Code("invokevirtual", ch);
         p_method->add_code(p_code);
         break;

         case 0x2A:
         p_code = new Code("aload_0");
         p_method->add_code(p_code);
         break;

         case 0x3C:
         p_code = new Code("istore_1");
         p_method->add_code(p_code);
         break;

         default:
            cerr << "byte: " << (int)code[i] << endl;
            cerr << "bad instruction..." << endl;
            exit(1);
         }
      }

      stream.read((char*)&exception_table_length, sizeof(u2));
       
      swap_endian(exception_table_length);

      cout << "exception_table_length: " << dec << exception_table_length << endl << flush;

      for(u2 i = 0; i < exception_table_length; i++) {
         MyCode * c = new MyCode();
         if(c->read(stream)) {
            cout << "Code read..." << endl << flush;
            exception_table.push_back(c);
         }
         else {
            cout << "No Code..." << endl << flush;
            delete c;
            ret = false;
            break; 
         }
      }

      stream.read((char*)&attributes_count, sizeof(u2));

      swap_endian(attributes_count);

      cout << "attributes_count: " << dec << attributes_count << endl << flush;

      for(u2 i = 0; i < attributes_count; i++) {
         attribute_info * attr = new attribute_info();
         if(attr->read(stream)) {
            cout << "read code attributes..." << endl << flush;
            attributes.push_back(attr);
         }
         else {
            cout << "unable to read code attributes..." << endl << flush;
            ret = false;
            delete attr;
            break;
         }
      }

      return ret;
   }
};


class method_info : public ClassFileIO {
public:
   u2 access_flags;
   u2 name_index;
   u2 descriptor_index;
   u2 attributes_count;
   typedef vector< Code_attribute* > Code_attribute_info_vector_type;
   Code_attribute_info_vector_type attributes;
   Method * p_method;

   u2 get_name_index() { return name_index; }

   void set_method(Method * p_m) { p_method = p_m; }

   bool read(istream & stream, cp_info_vector_type & constant_pool, vector< Code* > & ldc) {
      bool ret = true;

      stream.read((char*)&access_flags, sizeof(u2));
      stream.read((char*)&name_index, sizeof(u2));
      stream.read((char*)&descriptor_index, sizeof(u2));
      stream.read((char*)&attributes_count, sizeof(u2));

      swap_endian(access_flags);
      swap_endian(name_index);
      swap_endian(descriptor_index);
      swap_endian(attributes_count);

      cout << "-----START-METHOD-----------" << endl;
      cout << "access_flags: " << dec << access_flags << endl << flush;
      cout << "name_index: " << dec << name_index << endl << flush;
      cout << "descriptor_index: " << dec << descriptor_index << endl << flush;
      cout << "attributes_count: " << dec << attributes_count << endl << flush;
      cout << "-----END-METHOD-----------" << endl;

      Code_attribute * ainfo;

      for(u2 i = 0; i < attributes_count; i++) {
         ainfo = new Code_attribute();

         ainfo->set_method(p_method);

         if(ainfo->read(stream, constant_pool, ldc)) {
            cout << "read method attribute..." << endl << flush;
            attributes.push_back(ainfo);
         }
         else {
            cout << "can not read method..." << endl << flush;
            ret = false;
            break;
         }         
      }

      return ret;
   }
};


class field_info : public ClassFileIO {
public:
   u2 access_flags;
   u2 name_index;
   u2 descriptor_index;
   u2 attributes_count;
   attribute_info_vector_type attributes;

   bool read(istream & stream) {
      bool ret = true;

      stream.read((char*)&access_flags, sizeof(u2));
      stream.read((char*)&name_index, sizeof(u2));
      stream.read((char*)&descriptor_index, sizeof(u2));
      stream.read((char*)&attributes_count, sizeof(u2));

      swap_endian(access_flags);
      swap_endian(name_index);
      swap_endian(descriptor_index);
      swap_endian(attributes_count);

      cout << "access_flags: " << access_flags << endl << flush;
      cout << "name_index: " << name_index << endl << flush;
      cout << "descriptor_index: " << descriptor_index << endl << flush;
      cout << "attributes_count: " << attributes_count << endl << flush;

/*
   short attribute_name_index;
   short name_index;
   short attribute_count;
   int attribute_length;
   short num_annotations;
   short type_index;
   short num_element_value_pairs;
   short element_name_index;
   short element_value;
*/


      for(u2 i = 0; i < attributes_count; i++) {
         attribute_info * ainfo = new attribute_info();
         if(ainfo->read(stream)) {
            cout << "attribute..." << endl << flush; 
            attributes.push_back(ainfo);
         }
         else {
            cerr << "bad attribute..." << endl;
            delete ainfo;
            ret = false;
            break;
         }
      }

      return ret;
   }
};

class CONSTANT_NameAndType_info {
public:
   u1 tag;
   u2 name_index;
   u2 descriptor_index;
};

class CONSTANT_Methodref_info {
public:
   u1 tag;
   u2 class_index;
   u2 name_and_type_index;
};

string to_string2(int i) 
{
   char buffer[64];
   sprintf(buffer, "%d", i);
   return string(buffer);
}

typedef vector< string > string_vector_type;

class ClassFile : public ClassFileIO {
public:
   u4 magic;
   u2 minor_version;
   u2 major_version;
   u2 const_pool_count;
   cp_info_vector_type constant_pool;
   u2 access_flags;
   u2 this_class;
   u2 super_class;
   u2 interfaces_count;
   typedef vector< u2 > interface_vector_type;
   interface_vector_type interfaces;
   u2 fields_count;
   typedef vector< field_info* > field_info_vector_type;
   field_info_vector_type fields;
   u2 methods_count;
   method_info_vector_type methods;
   u2 attributes_count;
   attribute_info_vector_type attributes;
   method_vector_type method_vec;
   typedef vector< CONSTANT_Methodref_info* > methodref_vector_type;
   methodref_vector_type methodref_pool;
   typedef vector< CONSTANT_NameAndType_info* > name_and_type_vector_type;
   name_and_type_vector_type name_and_type_pool;
   typedef vector< pair< int, int > > name_vector_type;
   name_vector_type name_vec;
   string class_name;
   string_vector_type string_vec;
   string_vector_type utf8_vec;

   ClassFile(const string & cn) {
      class_name= cn;
   }

   ~ClassFile() {
      for(size_t i = 0; i < methodref_pool.size(); i++) {
         delete methodref_pool[i];
      }
      methodref_pool.clear();

      for(size_t i = 0; i < name_and_type_pool.size(); i++) {
         delete name_and_type_pool[i];
      }
      name_and_type_pool.clear();
   }


   enum
   {
       CONSTANT_Utf8 =                1,
       CONSTANT_Integer =             3,
       CONSTANT_Float=                4,
       CONSTANT_Long =                5,
       CONSTANT_Double =              6,
       CONSTANT_String =              7,
       CONSTANT_Class =               8,
       CONSTANT_Fieldref =            9,
       CONSTANT_Methodref =          10,
       CONSTANT_InterfaceMethodref = 11,
       CONSTANT_NameAndType =        12,
   };

   void const_dump(ostream & out) {
      cout << "start const_dump()..." << endl << flush;

      for(size_t i = 1; i < constant_pool.size(); i++) {
         if(constant_pool[i]) {
            if(constant_pool[i]->info) {
               cout << "constant_pool[" << i << "]->info == \"" 
                    << constant_pool[i]->info << "\"" << endl << flush;
            }
            else {
               cout << "constant_pool[" << i << "]->tag == \"" << (int)(constant_pool[i]->tag) << "\"" 
                    << endl << flush;
            }
         }
      }
  
      cout << "-----------------" << endl;
      for(size_t i = 0; i < attributes.size(); i++) {
         cout << "attributes[" << i << "]->attribute_name_index == " << attributes[i]->attribute_name_index << endl;
         cout << "attributes[" << i << "]->attribute_length == " << attributes[i]->attribute_length << endl;
         cout << "attributes[" << i << "]->info == " << attributes[i]->info << endl;
      }
      cout << "-----------------" << endl;

      cout << "done const_dump()..." << endl << flush;
   }

   Method * get_method(const string & name) {
      Method * ret = 0;
      for(size_t i = 0; i < method_vec.size(); i++) {
         if(name == method_vec[i]->get_name()) {
            ret = method_vec[i];
            break;
         }
      }
      return ret;
   }

   bool read(istream & stream) {
      bool ret = false;

      //vector< pair< int, int > > name_vec;

      string_vec.clear();
      utf8_vec.clear();

      stream.read((char*)&magic, sizeof(u4));
      stream.read((char*)&minor_version, sizeof(u2));
      stream.read((char*)&major_version, sizeof(u2));
      stream.read((char*)&const_pool_count, sizeof(u2));

      swap_endian(magic);
      swap_endian(minor_version);
      swap_endian(major_version);
      swap_endian(const_pool_count);

      cout << "magic: " << hex << magic << endl
           << "minor_version: " << dec << minor_version << endl 
           << "major_version: " << dec << major_version << endl
           << "const_pool_count: " << dec << const_pool_count << endl << endl << flush;

      if(magic == 0xCAFEBABE) {
         ret = true;
         size_t size = 0U;

         constant_pool.push_back(new cp_info());

         for(u2 i = 1; i < const_pool_count; i++) {
            if(!stream)
               break;
            cp_info * cp = new cp_info();
            cp->tag = stream.get();
            switch(cp->tag) {
            case CONSTANT_Utf8:
            size = 2;
            break;

            case CONSTANT_Integer:
            size = 4;
            break;

            case CONSTANT_Float:
            size = 4;
            break;

            case CONSTANT_Long:
            size = 8;
            break;

            case CONSTANT_Double:
            size = 8;
            break;

            case CONSTANT_String:
            size = 2;
            break;

            case CONSTANT_Class:
            size = 2;
            break;

            case CONSTANT_Fieldref:
            size = 4;
            break;

            case CONSTANT_Methodref:
            size = 4;
            break;

            case CONSTANT_InterfaceMethodref:
            size = 4;
            break;

            case CONSTANT_NameAndType:
            size = 4;
            break;
            }

            u1 * buffer = new u1 [ size ];
  
            stream.read((char*)buffer, size);

            u2 * iptr = 0;
            u2 * iptr2 = 0;
            u2 temp;
            u1 * buffer2 = 0;

            cp->info = 0;

            switch(cp->tag) {
            case CONSTANT_Utf8:
            iptr = (u2*)(buffer);
            temp = *iptr;
            swap_endian(temp);
            buffer2 = new u1[temp+1];
            memset(buffer2, 0, temp+1);
            stream.read((char*)buffer2, temp);
            cp->info = buffer2;
            buffer2 = 0;
            utf8_vec.push_back(string((const char*)cp->info));
            break;

            case CONSTANT_Integer:
            break;

            case CONSTANT_Float:
            break;

            case CONSTANT_Long:
            break;

            case CONSTANT_Double:
            break;

            case CONSTANT_String:
            iptr = (u2*)buffer;
            swap_endian(*iptr);
string_vec.push_back(to_string2(*iptr));
            break;

            case CONSTANT_Class:
            iptr = (u2*)buffer;
            swap_endian(*iptr);
            break;

            case CONSTANT_Fieldref:
            iptr = (u2*)buffer;
            swap_endian(*iptr);
            break;

            case CONSTANT_Methodref:
            iptr = (u2*)buffer;
            swap_endian(*iptr);
            {
               CONSTANT_Methodref_info * p_info = new CONSTANT_Methodref_info();
               p_info->tag = 0;
               p_info->class_index = (*iptr)-1;
               //p_info->class_index = 0;
               p_info->name_and_type_index = name_and_type_pool.size() - 1;
               methodref_pool.push_back(p_info);
               buffer2 = new u1[64];
               memset(buffer2, 0, 64);
               sprintf((char*)buffer2, "%d", methodref_pool.size()-1);
               cp->info = buffer2;
               buffer2 = 0;
            }
            break;

            case CONSTANT_InterfaceMethodref:
            iptr = (u2*)buffer;
            swap_endian(*iptr);
            break;

            case CONSTANT_NameAndType:
            iptr = (u2*)buffer;
            iptr2 = (u2*)(buffer + 2);
            swap_endian(*iptr);
            swap_endian(*iptr2);
            {
               CONSTANT_NameAndType_info * p_info = new CONSTANT_NameAndType_info();
               p_info->tag = 0;
               p_info->name_index = *iptr;
               p_info->descriptor_index = *iptr2;
               name_and_type_pool.push_back(p_info);
               name_vec.push_back(pair< int, int >(p_info->name_index, p_info->descriptor_index));
cout << "name_index: " << p_info->name_index << endl;
cout << "descriptor_index: " << p_info->descriptor_index << endl;
            }
            break;
            }

            if(cp->info) {
               cout << "info[" << i << "]->info == \"" << cp->info << "\"" << endl << flush;
            }
            else {
               cout << "info[" << i << "]->tag == \"" << (int)(cp->tag) << "\"" << endl << flush;
            }

            constant_pool.push_back(cp);

            delete [] buffer;
         }
      }

/*
for(int b = 0; b < methodref_pool.size(); b++) {
if(methodref_pool[b]->info) {
cout << "[" << methodref_pool[b]->info << "]" << endl;
}
}
*/

      //stream.unget();
      stream.read((char*)&access_flags, sizeof(u2));
      stream.read((char*)&this_class, sizeof(u2));
      stream.read((char*)&super_class, sizeof(u2));
      stream.read((char*)&interfaces_count, sizeof(u2));

      swap_endian(access_flags);
      swap_endian(this_class);
      swap_endian(super_class);
      swap_endian(interfaces_count);

      u2 iface;
      for(u2 i = 0; i < interfaces_count; i++) {
         iface = 0;
         stream.read((char*)&iface, sizeof(u2));
         swap_endian(iface);
      }

      cout << "access_flags: " << dec << access_flags << endl << flush;
      cout << "this_class: " << dec << this_class << endl << flush;
      cout << "super_class: " << dec << super_class << endl << flush;
      cout << "interfaces_count: " << dec << interfaces_count << endl << flush;

      stream.read((char*)&fields_count, sizeof(u2));

      swap_endian(fields_count);

      cout << "fields_count: " << dec << fields_count << endl << flush;

      for(u2 i = 0; i < fields_count; i++) {
cout << "i == " << i << endl << flush;

         field_info * finfo = new field_info();

         if(finfo->read(stream)) {
            cout << "read field info..." << endl << flush;

            fields.push_back(finfo);
         }
         else {
            delete finfo;
            cerr << "read error..." << endl;
            ret = false;
            break;
         }
      }

      cout << "done read fields..." << endl << flush;

      stream.read((char*)&methods_count, sizeof(u2));

      swap_endian(methods_count);

      cout << "methods_count: " << dec << methods_count << endl << flush;

      vector< Code* > ldc;

      for(u2 i = 0; i < methods_count; i++) {
         method_info * minfo = new method_info();

         Method * p_method = new Method();

         minfo->set_method(p_method);
 
         if(minfo->read(stream, constant_pool, ldc)) {
            cout << "read method info..." << endl << flush;

cout << "minfo->get_name_index() == " << minfo->get_name_index() << endl << flush;
cout << "constant_pool[" << minfo->get_name_index() << "] == \""
     << (const char *)(constant_pool[minfo->get_name_index()]->info) << "\"" << endl << flush;

            p_method->set_name((const char *)(constant_pool[minfo->get_name_index()]->info));
            //p_method->set_name("main");

            method_vec.push_back(p_method);
         }
         else {
            delete minfo;
            cerr << "read method..." << endl;
            ret = false;
            break; 
         }
      }

      cout << "LDC:" << endl;
      for(size_t i = 0; i < ldc.size(); i++) {
         int index = atoi(ldc[i]->arg1.c_str());
         int tag = constant_pool[index]->tag;
         int tindex = atoi(string_vec[i].c_str());
         int sindex = (tindex - tag) + index + 1;
         string val = "";

         cout << "TINDEX: \"" << tindex << "\" ";
         cout << "INDEX: \"" << index << "\" ";
         cout << "TAG: \"" << tag << "\" ";
         cout << "SINDEX: " << sindex << endl<< endl;

         cout << "(" << tindex << " - " << tag << ") + " << index << " + 1 == " << sindex << endl << endl;

         ldc[i]->arg1 = string("\"") + (const char*)(constant_pool[sindex]->info) + string("\"");
         cout << "STRING[" << i << "] -> \"" << (const char*)(constant_pool[sindex]->info) << "\"" << endl;
      }
      cout << "UTF8:" << endl;
      for(size_t i = 0; i < utf8_vec.size(); i++) {
         cout << "UTF8[" << i << "] -> \"" << utf8_vec[i] << "\"" << endl;
      }
      cout << "CLASS: " << class_name << endl;
      for(size_t i = 0; i < name_vec.size(); i++) {
         cout << "FUNCTION: " << (const char*)(constant_pool[name_vec[i].first]->info)
              << "(" << (const char*)(constant_pool[name_vec[i].second]->info) << ")" << endl;
         cout << "i -> " << i << endl;
      }

      return ret;
   }

   void write_asm(ostream & out) {
      out << ".class " << class_name << endl << endl;

      field_info * p_field;

      for(size_t i = 0; i < fields.size(); i++) {
         p_field = fields[i];
         out << ".field " << (const char*)(constant_pool[p_field->name_index]->info) << endl;
         out << endl;
      }

      out << endl;

      Method * p_method;
 
      size_t index = 0U;

      for(size_t i = 0; i < method_vec.size(); i++) {
         p_method = method_vec[i];
         out << ".method " << p_method->get_name() << endl;
         out << ".data" << endl;
         out << ".code" << endl;
         for(size_t j = 0; j < p_method->get_size(); j++) {
            string op = p_method->get_code(j)->get_code();
            string arg1 = p_method->get_code(j)->get_arg1();
            string arg2 = p_method->get_code(j)->get_arg2();
            if(op == "invokestatic") {
               index = atoi(arg1.c_str());
               arg1 = (const char*)(constant_pool[index]->info);
               index = atoi(arg1.c_str());
               arg1 = (const char*)(constant_pool[(name_vec[index].first-1)]->info);
               arg1 += ".";
               arg1 += (const char*)(constant_pool[name_vec[index].first]->info);
               arg2 = (const char*)(constant_pool[name_vec[index].first+1]->info);
            }
            else if(op == "invokespecial") {
            }
            else if(op == "invokevritual") {
            }
            cout << "\t" << op << " " << arg1 << " " << arg2 << endl;
         }
         out << ".end" << endl;
         out << endl;
      }
   }
};

class Exception_attribute : public ClassFileIO {
public:
   u2 attribute_name_index;
   u4 attribute_length;
   u2 number_of_exceptions;
   u2 * exception_index_table;
};

class InnerClass_attribute : public ClassFileIO {
public:
   u2 attribute_name_index;
   u4 attribute_length;
   u2 number_of_classes;
   class InnerClass : public ClassFileIO {
   public:
      u2 inner_class_info_index;
      u2 outer_class_info_idnex;
      u2 inner_name_index;
      u2 inner_class_access_flags;
   };
   typedef vector< InnerClass* > inner_class_vector_type;
   inner_class_vector_type classes;
};

class EnclosingMethod_attribute : public ClassFileIO {
public:
   u2 attribute_name_index;
   u4 attribute_length;
   u2 class_index;
   u2 method_index;
};

class Synthetic_attribute : public ClassFileIO {
public:
   u2 attribute_name_index;
   u4 attribute_length;
};

class Signature_attribute : public ClassFileIO {
public:
   u2 attribute_name_index;
   u4 attribute_length;
   u2 signature_index;
};

class SourceFile_attribute : public ClassFileIO {
public:
   u2 attribute_name_index;
   u4 attribute_length;
   u2 sourcefile_index;
};

class SourceDebugExtension_attribute : public ClassFileIO {
public:
   u2 attribute_name_index;
   u4 attribute_length;
   u1 * debug_extension;
};

class LineNumberTable_attribute : public ClassFileIO {
public:
   u2 attribute_name_index;
   u4 attribute_length;
   u2 line_number_table_length;
   class LineNumber { 
      u2 start_pc;
      u2 line_number;
   };
   typedef vector< LineNumber* > line_number_vector_type;
   line_number_vector_type line_number_table; 
};

class LocalVariableTable_attribute : public ClassFileIO {
public:
   u2 attribute_name_index;
   u4 attribute_length;
   u2 local_variable_table_length;
   class LocalVariable : public ClassFileIO {
      u2 start_pc;
      u2 length;
      u2 name_index;
      u2 descriptor_index;
      u2 index;
   }; 
   typedef vector< LocalVariable* > local_variable_vector_type;
   local_variable_vector_type local_variable_table;
};

class LocalVarableTableType_attribute : public ClassFileIO {
public:
   u2 attribute_name_index;
   u4 attribute_length;
   u2 local_variable_type_table_length;
   class LocalVariableType : public ClassFileIO {
   public:
      u2 start_pc;
      u2 length;
      u2 name_index;
      u2 signature_index;
      u2 index;
   };
   typedef vector< LocalVariableType > local_variable_type_vector_type;
   local_variable_type_vector_type local_variable_type_table;
};

class Deprecated_attribute : public ClassFileIO {
public:
   u2 attribute_name_index;
   u4 attribute_length;
};

class annotation;

class RuntimeVisibleAnnotations_attribute : public ClassFileIO {
public:
   u2 attribute_name_index;
   u4 attribute_length;
   u2 num_annotations;
   typedef vector< annotation* > annotations_vector_type;
   annotations_vector_type annotations;
};

class element_value;

class annotation : public ClassFileIO {
public:
   u2 type_index;
   u2 num_element_value_pairs;
   class annotation_element {
   public:
      u2 element_name_index;
      element_value * value;
   };
   typedef vector< annotation_element* > annotation_element_vector_type;
   annotation_element_vector_type  element_value_pairs;
};

typedef vector< annotation* > annotation_vector_type;

class element_value : public ClassFileIO {
public:
   u1 tag;
   u2 const_value_index;
   class enum_const : public ClassFileIO {
   public:
      u2 type_name_index;
      u2 const_name_index;
   };
   enum_const enum_const_value;
   u2 class_info_index;
   annotation annotation_value;
   class array_value_value : public ClassFileIO {
   public:
      u2 num_values;
      typedef vector< element_value* > element_vector_type;
      element_vector_type values;
   };
   array_value_value array_value;
};

class RuntimeVisibileParameterAnnotations_attribute : public ClassFileIO {
public:
   u2 attribute_name_index;
   u4 attribute_length;
   u1 num_parameters;
   class anno_count : public ClassFileIO {
   public:
      u2 num_annotations;
      annotation_vector_type annotations;
      //annotation *annotations[num_annotations];
   };
   typedef vector< anno_count* > anno_count_vector_type;
   anno_count_vector_type parameter_annotations;
};

class RuntimeInvisibleParameterAnnotations_attribute : public ClassFileIO {
public:
   u2 attribute_name_index;
   u4 attribute_length;
   u1 num_parameters;
   class ann : public ClassFileIO {
   public:
      u2 num_annotations;
      annotation_vector_type annotations;
   };
   typedef vector< ann* > ann_vector_type;
   ann_vector_type parameter_annotations;
};

class AnnotationDefault_attribute : public ClassFileIO {
public:
   u2 attribute_name_index;
   u4 attribute_length;
   element_value default_value;
};

}

#endif /* _CLASS_FILE_H */
