#ifndef _KBOX_H
#define _KBOX_H

#include <cstdlib>
#include <string>
#include <map>
#include <vector>

using namespace std;

/* ----------------------------------------------------- */

const long long int LITERAL_SIZE	= 256;
const long long int MEMORY_SIZE		= 1000000;
const long long int NUMBER_REGISTERS	= 16;

/* ----------------------------------------------------- */
/*                                                       */
/*                        ktypes                         */
/*                                                       */
/* ----------------------------------------------------- */

typedef unsigned long long int	klunk;

typedef long long int		int64;
typedef double			flt64;
typedef char			chr64;
typedef char*			str64;

int64 klunk2int (klunk);	// klunk -> int64
flt64 klunk2flt (klunk);	// klunk -> flt64
chr64 klunk2chr (klunk);	// klunk -> chr64
str64 klunk2str (klunk);	// klunk -> str64

klunk int2klunk (int64);	// int64 -> klunk
klunk flt2klunk (flt64);	// flt64 -> klunk
klunk chr2klunk (chr64);	// chr64 -> klunk
klunk str2klunk (str64);	// str64 -> klunk

klunk literal2klunk (const string&);

char* klunk2hex (klunk);

/* ----------------------------------------------------- */
/*                                                       */
/*                        kflags                         */
/*                                                       */
/* ----------------------------------------------------- */

class flags_type
{
private:
  klunk		PC;
  bool		EQ;
  bool		GT;
  bool		LT;

public:
  flags_type(klunk pc=1,bool eq=true,
    bool gt=false,bool lt=false)
  : PC(pc),EQ(eq),GT(gt),LT(lt)
  { }

  klunk getPC (void) const
  { return PC; }
  bool getEQ (void) const
  { return EQ; }
  bool getGT (void) const
  { return GT; }
  bool getLT (void) const
  { return LT; }

  void setPC (klunk k)
  { PC = k; }
  void setEQ (void)
  { EQ = true; LT = GT = false; }
  void setGT (void)
  { GT = true; EQ = LT = false; }
  void setLT (void)
  { LT = true; EQ = GT = false; }

  void display (void) const;
};

/* ----------------------------------------------------- */
/*                                                       */
/*                       kmemory                         */
/*                                                       */
/* ----------------------------------------------------- */

class map_entry
{
private:
  string	name;
  klunk		address;
  klunk		size;

public:
  map_entry ()
  : name(""),address(0),size(0)
  {  }
  map_entry (const string& n,klunk a,klunk s)
  : name(n),address(a),size(s)
  {  }

  string get_name (void) const
  { return name; }
  klunk get_address (void) const
  { return address; }
  klunk get_size (void) const
  { return size; }

  void set_name (const string& n)
  { name = n; }
  void set_address (klunk a)
  { address = a; }
  void set_size (klunk s)
  { size = s; }
};

class memory_type
{
private:
  klunk		data[MEMORY_SIZE];
  klunk		next_available;

public:
  vector<map_entry>	map;

  memory_type (void)
  : next_available(1)
  {  }

  klunk get_next_available (void) const
  { return next_available; }
  void set_next_available (klunk k)
  { next_available = k; }

  klunk peek (klunk k) const
  { return data[k]; }
  void poke (klunk k,klunk val)
  { data[k] = val; }

  bool is_map_entry (const string&,map_entry&) const;
  void insert_map_entry (const string&,klunk,klunk);

  void define (const string&,klunk);
  void reserve (const string&,klunk);

  void display_map (void) const;
  void display_static (void) const;
  void display_stack (klunk k) const;
  void display_heap (klunk k) const;
};

/* ----------------------------------------------------- */
/*                                                       */
/*                      kregisters                       */
/*                                                       */
/* ----------------------------------------------------- */

class registers_type
{
private:
  klunk		Iregisters[NUMBER_REGISTERS];
  klunk		Fregisters[NUMBER_REGISTERS];

public:
  bool is_register (string&,char&,klunk&) const;

  klunk get_register (string&) const;
  void set_register (string&,klunk);

  void display_Iregisters (void) const;
  void display_Fregisters (void) const;
};

/* ----------------------------------------------------- */
/*                                                       */
/*                        kcode                          */
/*                                                       */
/* ----------------------------------------------------- */

class global_table
{
private:
  vector<string>	data;

public:
  bool is_global (const string&) const;
  void add_global (const string& n)
  { data.push_back(n); }
  void display (void) const;
};

class extern_table
{
private:
  vector<string>	data;

public:
  bool is_extern (const string&) const;
  void add_extern (const string& n)
  { data.push_back(n); }
  void display (void) const;
};

class label_entry
{
private:
  string	name;
  klunk		location;

public:
  label_entry (const string& n,klunk l)
  : name(n),location(l)
  {  }

  string get_name (void) const
  { return name; }
  klunk get_location (void) const
  { return location; }
};

class label_table
{
private:
  vector<label_entry>	data;

public:
  bool is_label (const string&) const;
  void add_label (const label_entry& le)
  { data.push_back(le); }
  void display (void) const;
  klunk get_location (const string&) const;
};

class code_entry
{
private:
  string	opcode;
  string	operand1;
  string	operand2;
  string	operand3;

public:
  code_entry (const string& opc,const string& op1,
              const string& op2,const string& op3)
  : opcode(opc),operand1(op1),operand2(op2),operand3(op3)
  {  }

  string get_opcode (void) const
  { return opcode; }
  string get_operand1 (void) const
  { return operand1; }
  string get_operand2 (void) const
  { return operand2; }
  string get_operand3 (void) const
  { return operand3; }
};

class code_table
{
private:
  vector<code_entry>	data;

public:
  klunk get_code_size (void) const
  { return data.size(); }

  code_entry get_instruction (klunk k) const
  { return data[k]; }

  void add_instruction (const code_entry& ce)
  { data.push_back(ce); }
};

/* ----------------------------------------------------- */
/*                                                       */
/*                         KBOX                          */
/*                                                       */
/* ----------------------------------------------------- */

struct kbox_type
{
  memory_type		MEMORY;
  registers_type	REGISTERS;
  flags_type            FLAGS;
  code_table		INSTRUCTIONS;
  global_table		GLOBALS;
  extern_table		EXTERNS;
  label_table           LABELS;
};

#endif // _KBOX_H
