#include "instruction_map.hpp"
#include "opcode_utility.hpp"

#include "debug_macro.hpp"

/** Instruction is invalid by default.*/
Instruction::Instruction()
		:m_signature(INVALID_SIGNATURE)
{
}
/** construct by address and opcode.*/
Instruction::Instruction(MemoryAddress address, Byte opcode)
		:m_signature(VALID_SIGNATURE),
		 m_info(&opcode::instruction_set[opcode]),
		 m_address(address),
		 m_parameter(0)
{
	m_bytecode.push_back(opcode);
	if (bytelength() == 1) {
		setupOperand();
	}
}
void Instruction::setByteParameter(Byte byte)
{
	invariant();
	require(bytelength() == 2);
	require(m_bytecode.size() == 1);
	
	m_bytecode.push_back(byte);
	if (addressingMode() == opcode::REL) {
		// the origin is next address
		if (byte < 0x80) {
			m_parameter = next() + byte;
		} else {
			m_parameter = next() - (0x100 - byte);
		}
	} else {
		m_parameter = byte;
	}
	setupOperand();
}
void Instruction::setLowHighParameter(Byte low, Byte high)
{
	invariant();
	require(bytelength() == 3);
	require(m_bytecode.size() == 1);
	
	m_bytecode.push_back(low);
	m_bytecode.push_back(high);
		
	m_parameter = low + (high << 8);
	setupOperand();
}

bool Instruction::isValid()const
{
	return m_signature == VALID_SIGNATURE;
}
void Instruction::invariant()const
{
	require(isValid());
}
/** returns operation of instruction.*/
const opcode::Operation& Instruction::op()const
{
	invariant();
	return m_info->op;
}
opcode::AddressingMode Instruction::addressingMode()const
{
	invariant();
	return m_info->addressing_mode;
}
opcode::AccessType Instruction::accessType()const
{
	invariant();
	return m_info->access_type;
}
const opcode::OperationInfo* Instruction::operation()const
{
	invariant();
	OperationInfoServer server;
	return server.getInfoOf(m_info->op);
}
const char* Instruction::mnemonic()const
{
	invariant();
	return operation()->mnemonic;
}
const std::vector<Byte>* Instruction::bytecode()const
{
	invariant();
	return &m_bytecode;
}
unsigned Instruction::bytelength()const
{
	invariant();
	opcode::AddressingModeType addtype(addressingMode());
	return addtype.bytelength();
}
Word Instruction::parameter()const
{
	invariant();
	require_else( bytelength() == m_bytecode.size() ) {
		inspect(bytelength());
		inspect(m_bytecode.size());
	}
	
	return m_parameter;
}
MemoryAddress Instruction::address()const
{
	invariant();
	return m_address;
}
MemoryAddress Instruction::next()const
{
	invariant();
	return address() + bytelength();
}
const Operand& Instruction::result()const
{
	invariant();
	require(bytelength() == m_bytecode.size());
	return m_result;
}
const Operand& Instruction::first()const
{
	invariant();
	require(bytelength() == m_bytecode.size());
	return m_first;
}
const Operand& Instruction::second()const
{
	invariant();
	require(bytelength() == m_bytecode.size());
	return m_second;
}

/** setup all infomations.*/
void Instruction::setupOperand()
{
	invariant();
	require(bytelength() == m_bytecode.size());
	
	m_result.setup(operation()->result, addressingMode(), m_parameter);
	m_first.setup(operation()->first, addressingMode(), m_parameter);
	m_second.setup(operation()->second, addressingMode(), m_parameter);
}
/** get invalid object.*/
Instruction::shared_ptr Instruction::getInvalid()
{
	static shared_ptr invalid(new Instruction);
	return invalid;
}
