/*
 * Remote Laboratory Instrumentation Server
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * (c) 2009 - 2015 Timothy Pearson
 * Raptor Engineering
 * http://www.raptorengineeringinc.com
 */

#include <ctype.h>
#include <ctime>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <getopt.h>
#include "parameters.h"
#include "gpib_functions.h"
#include "gpib/ib.h"

#include "companalyzer_functions.h"

extern char falpha[1024];
double companalyzer_raw_trace_data[1024];

double companalyzer_frequency;

unsigned long companalyzerTraceLength (const char * companalyzerType) {
	if (strcmp("HP4191A", companalyzerType) == 0) {
		return 417;
	}
	else {
		return 1;
	}
}

int companalyzer_set_date(struct tm * datetime, const char * companalyzerType, int gpibDevice) {
	char datebuffer [80];
	strftime(datebuffer, 80, "CONF:DATE %m%d%y",datetime);

	if (strcmp("HP4191A", companalyzerType) == 0) {
		printf("[INFO] Setting date on component analyzer\n\r");
		if (strcmp("HP4191A", companalyzerType) == 0) {
			printf("[INFO] Instrument does not contain a clock, ignoring...\n\r");
			return 0;
		}
		else {
			return 1;
		}
	}
	else {
		return 1;
	}
}

int companalyzer_set_time(struct tm * datetime, const char * companalyzerType, int gpibDevice) {
	char timebuffer [80];
	strftime(timebuffer, 80, "CONF:TIME +%H.%M",datetime);	// FIXME wrong format

	if (strcmp("HP4191A", companalyzerType) == 0) {
		printf("[INFO] Setting time on component analyzer\n\r");
		if (strcmp("HP4191A", companalyzerType) == 0) {
			printf("[INFO] Instrument does not contain a clock, ignoring...\n\r");
			// Since this function is used to detect instrument presence, reset the instrument displays here...
			snprintf(timebuffer, 80, "A1B1");
			#ifdef ENABLE_EXTRA_DEBUGGING
			printf("[DEBG] Writing: %s\n\r", timebuffer);
			#endif
			if (gpib_write(gpibDevice, timebuffer) == 0) {
				char errorbuf[1000];
				return companalyzer_set_measurement_frequency(1000000, companalyzerType, gpibDevice, errorbuf);
			}
			else {
				return 2;
			}
			return 0;
		}
		else {
			return 1;
		}
	}
	else {
		return 1;
	}
}

int companalyzer_lock_local_controls (const char * companalyzerType, int gpibDevice) {
	if (strcmp("HP4191A", companalyzerType) == 0) {
		printf("[INFO] Locking component analyzer local controls\n\r");
		if (strcmp("HP4191A", companalyzerType) == 0) {
			#ifdef ENABLE_EXTRA_DEBUGGING
			printf("[DEBG] Setting REN\n\r");
			#endif
			if (ibsre(gpibDevice, 1) == 0) {
				return 0;
			}
			else {
				return 2;
			}
		}
		else {
			return 1;
		}
	}
	else {
		return 1;
	}
}

int companalyzer_set_measurement_parameters(companalyzer_measurement::companalyzer_measurement_t parameter_a, companalyzer_measurement::companalyzer_measurement_t parameter_b, const char * companalyzerType, int gpibDevice, char * extendedError) {
	char first_parameter[1024];

	if ((strcmp("HP4191A", companalyzerType) == 0)) {
		printf("[INFO] Setting component analyzer measurement parameters to types %d:%d\n\r", parameter_a, parameter_b);
		if (strcmp("HP4191A", companalyzerType) == 0) {
			falpha[0] = 0;
			first_parameter[0] = 0;
			if (parameter_a == companalyzer_measurement::resistance) {
				sprintf(first_parameter, "A4");
			}
			else if (parameter_a == companalyzer_measurement::conductance) {
				sprintf(first_parameter, "A5");
			}
			else if (parameter_a == companalyzer_measurement::inductance) {
				sprintf(first_parameter, "A7");
			}
			else if (parameter_a == companalyzer_measurement::capacitance) {
				sprintf(first_parameter, "A8");
			}
			else if (parameter_a == companalyzer_measurement::impedance) {
				sprintf(first_parameter, "A1");
			}
			else if (parameter_a == companalyzer_measurement::admittance) {
				sprintf(first_parameter, "A2");
			}
			else if (parameter_a == companalyzer_measurement::reflection_absolute) {
				sprintf(first_parameter, "A3");
			}
			else if (parameter_a == companalyzer_measurement::reflection_x) {
				sprintf(first_parameter, "A6");
			}

			if (first_parameter[0] != 0) {
				if (parameter_b == companalyzer_measurement::resistance) {
					sprintf(falpha, "%sB1", first_parameter);
				}
				else if (parameter_b == companalyzer_measurement::conductance) {
					sprintf(falpha, "%sB2", first_parameter);
				}
				else if (parameter_b == companalyzer_measurement::dissipation_factor) {
					sprintf(falpha, "%sB3", first_parameter);
				}
				else if (parameter_b == companalyzer_measurement::quality_factor) {
					sprintf(falpha, "%sB4", first_parameter);
				}
				else if (parameter_b == companalyzer_measurement::phase_angle_deg) {
					sprintf(falpha, "%sB1", first_parameter);
				}
				else if (parameter_b == companalyzer_measurement::phase_angle_rad) {
					sprintf(falpha, "%sB2", first_parameter);
				}
			}
			if (strlen(falpha) == 0) {
				sprintf(extendedError, "EXTAn invalid parameter or combination of parameters was requested°");
				return 2;
			}
			else {
				#ifdef ENABLE_EXTRA_DEBUGGING
				printf("[DEBG] Writing: %s\n\r", falpha);
				#endif
				if (gpib_write(gpibDevice, falpha) == 0) {
					return 0;
				}
				else {
					sprintf(extendedError, "EXTAn unknown communications error has occured!°");
					return 2;
				}
			}
		}
		else {
			sprintf(extendedError, "EXTAn invalid device was selected!°");
			return 1;
		}
	}
	else {
		sprintf(extendedError, "EXTAn invalid device was selected!°");
		return 1;
	}
}

int companalyzer_get_max_measurement_frequency(double * frequency, const char * companalyzerType, int gpibDevice, char * extendedError) {
	if ((strcmp("HP4191A", companalyzerType) == 0)) {
		printf("[INFO] Getting maximum component analyzer measurement frequency\n\r");
		if (strcmp("HP4191A", companalyzerType) == 0) {
			*frequency = 1000000000;
			return 0;
		}
		else {
			sprintf(extendedError, "EXTAn invalid device was selected!°");
			return 1;
		}
	}
	else {
		sprintf(extendedError, "EXTAn invalid device was selected!°");
		return 1;
	}
}

int companalyzer_get_min_measurement_frequency(double * frequency, const char * companalyzerType, int gpibDevice, char * extendedError) {
	if ((strcmp("HP4191A", companalyzerType) == 0)) {
		printf("[INFO] Getting minimum component analyzer measurement frequency\n\r");
		if (strcmp("HP4191A", companalyzerType) == 0) {
			*frequency = 1000000;
			return 0;
		}
		else {
			sprintf(extendedError, "EXTAn invalid device was selected!°");
			return 1;
		}
	}
	else {
		sprintf(extendedError, "EXTAn invalid device was selected!°");
		return 1;
	}
}

int companalyzer_set_measurement_frequency(double frequency, const char * companalyzerType, int gpibDevice, char * extendedError) {
	if ((strcmp("HP4191A", companalyzerType) == 0)) {
		printf("[INFO] Setting component analyzer measurement frequency to %eMHz\n\r", frequency);
		if (strcmp("HP4191A", companalyzerType) == 0) {
			falpha[0] = 0;

			double adjusted_frequency = frequency / 1000000.0;

			if ((adjusted_frequency >= 1.0) && (adjusted_frequency <= 1000.0)) {
				sprintf(falpha, "FR%04.04fEN", adjusted_frequency);
			}
			if (strlen(falpha) == 0) {
				sprintf(extendedError, "EXTAn invalid parameter or combination of parameters was requested°");
				return 2;
			}
			else {
				#ifdef ENABLE_EXTRA_DEBUGGING
				printf("[DEBG] Writing: %s\n\r", falpha);
				#endif
				if (gpib_write(gpibDevice, falpha) == 0) {
					companalyzer_frequency = frequency;
					return 0;
				}
				else {
					sprintf(extendedError, "EXTAn unknown communications error has occured!°");
					return 2;
				}
			}
		}
		else {
			sprintf(extendedError, "EXTAn invalid device was selected!°");
			return 1;
		}
	}
	else {
		sprintf(extendedError, "EXTAn invalid device was selected!°");
		return 1;
	}
}

int companalyzer_get_parameter_measurement(companalyzer_measurements_t * retval, const char * companalyzerType, int gpibDevice) {
	char segarray[1024];
	char meas_a_string[1024];
	char meas_b_string[1024];
	int i;
	long ai;
	long left_char;
	long right_char;
	char* display_data_string;

	int max_num_bytes = 0;
	double parameter_value;
	companalyzer_measurements_t measurement;
	companalyzer_status::companalyzer_status_t parameter_status;
	companalyzer_measurement_type::companalyzer_measurement_type parameter_type;

	if ((strcmp("HP4191A", companalyzerType) == 0)) {
		printf("[INFO] Getting component analyzer measurement\n\r");

		// Read measurement
		max_num_bytes = 512;	// Request more bytes than are possible to ensure no bytes are left behind
		#ifdef ENABLE_EXTRA_DEBUGGING
		printf("[DEBG] Trying to read %i bytes from GPIB device...\n", max_num_bytes);
		#endif

		ibtmo(gpibDevice, T10s);
		ibeos(gpibDevice, 0x0);

		ai = gpib_read_array(gpibDevice, max_num_bytes, segarray);
		if (ai == -1) {
			return 1;
		}
		else {
			if (strcmp("HP4191A", companalyzerType) == 0) {
				segarray[ai] = 0;

				left_char = 1;
				right_char = 0;
				for (right_char=left_char;right_char<ai;right_char++) {
					if (segarray[right_char] == ',') {
						break;
					}
				}
				strncpy(meas_a_string, segarray+left_char, right_char-left_char);
				meas_a_string[right_char-left_char] = 0;

				left_char = right_char + 1;
				for (right_char=left_char;right_char<ai;right_char++) {
					if (segarray[right_char] == '\r') {
						break;
					}
				}
				strncpy(meas_b_string, segarray+left_char, right_char-left_char);
				meas_b_string[right_char-left_char] = 0;

				// Parse display data strings
				for (i = 0; i < 2; i++) {
					if (i == 0) {
						display_data_string = meas_a_string;
					}
					else {
						display_data_string = meas_b_string;
					}

					switch (display_data_string[0]) {
						case 'N':
							parameter_status = companalyzer_status::ok;
							break;
						case 'O':
							parameter_status = companalyzer_status::overflow;
							break;
						case 'D':
							parameter_status = companalyzer_status::invalid;
							break;
						case 'U':
							parameter_status = companalyzer_status::cal_req;
							break;
					}

					if (i == 0) {
						switch (display_data_string[1]) {
							case 'Z':
								measurement.parameter_a = companalyzer_measurement::impedance;
								break;
							case 'Y':
								measurement.parameter_a = companalyzer_measurement::admittance;
								break;
							case 'M':
								measurement.parameter_a = companalyzer_measurement::reflection_absolute;
								break;
							case 'R':
								measurement.parameter_a = companalyzer_measurement::resistance;
								break;
							case 'G':
								measurement.parameter_a = companalyzer_measurement::conductance;
								break;
							case 'X':
								measurement.parameter_a = companalyzer_measurement::reflection_x;
								break;
							case 'L':
								measurement.parameter_a = companalyzer_measurement::inductance;
								break;
							case 'C':
								measurement.parameter_a = companalyzer_measurement::capacitance;
								break;
						}
					}
					else {
						switch (display_data_string[1]) {
							case 'D':
								measurement.parameter_b = companalyzer_measurement::phase_angle_deg;
								break;
							case 'R':
								measurement.parameter_b = companalyzer_measurement::phase_angle_rad;
								break;
							case 'X':
								measurement.parameter_b = companalyzer_measurement::reactance;
								break;
							case 'B':
								measurement.parameter_b = companalyzer_measurement::susceptance;
								break;
							case 'Y':
								measurement.parameter_b = companalyzer_measurement::reflection_y;
								break;
// 							case 'R':
// 								measurement.parameter_b = companalyzer_measurement::resistance;
// 								break;
							case 'G':
								measurement.parameter_b = companalyzer_measurement::conductance;
								break;
// 							case 'D':
// 								measurement.parameter_b = companalyzer_measurement::dissipation_factor;
// 								break;
							case 'Q':
								measurement.parameter_b = companalyzer_measurement::quality_factor;
								break;
						}
					}

					switch (display_data_string[2]) {
						case 'N':
							parameter_type = companalyzer_measurement_type::normal;
							break;
						case 'D':
							parameter_type = companalyzer_measurement_type::deviation;
							break;
						case 'P':
							parameter_type = companalyzer_measurement_type::deviation_percent;
							break;
					}

					parameter_value = atof(display_data_string + 3);

					if (i == 0) {
						measurement.parameter_a_status = parameter_status;
						measurement.parameter_a_type = parameter_type;
						measurement.parameter_a_value = parameter_value;
					}
					else {
						measurement.parameter_b_status = parameter_status;
						measurement.parameter_b_type = parameter_type;
						measurement.parameter_b_value = parameter_value;
					}
				}
				measurement.frequency = companalyzer_frequency;

				*retval = measurement;
			}
			else {
				return 2;
			}
		}
	
		ibtmo(gpibDevice, T10s);
	
		#ifdef ENABLE_EXTRA_DEBUGGING
		printf("[DEBG] Read %li bytes from GPIB device\n", ai);
		#endif

		return 0;
	}
	else {
		return -1;
	}
}