#include "instruction_scanner.hpp"
#include "debug_macro.hpp"
#include <sstream>

/** scanner needs start address of instructions.*/
InstructionScanner::InstructionScanner(MemoryAddress start_address)
		:m_start_address(start_address),
		 m_current(start_address)
{
}
InstructionScanner::InstructionScanner(MemoryAddress start_address, MemoryAddress end_address)
		:m_start_address(start_address),
		 m_current(start_address)
{
}
/** add instruction to map, and increment current address.*/
Instruction::shared_ptr
InstructionScanner::createInstruction(unsigned char byte)
{
	Instruction::shared_ptr instr(new Instruction(m_current, byte));
	(*m_map)[m_current]=instr;
	m_current+=instr->bytelength();
	return instr;
}
/** get scanned instruction map.*/
InstructionMap::shared_ptr
InstructionScanner::getMap()const
{
	return m_map;
}
/** read stream and build instruction map.*/
void
InstructionScanner::read(std::istream& is)
{
	m_map.reset(new InstructionMap::map);
	unsigned byte_count = 0;
	for (char ch; is.get(ch);){
//		while (!is.eof()) {  // caution: 'eof' can slip in stringstream.
		Instruction::shared_ptr instr;
		instr = createInstruction(ch);
		byte_count++;
		switch (instr->bytelength()) {
		case 3:
			char low, high;
			if (is.get(low) && is.get(high)) {
				instr->setLowHighParameter(low, high);
				byte_count+=2;
			} else {
				instr->setLowHighParameter(0, 0);
			}
			break;
		case 2:
			if (is.get(ch)) {
				instr->setByteParameter(ch);
				byte_count++;
			} else {
				instr->setByteParameter(0);
			}
			break;
		case 1:
		default:
			break;
		}
	}
}
/** convert vector<Byte> to stream, then read the stream.*/
void
InstructionScanner::readByteVector(std::vector<unsigned char>& bytes)
{
	std::stringstream ss;
	for (unsigned i=0; i<bytes.size(); ++i) {
		ss << bytes[i];
	}
	read(ss);
}
	

InstructionPostScanner::InstructionPostScanner(InstructionMap::shared_ptr map, MemoryAddress address)
		:InstructionScanner(address)
{
	m_map = map;
}
/** read three bytes and add instruction to map.*/
void
InstructionPostScanner::readThreeBytes(Byte opcode, Byte low, Byte high) 
{
	Instruction::shared_ptr instr;
	instr = createInstruction(opcode);
	if (instr->bytelength() == 2) {
		instr->setByteParameter(low);
	} else if (instr->bytelength() == 3) {
		instr->setLowHighParameter(low, high);
	} 
}
