#include <cstdlib>
#include <iostream>
#include <string>
#include <cstring>

#include "kbox.h"
#include "ksource.h"
#include "ksetup.h"

using namespace std;

extern kbox_type       VISUAL_KBOX;
extern ksource_type    KBOX_SOURCE;

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

  long long int	MAXPC;

  static bool	data_segment_started = false;
  static bool	data_segment_finished = false;
  static bool	code_segment_started = false;
  static bool	code_segment_finished = false;
  static bool	instructions_started = false;
  static bool	instructions_finished = false;

  string	item;

void setup_kbox (const string& source_file_name)
{
  ksource_type KBOX_SOURCE;
  KBOX_SOURCE.open(source_file_name);
  KBOX_SOURCE.read_line();
  if (KBOX_SOURCE.is_eof())
  {
    cerr << " ... empty source file!\n";
    exit(EXIT_FAILURE);
  }
  item = KBOX_SOURCE.get_opcode();
  if (item == "_DATA_SEGMENT")
  {
    process_data_segment(KBOX_SOURCE);
    data_segment_finished = true;
    process_code_segment(KBOX_SOURCE);
    code_segment_finished = true;
  }
  else if (item == "_CODE_SEGMENT")
  {
    process_code_segment(KBOX_SOURCE);
    code_segment_finished = true;
    process_data_segment(KBOX_SOURCE);
    data_segment_finished = true;
  }
  else
  {
    cerr << " ... unexpected item (" << item << ")!\n";
    exit(EXIT_FAILURE);
  }
  KBOX_SOURCE.close();

  string zr_reg = "I11";
  string sp_reg = "I12";
  string fp_reg = "I13";
  string rp_reg = "I14";
  string hp_reg = "I15";
  VISUAL_KBOX.REGISTERS.set_register
    (zr_reg,0);
  VISUAL_KBOX.REGISTERS.set_register
    (sp_reg,MEMORY_SIZE);
  VISUAL_KBOX.REGISTERS.set_register
    (fp_reg,MEMORY_SIZE);
  VISUAL_KBOX.REGISTERS.set_register
    (rp_reg,MEMORY_SIZE);
  VISUAL_KBOX.REGISTERS.set_register
    (hp_reg,VISUAL_KBOX.MEMORY.get_next_available ());

  VISUAL_KBOX.FLAGS.setPC(0);
  VISUAL_KBOX.FLAGS.setEQ();

  MAXPC = VISUAL_KBOX.INSTRUCTIONS.get_code_size();
}

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

void process_data_segment (ksource_type& infile)
{
  if (data_segment_started)
  {
    cout << " ... redefinition _DATA_SEGMENT!\n";
    exit (EXIT_FAILURE);
  }
  data_segment_started = true;
  while (infile.read_line())
  {
    if (infile.is_eof())
      return;
    item = infile.get_opcode();
    if (item == "_CODE_SEGMENT")
      return;
    if (item == "_DEFINE")
    {
      string label = infile.get_operand();
      klunk value = literal2klunk(infile.get_operand());
      VISUAL_KBOX.MEMORY.define(label,value);
    }
    else if (item == "_RESERVE")
    {
      string label = infile.get_operand();
      klunk size = stoll(infile.get_operand());
      VISUAL_KBOX.MEMORY.reserve(label,size);
    }
    else
    {
      cerr << " ... inappropriate data segment item ("
           << item << ")\n";
      exit(EXIT_FAILURE);
    }
  }
}

void process_code_segment (ksource_type& infile)
{
  if (code_segment_started)
  {
    cout << " ... redefinition _CODE_SEGMENT!\n";
    exit (EXIT_FAILURE);
  }
  code_segment_started = true;
  while (infile.read_line())
  {
    if (infile.is_eof())
      return;
    item = infile.get_opcode();
    if (item == "_DATA_SEGMENT")
      return;
    if (item  == "_GLOBAL")
    {
      string label = infile.get_operand();
      VISUAL_KBOX.GLOBALS.add_global(label);
    }
    else if (item == "_EXTERN")
    {
      string label = infile.get_operand();
      VISUAL_KBOX.EXTERNS.add_extern(label);
    }
    else
    {
      process_instructions(infile);
      instructions_finished = true;
      return;
    }
  }
}

void process_instructions (ksource_type& infile)
{
  instructions_started = true;
  while (true)
  {
    if (item == "_DATA_SEGMENT")
      return;
    if (item == "_LABEL")
    {
      string label = infile.get_operand();
      label_entry le
        (label,VISUAL_KBOX.INSTRUCTIONS.get_code_size());
      VISUAL_KBOX.LABELS.add_label(le);
    }
    else if (item[0] == '_')
    {
      cerr << " ... inappropriate code segment item ("
           << item << ")\n";
      exit(EXIT_FAILURE);
    }
    else // item is an instruction opcode!
    {
      string opcode = item;
      string operand1 = infile.get_operand();
      string operand2 = infile.get_operand();
      string operand3 = infile.get_operand();
      code_entry ce(opcode,operand1,operand2,operand3);
      VISUAL_KBOX.INSTRUCTIONS.add_instruction(ce);
    }
    infile.read_line();
    if (infile.is_eof())
      return;
    else
      item = infile.get_opcode();
  }
}
