//
// Copyright (C) ValoFly GmbH - All Rights Reserved
//

#include <TetherCom/TetherCom.h>

#include <string>
#include <vector>
#include <iostream>

void printHeader()
{
	std::cout << "###########################################" << std::endl;
	std::cout << "# ValoFly TetherCom Library usage example #" << std::endl;
	std::cout << "###########################################" << std::endl << std::endl;
	std::cout << "This example applications shows, how ValoFly TetherCom library can be used." << std::endl << std::endl << std::endl;
}

/**
* Print help
*/
void printHelp()
{
	printHeader();
    
    VALOFLY::TetherCom tetherCom;
	tetherCom.printVersionDetails();
}

/**
* Parse given arguments of program call
*
* Parse given arguments and call help function if help flag is part of given arguments
*
* @throw "Runtime Error" Throws runtime error on unknown flags or if help function was called
*/
void parseArguments(int argc, char* argv[])
{
	int configArgPos = -1;
	std::string configFile;

	for (int i = 0; i < argc; i++)
	{
		if (std::string(argv[i]).compare("-h") == 0 || std::string(argv[i]).compare("/h") == 0)
		{
			printHelp();
			std::exit(EXIT_SUCCESS);
		}
	}
}

/**
* Example of TetherCom library usage
*
* This is an example application showing usage of the TetherCom library.
*
* It shows:
*   - create an instance of library object
*   - get list of serial devices and print them
*   - open a connection to ValoFly Tether.Solutions ground station
*   - request current system status information and print them
*   - setup a configuration parameter set and transmit it to ValoFly Tether.Solutions ground station
*   - set single configuration parameters
*   - activate and deactivate ValoFly Tether.Solutions ground station
*   - close connection to ValoFly Tether.Solutions ground station
*/
int main(int argc, char* argv[])
{    
	// new instance of TetherCom object which handle the connection to the tether ground station
	VALOFLY::TetherCom tetherCom;

	// catch help flag
	try {
		parseArguments(argc, argv);
	}
	catch (const std::exception & ex)
	{
		std::cout << "Failed to init TetherCom application!" << std::endl;
		std::cout << ex.what() << std::endl;
		return EXIT_FAILURE;
	}

	printHeader();
    
    // print version details of used TetherCom Library to standard out
    tetherCom.printVersionDetails();

	// get a list (vector of string) of available serial ports, with its description and hardware ID
	std::vector<std::string> serialDevListPort;
	std::vector<std::string> serialDevListDesc;
	std::vector<std::string> serialDevListHwID;
	tetherCom.getSerialPortsInfo(serialDevListPort, serialDevListDesc, serialDevListHwID);

	// print all available serial ports, with its description and hardware ID to current standard output
    std::cout << "Available serial ports:" << std::endl;
	tetherCom.printSerialPortList();
	    
	// define serial port to connect with by user input
	std::string portName;
	std::cout << std::endl << "Type COM port name to connect." << std::endl;
	std::cin >> portName;

	std::cout << std::endl << "Try to connect to " << portName << "." << std::endl;

	// try to establish serial connection to tether ground station
	try
	{
		tetherCom.openConnection(portName);
	}
	catch (std::exception ex)
	{
		printf("%s", ex.what());
		return EXIT_FAILURE;
	}
	std::cout << "Open serial port \"" << portName << "\" successfull!" << std::endl;

	// configure individual connection timeout in milli seconds to prevent communication issues
	tetherCom.setConnectionTimeout(400);
	
	VALOFLY::TetherCom::systemStatus status;
	try {
		// get current state of tether ground station
		std::cout << "Request tether system status..." << std::endl;
		tetherCom.getSystemStatus(status);


		// print current state of tether ground station to given output stream
		tetherCom.print(status, std::cout);


		// set config of tether ground station
        // VALOFLY::TetherCom::systemConfig struct is designed to be bidirectional,
        // but struct field cableRetractionStatus is not setable by user and a read only value transmitted
        // by ValoFly Tether.Solutions Ground Station.
        // so it could be undefined at initialisation of a systemConfig struct for set Tether.Solutions Ground Station configuration.
		VALOFLY::TetherCom::systemConfig config;
		config.cableRetractionStartLength = 5.67f; // [m], precision 0.01m
		config.cableRetractionTorque = 50; // %
		config.systemActivation = false; // true/false => on/off
		tetherCom.setSystemConfig(config);
        
        // set config with single values:
        tetherCom.setSystemConfig(config.systemActivation, config.cableRetractionTorque, config.cableRetractionStartLength);


		// set single config parameters - start length of cable retraction
        // it is not neccessary to create/set/update a VALOFLY::TetherCom::systemConfig object
		// configuration values can setted directly
        config.cableRetractionStartLength = 4.21f; // [m], precision 0.01m
        tetherCom.setCableRetractionStartLength(config.cableRetractionStartLength);
        

		// set single config parameters - torque of cable retraction in percent
		tetherCom.setCableRetractionTorque(40); // %

		// set single config parameters - set system activation
		if (config.systemActivation)
			std::cout << "Activate tether system..." << std::endl;
		else
			std::cout << "Deactivate tether system..." << std::endl;

		tetherCom.setSystemActivation(config.systemActivation);


		// enable tether ground station
		std::cout << "Activate tether system..." << std::endl;
		tetherCom.setSystemActivation(true);


		// disable tether ground station
		std::cout << "Deactivate tether system..." << std::endl;
		tetherCom.setSystemActivation(false);
        
        
        // set an out of range value and handle exception
        // lower than valid minimal retraction start length
        float startLength = 0.01f;
        try
        {
            tetherCom.setCableRetractionStartLength(startLength); // [m], precision 0.01m
        }
        catch (VALOFLY::ValueException& ex)
        {
            if (startLength < tetherCom.getMinimalCableRetractionStartLength())
            {
                startLength = tetherCom.getMinimalCableRetractionStartLength();
                tetherCom.setCableRetractionStartLength(startLength); // [m], precision 0.01m
            }
        }
        
        // higher then valid maximal retraction torque
        unsigned int torque = 120;
        try
        {
            tetherCom.setCableRetractionTorque(torque); // %
        }
        catch (VALOFLY::ValueException& ex)
        {
            if (torque < tetherCom.getMinimalCableRetractionTorque())
            {
                torque = tetherCom.getMinimalCableRetractionTorque();
                tetherCom.setCableRetractionTorque(torque); // %
            }
            else if (torque > tetherCom.getMaximalCableRetractionTorque())
            {
                torque = tetherCom.getMaximalCableRetractionTorque();
                tetherCom.setCableRetractionTorque(torque); // %
            }
        }


		// ValoFly Tether.Solutions Systems have a lot of parameters to describe the current system state.
		// To determine if there is any warnings or errors without to know which one occurred specific, it can directly requested from the system
		VALOFLY::TetherComDefinitions::systemError errorState;
		if (tetherCom.checkSystemStatus(errorState))
		{
			// warning or error occurred
			if (errorState == VALOFLY::TetherComDefinitions::systemError::generic_error)
				printf("Some errors occored!\n");
			else if (errorState == VALOFLY::TetherComDefinitions::systemError::generic_warning)
				printf("Some warnings occored!\n");
		}
		else
			printf("System operates between its limits.");
		
		
		// it is also possible to process an already requested VALOFLY::TetherCom::systemStatus struct and specify if maintenance information should be part of the analysis
		// if maintenance should be analysed, systems details will directly requested from Tether.SolutionsSystem
		tetherCom.getSystemStatus(status);
		if (tetherCom.checkSystemStatus(status, errorState, false))
		{
			// warning or error occurred
			if (errorState == VALOFLY::TetherComDefinitions::systemError::generic_error)
				printf("Some errors occored!\n");
			else if (errorState == VALOFLY::TetherComDefinitions::systemError::generic_warning)
				printf("Some warnings occored!\n");
		}
		else
			printf("System operates between its limits.");


		// if systemStatus Struct and systemDetails struct are already requested, error and warning check can be done by processing these two structs directly
		VALOFLY::TetherCom::systemDetails details;
		tetherCom.getSystemStatus(status);
		tetherCom.getSystemDetails(details);

		if (tetherCom.checkSystemStatus(status, details, errorState))
		{
			// warning or error occurred
			if (errorState == VALOFLY::TetherComDefinitions::systemError::generic_error)
				printf("Some errors occored!\n");
			else if (errorState == VALOFLY::TetherComDefinitions::systemError::generic_warning)
				printf("Some warnings occored!\n");
		}
		else
		printf("System operates between its limits.");


		// to gether system health data in more detail, following data objects can be checked

		// over all system error states
		// status.sysStates.error => VALOFLY::TetherComDefinitions::systemError::generic_error
		//							 VALOFLY::TetherComDefinitions::systemError::generic_warning
		//							 VALOFLY::TetherComDefinitions::systemError::no_error
		
		// cable warning / error states
		// status.cable.tetherStatus => VALOFLY::TetherComDefinitions::cableTetherStatus::ok
		//								VALOFLY::TetherComDefinitions::cableTetherStatus::failure
		//								VALOFLY::TetherComDefinitions::cableTetherStatus::max_length
		//								VALOFLY::TetherComDefinitions::cableTetherStatus::warning_length

		// cable guide error states
		// status.cable.guideStatus => VALOFLY::TetherComDefinitions::cableGuideStatus::ok
		//							   VALOFLY::TetherComDefinitions::cableGuideStatus::motor_failure
		//							   VALOFLY::TetherComDefinitions::cableGuideStatus::not_available

		// cable reel error states
		// status.cable.reelStatus => VALOFLY::TetherComDefinitions::cableReelStatus::ok
		//							  VALOFLY::TetherComDefinitions::cableReelStatus::motor_failure
		//							  VALOFLY::TetherComDefinitions::cableReelStatus::not_available

		// environment measurement system error states
		// if one of available fans failed, system is in error state
		// status.env.fan1Status | status.env.fan2Status => VALOFLY::TetherComDefinitions::fanStatus::ok
		//													VALOFLY::TetherComDefinitions::fanStatus::failure
		//													VALOFLY::TetherComDefinitions::fanStatus::not_available

		// Temperature / Humidity / Pressure 
		// status.env.envSensor1Status | status.env.envSensor2Status | status.env.envSensor3Status | status.env.envSensor4Status
		//			=> VALOFLY::TetherComDefinitions::envSensorStatus::ok
		//			   VALOFLY::TetherComDefinitions::envSensorStatus::sensor_failure
		//			   VALOFLY::TetherComDefinitions::envSensorStatus::temp_failure
		//			   VALOFLY::TetherComDefinitions::envSensorStatus::humidity_failure
		//			   VALOFLY::TetherComDefinitions::envSensorStatus::pressure_failure
		//			   VALOFLY::TetherComDefinitions::envSensorStatus::not_available

		// Tether Ground Station energy system error states
		// Main supply | Control system supply
		// status.energyGS.mainsStatus | status.energyGS.ctrlSupplyStatus => VALOFLY::TetherComDefinitions::supplyStatus::ok
		//																	 VALOFLY::TetherComDefinitions::supplyStatus::general_error
		//																	 VALOFLY::TetherComDefinitions::supplyStatus::range_error
		//																	 VALOFLY::TetherComDefinitions::supplyStatus::not_available

		// Externel UAV supply system error states
		// (Supply rail from Tether Ground Station to UAV)
		// status.energyUAV.extSupplyStatus => VALOFLY::TetherComDefinitions::extUAVSupplyStatus::supply_error
		//									   VALOFLY::TetherComDefinitions::extUAVSupplyStatus::load_error
		//									   VALOFLY::TetherComDefinitions::extUAVSupplyStatus::inactive
		//									   VALOFLY::TetherComDefinitions::extUAVSupplyStatus::pri_starting
		//									   VALOFLY::TetherComDefinitions::extUAVSupplyStatus::pri_running
		//									   VALOFLY::TetherComDefinitions::extUAVSupplyStatus::sec_starting
		//									   VALOFLY::TetherComDefinitions::extUAVSupplyStatus::sec_running
		//									   VALOFLY::TetherComDefinitions::extUAVSupplyStatus::stopping

		// check if maintenance is reuired
		// details.maintenance_required => 0 ... no maintenance required | 1 ... maintenance required

		// close connection to tether ground station and disconnect from serial port
		tetherCom.closeConnection();
	}
	catch (VALOFLY::IOException& ex)
	{
        // IO exceptions are thrown on issues with the IO interface or pyhsical communication layer
		printf("Error - IOException occurred!\n");
		printf("%s\n", ex.what());
		std::cout << "Close connection..." << std::endl;
		tetherCom.closeConnection();
		return EXIT_FAILURE;
	}
	catch (VALOFLY::DataException& ex)
	{
        // Data exceptions are thrown on issues with the transmitted data.
        // For example if received data is corrupted, doesn't match to the protocol, respones doesn't match to requests and so on
		printf("Error - DataException occurred!\n");
		printf("%s\n", ex.what());
		std::cout << "Close connection..." << std::endl;
		tetherCom.closeConnection();
		return EXIT_FAILURE;
	}
    catch (VALOFLY::ValueException& ex)
	{
        // Value exceptions are thrown on issues with processed data content
        // For example if values given by user are out of specified range, received values doesn't fit to expected values and so on
		printf("Error - ValueException occurred!\n");
		printf("%s\n", ex.what());
		std::cout << "Close connection..." << std::endl;
		tetherCom.closeConnection();
		return EXIT_FAILURE;
	}
	catch (std::exception& ex)
	{
		printf("Error - general exception occurred!\n");
		printf("%s\n", ex.what());
		std::cout << "Close connection..." << std::endl;
		tetherCom.closeConnection();
		return EXIT_FAILURE;
	}
}