/*
 * rpex.c
 *
 *  Created on: May 31, 2017
 *      Author: mathias
 */
#include <stdio.h>
#include <stdlib.h>
#define _GNU_SOURCE
#define __USE_GNU
#include <unistd.h>
#include <libgen.h>
#include <string.h>
#include "alkit-time-utils/utils.h"
#include "alkit-debug/debug.h"
#include "alkit-canwrapper/src/canwrapper.h"
#include "rpex.h"

int rpex_debug_level = 1;
char *log_filename = NULL;
char *settings_filename = NULL;
char *vin = NULL;
int canbus = 0;

void invalid_options(char *opt) {
  printf("Invalid options specified - '%s' does not combine with previous options.\n", opt);
}
void usage(char *name) {
	printf("usage: %s -h        - display this help\n", name);
  printf("       %s -v        - show program version\n", name);
  printf("       %s [-d <debug level>] [-can <bus_no>] [-logfile <filename>] [-vin <VIN-number>] [-settings <filename>]\n", name);
}

int parse_options(int argc, char **argv) {
	int i, decision_is_made = 0;
	char myname[100];
	char *tmp;

	tmp = basename(argv[0]);
	strcpy(myname, tmp);
	for (i=1; i<argc; i++) {
		if (strcmp("-h", argv[i]) == 0) {
			if (decision_is_made) {
				invalid_options(argv[i]);
				return 0;
			}
			usage(myname);
			return 0;
		}
		if (strcmp("-v", argv[i]) == 0) {
			if (decision_is_made) {
				invalid_options(argv[i]);
				return 0;
			}
			printf("%s version is %s\n", myname, RPEX_VERSION_STRING);
			printf("Alkit canwrapper version is %d\n", get_alkit_canwrapper_version());
			exit(0);
		}
		if (strcmp("-d", argv[i]) == 0) {
			i++;
			if (i >= argc) {
				printf("Debug level is needed.\n");
				return 0;
			}
			rpex_debug_level = atoi(argv[i]);
			if(rpex_debug_level < 0)
				rpex_debug_level = 0;
			continue;
		}
		if (strcmp("-can", argv[i]) == 0) {
			i++;
			if (i >= argc) {
				printf("CAN bus number needed.\n");
				return 0;
			}
			canbus = atoi(argv[i]);
			continue;
		}
		if (strcmp("-logfile", argv[i]) == 0) {
			i++;
			if (i >= argc) {
				printf("Log file name is needed after option '-logfile'.\n");
				return 0;
			}
			log_filename = strdup(argv[i]);
			continue;
		}
		if (strcmp("-settings", argv[i]) == 0) {
			i++;
			if (i >= argc) {
				printf("Settings file name is needed after option '-settings'.\n");
				return 0;
			}
			settings_filename = strdup(argv[i]);
			continue;
		}
		if (strcmp("-vin", argv[i]) == 0) {
			i++;
			if (i >= argc) {
				printf("VIN number is needed after option '-vin'.\n");
				return 0;
			}
			vin = strdup(argv[i]);
			continue;
		}
		printf("Unknown option %s\n", argv[i]);
		usage(myname);
		return 0;
	}

	return 1;
}

void read_settings(char *filename) {
	FILE *f;
	char line[500];
	char *p;

	f = fopen(filename, "r");
	if (f == NULL) {
    debug_msg(1, "Could not open settings file %s.\n", filename);
    return;
  }
	while (!feof (f)) {
		p = fgets(line, 500, f);
		if (p == NULL) 
			break;
		debug_msg(1, "Read line from settings file: %s\n", line);
	}
}

void handle_can_frame(candata_t *frame) {
	int i;

	debug_msg(1, "CAN frame read, id is 0x%03x\n", frame->id);
	debug_msg(1, "CAN data bytes: ");
	for(i=0; i<frame->databyte_count; i++) {
		debug_msg_no_time(1, "%02x ", frame->databytes[i]);
	}
	debug_msg_no_time(1, "\n");
}

/**
 * Program entry point
 */
int main(int argc, char **argv) {
	int ret, n;
	int ecu_id = 0x711;
	int can_filter_id = 0x611;
	int can_filter_mask = 0xfff;
	struct timeval t, timeout;
	int can_device_fd;
	fd_set readfds;
	int ready;
	candata_t req_frame, resp_frame;

	debug_enable(NULL);
	// parse parameters
	ret = parse_options(argc, argv);
	if (ret != 1) {
		return EXIT_FAILURE;
	}

	set_debug_level(rpex_debug_level);
	if (log_filename != NULL ) {
		debug_enable(log_filename);
	}

	debug_msg(1, "rpex version is %s\n", RPEX_VERSION_STRING);
	debug_msg(2, "Build date is %s, at %s\n", __DATE__, __TIME__);
	debug_msg(1, "Debug level is %d\n", rpex_debug_level);

	if(vin) 
		debug_msg(1, "VIN number of vehicle is %s\n", vin);

	if(settings_filename)
		read_settings(settings_filename);

	// Initialize CAN interface
	ret = init_can(canbus, DEFAULT_CAN_DEVICE);
	can_device_fd = get_can_fd(canbus);

	// Set up CAN filter
	//init_CAN_filters();
 install_CAN_filter(canbus, 0, CAN_FILTER_TYPE_PASS, can_filter_id, can_filter_mask);

	// Send CAN request
	memset(&req_frame, 0, sizeof(candata_t));
  req_frame.id = ecu_id;
  req_frame.bus_nr = canbus;
  req_frame.frame_type = CAN_FRAME_11_BIT;
	memset(req_frame.databytes, 0, 8);
	req_frame.databytes[0] = 0x02; // SingleFrame, length = 2
	req_frame.databytes[1] = 0x10; // Service 10
	req_frame.databytes[2] = 0x01; // Default Diagnostic Session
	req_frame.databyte_count = 8; // With padding

	debug_msg(1, "Sending diagnostic request on CAN bus %d...\n", canbus);
	n = write_can(canbus, &req_frame, &t);
	if(n<0) {
		debug_msg(1, "Error writing CAN frame\n");
		return -1;
	}

	// Read CAN response
	FD_ZERO(&readfds);
	FD_SET(can_device_fd, &readfds);
	timeout.tv_sec = 0;
	timeout.tv_usec = 200000; // 200 ms

	ready = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
	if (ready > 0) {
		ret = read_can(canbus, &resp_frame);
    if (ret == CAN_STATUS_OK) {
			// Read successful
			handle_can_frame(&resp_frame);
		} else {
			debug_msg(1, "Error reading CAN frame\n");
		}
	} else {
		debug_msg(1, "No response.\n");
	}
	
	debug_msg(1, "Done.\n");
	return 0;
}

