#include <sstream>

#include "BochsController.hpp"
#include "BochsMemory.hpp"
#include "../SALInst.hpp"
#include "../Listener.hpp"

namespace fail {

#ifdef DANCEOS_RESTORE
bx_bool restore_bochs_request = false;
bx_bool save_bochs_request    = false;
std::string  sr_path          = "";
#endif

bx_bool reboot_bochs_request        = false;
bx_bool interrupt_injection_request = false;

BochsController::BochsController()
	: SimulatorController(new BochsMemoryManager()),
	  m_CPUContext(NULL), m_CurrentInstruction(NULL)
{
	for (unsigned i = 0; i < BX_SMP_PROCESSORS; i++)
		addCPU(new ConcreteCPU(i));
}

BochsController::~BochsController()
{
	delete m_Mem;
	std::vector<ConcreteCPU*>::iterator it = m_CPUs.begin();
	while (it != m_CPUs.end()) {
		delete *it;
		it = m_CPUs.erase(it);
	}
}

void BochsController::updateBPEventInfo(BX_CPU_C *context, bxInstruction_c *instr)
{
	assert(context != NULL && "FATAL ERROR: Bochs internal member was NULL (not expected)!");
	assert(instr != NULL && "FATAL ERROR: Bochs internal member was NULL (not expected)!");
	m_CPUContext = context;
	m_CurrentInstruction = instr;
}

void BochsController::onTimerTrigger(void* thisPtr)
{
	// FIXME: The timer logic can be modified to use only one timer in Bochs.
	//        (For now, this suffices.)
	TimerListener* pli = static_cast<TimerListener*>(thisPtr);
	// Check for a matching TimerListener. (In fact, we are only
	// interessted in the iterator pointing at pli.)
	ListenerManager::iterator it = std::find(simulator.m_LstList.begin(),
											simulator.m_LstList.end(), pli);
	// TODO: This has O(|m_LstList|) time complexity. We can further improve this
	//       by creating a method such that makeActive(pli) works as well,
	//       reducing the time complexity to O(1).
	simulator.m_LstList.makeActive(it);
	simulator.m_LstList.triggerActiveListeners();
}

void BochsController::onIOPort(ConcreteCPU* cpu, unsigned char data, unsigned port, bool out) {
	// Check for active IOPortListeners:
	ListenerManager::iterator it = m_LstList.begin();
	while (it != m_LstList.end()) {
		BaseListener* pLi = *it;
		IOPortListener* pIOPt = dynamic_cast<IOPortListener*>(pLi);
		if (pIOPt != NULL && pIOPt->isMatching(port, out)) {
			pIOPt->setData(data);
			pIOPt->setTriggerCPU(cpu);
			it = m_LstList.makeActive(it);
			// "it" has already been set to the next element (by calling
			// makeActive()):
			continue; // -> skip iterator increment
		}
		it++;
	}
	m_LstList.triggerActiveListeners();
}

bool BochsController::save(const std::string& path)
{
	int stat;

	stat = mkdir(path.c_str(), 0777);
	if (!(stat == 0 || errno == EEXIST)) {
		return false;
		// std::cout << "[FAIL] Can not create target-directory to save!" << std::endl;
		// TODO: (Non-)Verbose-Mode? Log-level? Maybe better: use return value to indicate failure?
	}

	save_bochs_request = true;
	BX_CPU(0)->async_event |= 1;
	sr_path = path;
	m_CurrFlow = m_Flows.getCurrent();
	m_Flows.resume();
	return true;
}

void BochsController::saveDone()
{
	save_bochs_request = false;
	m_Flows.toggle(m_CurrFlow);
}

void BochsController::restore(const std::string& path)
{
	clearListeners();
	restore_bochs_request = true;
	BX_CPU(0)->async_event |= 1;
	sr_path = path;
	m_CurrFlow = m_Flows.getCurrent();
	m_Flows.resume();
}

void BochsController::restoreDone()
{
	restore_bochs_request = false;
	m_Flows.toggle(m_CurrFlow);
}

void BochsController::reboot()
{
	clearListeners();
	reboot_bochs_request = true;
	BX_CPU(0)->async_event |= 1;
	m_CurrFlow = m_Flows.getCurrent();
	m_Flows.resume();
}

void BochsController::rebootDone()
{
	reboot_bochs_request = false;
	m_Flows.toggle(m_CurrFlow);
}

void BochsController::fireInterrupt(unsigned irq)
{
	interrupt_injection_request = true;
	interrupt_to_fire = irq;
	// FIXME needed?  BX_CPU(0)->async_event |= 1;
	m_CurrFlow = m_Flows.getCurrent();
	m_Flows.resume();
}

void BochsController::fireInterruptDone()
{
	interrupt_injection_request = false;
	m_Flows.toggle(m_CurrFlow);
}

const std::string& BochsController::getMnemonic() const
{
	static std::string str;
	bxInstruction_c* pInstr = getCurrentInstruction();
	assert(pInstr != NULL && "FATAL ERROR: Bochs internal member was NULL (not expected)!");
	const char* pszName = get_bx_opcode_name(pInstr->getIaOpcode());
	if (pszName != NULL)
		str = pszName;
	else
		str.clear();
	return str;
}

ConcreteCPU& BochsController::detectCPU(BX_CPU_C* pCPU) const
{
	unsigned i = 0;
#if BX_SUPPORT_SMP
	for (; i < BX_SMP_PROCESSORS; i++) {
		if (BX_CPU_C[i] == pCPU) // cmp this ptr with all possible CPU objects
			break; // index "i" found! -> stop!
	}
#endif
	return getCPU(i);
}

} // end-of-namespace: fail
