#include "ringset_control.h"

/*
 * ringset_control_action
 *
 *	This function is declared in ringset_control.h
 */
void ringset_control_action(p_smx_dll_simulation_context context_p, p_smx_dll_device device_p) {
	
	SMX_DLL_ERROR
		rv = SMX_DLL_NO_ERROR;

	p_ringset_control_default_pointers
		default_pointers_p = NULL;

	p_smx_dll_pin
		CLK_p = NULL;

	p_smx_dll_pin
		VINTH_p = NULL;

	SMX_DLL_UINT64
		input
		,
		output
		,
		RSET_MIN
		,
		RSET_MAX
	;

	s_smx_dll_bus_conversion
		conversion;

	SMX_DLL_UINT64
		wake_identifier = RINGSET_CONTROL_PERFORM_ACTION;

	SMX_DLL_BOOLEAN
		taking_care_of_business = SMX_DLL_FALSE;

	SMX_DLL_BOOLEAN
		output_changed = SMX_DLL_FALSE;

	char
		message[RINGSET_CONTROL_MESSAGE_SIZE];

#ifdef _DEBUG
//	if( IsDebuggerPresent() ) {
//		DebugBreak();
//	}
#endif

	// init
	input =
		output =
			RSET_MIN =
				RSET_MAX = 0;

	// cast default pointers
	default_pointers_p = (p_ringset_control_default_pointers)device_p->unmanaged_user_storage;

	// get pointer to CLK pin
	if( SMX_DLL_NO_ERROR != ( rv = context_p->funcs->get_pin_by_index_from_bus( default_pointers_p->input_bus_pointers_p->CLK_bus_p, 0, &( CLK_p ) ) ) ) {
		context_p->funcs->fatal_error( device_p, "Unable to retrieve pointer to CLK0 from bus CLK." );
	}

	// get pointer to VINTH pin
	if( SMX_DLL_NO_ERROR != ( rv = context_p->funcs->get_pin_by_index_from_bus( default_pointers_p->input_bus_pointers_p->VINTH_bus_p, 0, &( VINTH_p ) ) ) ) {
		context_p->funcs->fatal_error( device_p, "Unable to retrieve pointer to VINTH0 from bus VINTH." );
	}

	// setup conversion
	conversion.encoding = SMX_DLL_ENCODING_UNSIGNED;
	conversion.type     = SMX_DLL_TYPE_8BIT;

	// set MINs and MAXs depending on VINTH
	if( context_p->funcs->is_pin_logic_0( VINTH_p, &( rv ) ) ) {
		RSET_MIN = default_pointers_p->parameter_values_p->RSET_MIN_LL;
		RSET_MAX = default_pointers_p->parameter_values_p->RSET_MAX_LL;
	}
	else {
		RSET_MIN = default_pointers_p->parameter_values_p->RSET_MIN_HL;
		RSET_MAX = default_pointers_p->parameter_values_p->RSET_MAX_HL;
	}

	// read the input bus to determine what's coming in
	if( SMX_DLL_NO_ERROR != ( rv = context_p->funcs->read_bus( default_pointers_p->input_bus_pointers_p->FBTH_bus_p, &( conversion ) ) ) ) {
		context_p->funcs->fatal_error( device_p, "Unable to read_bus during ringset_control_set_initial_condition (input)." );
	}
	input = (SMX_DLL_UINT64)conversion.ubyte8;

	// read the output bus to determine our current setting
	if( SMX_DLL_NO_ERROR != ( rv = context_p->funcs->read_bus( default_pointers_p->output_bus_pointers_p->RSET_bus_p, &( conversion ) ) ) ) {
		context_p->funcs->fatal_error( device_p, "Unable to read_bus during ringset_control_set_initial_condition (output)." );
	}
	output = (SMX_DLL_UINT64)conversion.ubyte8;

	// are we taking care of (scheduled) business?
	if( SMX_DLL_WAKE_TYPE_WAKE_REQUESTED == context_p->wake_reason.wake_type ) {

		// check that we recognize the wake identifier
		if( RINGSET_CONTROL_PERFORM_ACTION == context_p->wake_reason.identifier ) {

			// set a flag
			taking_care_of_business = TRUE;

			// if scheduling is enabled...
			if( context_p->edge_events_and_scheduling_wakes_enabled ) {

				// request a wakeup at the wake interval
				if( SMX_DLL_NO_ERROR != ( rv = context_p->funcs->set_scheduled_wake( device_p, default_pointers_p->parameter_values_p->WAKE_INTERVAL, &( wake_identifier ) ) ) ) {

					// create error message
					sprintf_s(
						message
						,
						RINGSET_CONTROL_MESSAGE_SIZE
						,
						"Unable to set_scheduled_wake during ringset_control_set_initial_condition.\nrv: %d, wake_identifier: %llu, interval: %g."
						,
						rv
						,
						wake_identifier
						,
						default_pointers_p->parameter_values_p->WAKE_INTERVAL
					);

					// display error message
					context_p->funcs->fatal_error( device_p, message );
				}

			}

		}
		// uhh, we shouldn't ever get here
		else {
			context_p->funcs->fatal_error(device_p, "Unrecognized wake identifier presented during ringset_control_action.");
		}
	}

	// first, let's see if FBTH3 is high, because we're outta here if it is at any point...
	if( RINGSET_CONTROL_FBTH3_HIGH & input ) {

		// set output to RSET_MIN
		conversion.ubyte8 = (SMX_DLL_UBYTE8)RSET_MIN;

		// write output bus
		if (SMX_DLL_NO_ERROR != ( rv = context_p->funcs->write_bus(
			default_pointers_p->output_bus_pointers_p->RSET_bus_p
			,
			&( conversion )
			,
			default_pointers_p->parameter_values_p->OUTPUT_DELAY
		) ) ) {
			context_p->funcs->fatal_error( device_p, "Unable to write bus during ringset_control_action." );
		}

	}
	// otherwise, why hast thou disturbed my slumber?
	else {

		// we requested a wake up and we've been awoken to perform our default action
		if( taking_care_of_business ) {

			// by default, conversion should reflect the output
			conversion.ubyte8 = (SMX_DLL_UBYTE8)output;

			// if FBTH2 is high, decrement RSET until MIN
			if( RINGSET_CONTROL_FBTH2_HIGH & input) {
				if( output - 1 >= RSET_MIN ) {
					conversion.ubyte8 = (SMX_DLL_UBYTE8)( output - 1 );
					output_changed = TRUE;
				}
				else if( output != RSET_MIN ) {
					conversion.ubyte8 = (SMX_DLL_UBYTE8)RSET_MIN;
					output_changed = TRUE;
				}
			}
			// if FBTH1 is high, keep the output as is
			else if( RINGSET_CONTROL_FBTH1_HIGH & input) {
				// nothing to do here, conversion already reflects the output

			}
			// if FBTH0 is high, increment RSET until MAX
			else if( RINGSET_CONTROL_FBTH0_HIGH & input) {
				if( output + 1 <= RSET_MAX ) {
					conversion.ubyte8 = (SMX_DLL_UBYTE8)( output + 1 );
					output_changed = TRUE;
				}
				else if (output != RSET_MAX) {
					conversion.ubyte8 = (SMX_DLL_UBYTE8)RSET_MAX;
					output_changed = TRUE;
				}
			}
			// if nothing is high, set output to RSET_MAX
			else if( output != RSET_MAX ) {
				conversion.ubyte8 = (SMX_DLL_UBYTE8)RSET_MAX;
				output_changed = TRUE;
			}

			// check that we're in bounds, as VINTH may have changed
			if( RSET_MIN > conversion.ubyte8 ) {
				conversion.ubyte8 = (SMX_DLL_UBYTE8)RSET_MIN;
				output_changed = TRUE;
			}
			else if( RSET_MAX < conversion.ubyte8 ) {
				conversion.ubyte8 = (SMX_DLL_UBYTE8)RSET_MAX;
				output_changed = TRUE;
			}

			// write output bus
			if( output_changed ) {
				if( SMX_DLL_NO_ERROR != ( rv = context_p->funcs->write_bus(
					default_pointers_p->output_bus_pointers_p->RSET_bus_p
					,
					&( conversion )
					,
					default_pointers_p->parameter_values_p->OUTPUT_DELAY
				) ) ) {
					context_p->funcs->fatal_error( device_p, "Unable to write bus during ringset_control_action." );
				}
			}

		}

		// an input changed
		else if( SMX_DLL_WAKE_TYPE_INPUT_CHANGED == context_p->wake_reason.wake_type ) {

			// for the moment, we're not worried about when the inputs change

		}

	}

}
