#include <stdio.h>
#include <unistd.h>

#include "../hubolib.h"
#include "../hubocfg.h" // Required for changing default I2C device.

using namespace HuboLib;

/*
Compile and link: 
	g++ SetDigitalOutput.cpp -L../ -lhubo -lpthread -lrt -o SetDigitalOutput
Run:
	sudo ./SetDigitalOutput
Purpose:
	Turns on/off a single output as specified in the command line. 
*/

void BoostThreadPriority();

int main(int argc, char* argv[])
{
	// Lets have this program finish as soon as possible.
	BoostThreadPriority();

	if (argc != 3)
	{
		printf ("Usage: \n  SetDigitalOutput <digital output to activate>\n  example: SetDigitalOutput 7 0  // this will set channel 7 to 0\n");
		return 0;
	}

	int channel = 0;
	if (sscanf (argv[1], "%d", &channel) != 1)
	{
		printf ("Digital output could not be determined!\n");
		return 0;
	}

	int outValue = 0;
	if (sscanf (argv[2], "%d", &outValue) != 1)
	{
		printf ("Value for digital output could not be determined!\n");
		return 0;
	}

	if (outValue<0 || outValue>1)
	{
		printf ("Value for digital output must be 0 or 1!\n");
		return 0;
	}

	printf ("Setting digital output %d to %d.\n", channel, outValue);
	
	// If required - set the I2C device to work with. The Raspberry Pi uses "/dev/i2c-1" which is default, the Banana Pi uses "/dev/i2c-0"
	#ifdef BPI
	    g_I2CConfig.m_sI2CDevice = "/dev/i2c-0";
	#endif

	// Initialize the library once in your program.
	if (!Initialize())
	{
		printf ("Error: Initialize\n");
		return 0;
	}
	
	// Set the cycle time to 2ms (50Hz).
	Set_Cycle_Time(20);
	
	// Wait until the output latch buffer is filled.
	Wait_For_MCP23017_Buffered_Values();

	unsigned char read_out_value, channel_values;
	Readback_DO_Channels (channel_values);

	printf ("Old channel values x%02X\n", channel_values);

	if (outValue)
		channel_values |=  (1 << channel);
	else
		channel_values &= ~(1 << channel);

	printf ("New channel values x%02X\n", channel_values);
	
	// Set the outputs.
	Set_DO_Channels (channel_values);

	// For the given cycle time 20ms wait should be sufficient!
	bool bResult = false;
	for (int retry=0; retry<10; retry++)
	{
		usleep(2L*1000L);
		Readback_DO_Channels (read_out_value);
		if (read_out_value == channel_values)
		{
			bResult = true;
			break;
		}
	}

	// Free library resources.
	Uninitialize();
	
	return bResult ? 1 : 0;
}

#include <pthread.h>    
#include <assert.h>    
void BoostThreadPriority()
{
	// Scheduling params.
	sched_param param;
	int			policy;
	int			ret;
	pthread_t	threadHandle = pthread_self();

	// Get current scheduling parameters of thread.
	ret				= pthread_getschedparam (threadHandle, &policy, &param);
	assert (ret == 0);

	// Set scheduling parameters of thread to real time values (FIFO scheduling type and max prio).
	policy				 = SCHED_FIFO; // SCHED_RR;
	param.sched_priority = sched_get_priority_max(policy); // New max priority for new scheduling concept.
	ret					 = pthread_setschedparam(threadHandle, policy, &param);
	assert (ret == 0);
}
