#ifndef __BOCHS_CONTROLLER_HPP__
  #define __BOCHS_CONTROLLER_HPP__

#include <string>
#include <cassert>
#include <iostream>
#include <iomanip>
#include <string.h>

#include "FailBochsGlobals.hpp"

#include "../SimulatorController.hpp"

#include "bochs.h"
#include "cpu/cpu.h"
#include "config.h"
#include "iodev/iodev.h"
#include "pc_system.h"
#include "gui/siminterface.h" // for BX_SMP_PROCESSORS

namespace fail {

class ExperimentFlow;

/**
 * \class BochsController
 * Bochs-specific implementation of a SimulatorController.
 * 
 * @note The instruction (IP) pointer modification handler (onBreakpoint())
 * is called (from the Breakpoints aspect) *every* time the Bochs-internal IP
 * changes. The handler itself evaluates if a breakpoint event needs to be
 * triggered. This handler needs to implement the breakpoint-mechanism in an
 * indirect fashion because the Bochs simulator doesn't support native
 * breakpoints. To be compatible with the interface specified by the simulator
 * class, we need to provide the two members \c m_CPUContext and \c m_CacheEntry.
 * The elements are being set before the handler is called (see
 * \c updateBPEventInfo())
 */
class BochsController : public SimulatorController {
private:
	ExperimentFlow* m_CurrFlow; //!< Stores the current flow for save/restore-operations
	BX_CPU_C *m_CPUContext; //!< Additional information that is passed on occurence of a BPEvent
	bxInstruction_c *m_CurrentInstruction; //!< dito.
public:
	/**
	 * Initialize the controller, i.e. add the number of simulated CPUs.
	 */
	BochsController();
	~BochsController();
	/* ********************************************************************
	 * Standard Listener Handler API:
	 * ********************************************************************/
	/**
	 * I/O port communication handler. This method is called (from
	 * the IOPortCom aspect) every time when Bochs performs a port I/O operation.
	 * @param cpu the CPU that caused the IO port access
	 * @param data the data transmitted
	 * @param port the port it was transmitted on
	 * @param out true if the I/O traffic has been outbound, false otherwise
	 */
	void onIOPort(ConcreteCPU* cpu, unsigned char data, unsigned port, bool out);
	/**
	 * Internal handler for TimerListeners. This method is called when a previously
	 * registered (Bochs) timer triggers. It searches for the provided TimerListener
	 * object within the ListenerManager and fires such an event by calling
	 * \c triggerActiveListeners().
	 * @param thisPtr a pointer to the TimerListener-object triggered
	 * 
	 * FIXME: Due to Bochs internal timer and ips-configuration related stuff,
	 *        the simulator sometimes panics with "keyboard error:21" (see line
	 *        1777 in bios/rombios.c, function keyboard_init()) if a TimerListener
	 *        is added *before* the bios has been loaded and initialized. To
	 *        reproduce this error, try adding a \c TimerListener as the initial step
	 *        in your experiment code and wait for it (\c addListenerAndResume()).
	 *        This leads to the consequence that timers cannot be added/enabled at
	 *        boot time.
	 */
	void onTimerTrigger(void *thisPtr);
	/* ********************************************************************
	 * Simulator Controller & Access API:
	 * ********************************************************************/
	/**
	 * Save simulator state.
	 * @param path Location to store state information
	 * @return \c true if the state has been successfully saved, \c false otherwise
	 */
	bool save(const std::string& path);
	/**
	 * Save finished: Callback from Simulator
	 */
	void saveDone();
	/**
	 * Restore simulator state. Clears all Listeners.
	 * @param path Location to previously saved state information
	 */
	void restore(const std::string& path);
	/**
	 * Restore finished: Callback from Simulator
	 */
	void restoreDone();
	/**
	 * Reboot simulator. Clears all Listeners.
	 */
	void reboot();
	/**
	 * Reboot finished: Callback from Simulator
	 */
	void rebootDone();
	/**
	 * Fire an interrupt.
	 * @param irq Interrupt to be fired
	 */
	void fireInterrupt(unsigned irq);
	/**
	 * Fire done: Callback from Simulator 
	 */
	void fireInterruptDone();
	/* ********************************************************************
	 * BochsController-specific (not implemented in SimulatorController!):
	 * ********************************************************************/
	/**
	 * Retrieves the textual description (mnemonic) for the current
	 * instruction. The format of the returned string is Bochs-specific.
	 * @return the mnemonic of the current instruction whose address
	 *         is given by \c Register::getInstructionPointer(). On errors,
	 *         the returned string is empty
	 */
	const std::string& getMnemonic() const;
	/**
	 * Retrieves the current Bochs instruction cache entry
	 * @returns a pointer to a bxICacheEntry_c object
	 */
	inline bxInstruction_c *getCurrentInstruction() const { return m_CurrentInstruction; }
	/**
	 * Retrieves the current CPU context
	 * @return a pointer to a \c BX_CPU_C object
	 */
	inline BX_CPU_C *getCPUContext() const { return m_CPUContext; }
	/**
	 * Updates the internal BPEvent data. See \c BochsController::onBreakpoint
	 * for further information. This method should only be called from the breakpoint aspect.
	 * @param context the CPU context object ptr (Bochs internal=
	 * @param cacheEntry the Bochs internal CPU cache entry ptr
	 */
	void updateBPEventInfo(BX_CPU_C *context, bxInstruction_c *instr);
	/**
	 * Retrieves the concrete CPU object, based on the given Bochs' internal pointer \a pCPU.
	 * @param pCPU the Bochs' internal CPU object
	 * @return the FailBochs CPU representation that corresponds to Bochs' internal CPU \a pCPU
	 * @see The uses SimulatorController::getCPU().
	 */
	ConcreteCPU& detectCPU(BX_CPU_C* pCPU) const;
};

} // end-of-namespace: fail

#endif // __BOCHS_CONTROLLER_HPP__
