#pragma once

#include <iostream>
#include <Logger.hpp>
#include <string>
#include <CPFEventTypes.hpp>
#include <event_queue_interface.hpp>

/// This is a helper macro to encapsulate the general semantics of an action
/// change: set the current action variable and generate an entry action event
/// so that the entry handler will be triggered next run through the state loop.
#define ACTION_CHANGE(action_var, next_action) \
	action_var = next_action; \
	CentralEventQueue::enqueue(EntryActionEvent());

/// Convenience macro to put in place a default handler behavior for action switch/case statements
#define DEFAULT_UNEXPECTED_EVENT_HANDLER(action_name, id) \
		std::cout << "[Action: " << action_name << "] Unexpected message received, id: " << static_cast<int>(id) << std::endl;

/// Convenience macro to convert from the base event_t to the target event struct
#define CONVERT_TO_EVENT_TYPE(event, type) \
	 reinterpret_cast<const type*>(&event)

/// List of CPF System States
enum StateNames
{
	SMNotRunning = 0,
	firstState,
	secondState,
	numStates
};

/// This function will return a string corresponding to the current state's
/// numeric ID.
std::string_view getNameForStateID(StateNames id);

static const std::string stmchEngMsgID = "stmch";

/** Base Class for CPF System States
 *
 * Derived classes must implement the following functions:
 * - doStateEntry();
 * - doStateExit();
 *
 * In the derived class constructor, the following must be done:
 * 	- Define the action labels for the state
 *
 * For notes on creating a new system staet, please see docs/AddingANewState.md
 */
class StateBase
{
  public:
	/** State Entry Point
	 *
	 * The state processor will invoke enter() when transitioning into the state.
	 * This function will perform standard state transition actions. Then it will
	 * invoke doStateEntry() function, which contains each state's particular
	 * entry actions.
	 */
	void enter();

	/** State Exit Point
	 *
	 * The state processor will invoke exit() when transitioning out of the state.
	 * This function will perform standard state transition actions. Then it will
	 * invoke doStateExit() function, which contains each state's particular
	 * exit actions.
	 *
	 * @param [in] timeout Indicates whether the state is exiting due to a timeout.
	 * (TODO: GENE DOCUMENT PURPOSE)
	 */
	void exit(bool timeout);

	StateNames getStateID()
	{
		return state_id_;
	}

	/// Logs the engineering file CSV header for this module.
	static void logEngFileHeader();

	/// This function passes the event to the underlying etl::fsm class, which will pass the event
	/// to the current Action's class and invoke the proper event handler.
	/// @returns the next state, based on the current action and the event.
	StateNames process(const event_t& event);

	/// Derived classes implement this function to receive an event
	/// and process it based on the current action implementation.
	/// @param [in] event The event to be processed. Check the ID by
	///		calling event.get_message_id(). If you need data from that event,
	///		you will need to cast the message to the appropriate type to access it.
	/// @returns The name of the state to transition to. If no transition is desired,
	///	 it should return the id of the current state.
	virtual StateNames doStateActions(const event_t& event) = 0;

	void LogUnknownEvent(const event_t& msg)
	{
		(void) msg;
		Logger::logEngLine(ALWAYS, stmchEngMsgID, "Unknown event ID received: " + std::to_string(msg.get_message_id()));
	}

protected:
	/// This function is invoked by StateBase::enter().
	/// All derived class entry actions should be contained in this function.
	/// At a minimum, this should perform an ACTION_CHANGE to the first action
	/// within the state.
	/// @code
	/// ACTION_CHANGE(current_action_, getGPSPosition);
	/// @endcode
	virtual void doStateEntry() = 0;

	/// This function is invoked by StateBase::exit().
	/// All derived class exit actions should be contained in this function.
	virtual void doStateExit(bool timeout) = 0;

	/// Logging convenience function for use by state components.
	void smLogEngLine(std::string comment);

  protected:
  /// Construct a State with a name and execution timeout
  ///
  /// @param [in] label The ID for the state
  /// @param [in] timeout The timeout for the state (TODO: GENE DOCUMENT PURPOSE)
	StateBase(StateNames stateID, uint32_t timeout);

	virtual ~StateBase() = default;

	uint32_t stateTimeout;
	const StateNames state_id_;
};
