/*
 * 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 <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include "debug.h"
#include "rpex.h"

int rpex_debug_level = 1;
char *log_filename = NULL;
char *settings_filename = NULL;
char *vin = NULL;
int canbus = 0;
char *ip_addr = "127.0.0.1";
int port_num = 31415;

#define RPEX_SIGNAL1 "CAN1.VehicleSpeed"
#define RPEX_SIGNAL2 "CAN1.EngineSpeed"

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>] [--logfile <filename>] [--vin <VIN-number>] [--settings <filename>] [--ip <IP address>] [--port <n>]\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);
			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("--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;
		}
		if (strcmp("--ip", argv[i]) == 0) {
			i++;
			if (i >= argc) {
				printf("IP address needed after option '--ip'.\n");
				return 0;
			}
			ip_addr = strdup(argv[i]);
			continue;
		}
		if (strcmp("--port", argv[i]) == 0) {
			i++;
			if (i >= argc) {
				printf("Port number needed after option '--port'.\n");
				return 0;
			}
			port_num = atoi(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);
	}
}

/* Set up pubsub client connection */
int setup_pubsub_connection(char *pubsub_ip, int pubsub_port) {
	int connected = 0;
	struct sockaddr_in src_addr;
	struct sockaddr_in dest_addr;
	int pubsub_socket;

	pubsub_socket = socket(AF_INET, SOCK_STREAM, 0);
	if (pubsub_socket < 0) {
		debug_msg(1, "setup_pubsub_socket: Couldn't open socket.\n");
		return -1;
	}

	src_addr.sin_family = AF_INET;
	src_addr.sin_addr.s_addr = INADDR_ANY;
	src_addr.sin_port = INADDR_ANY;

	dest_addr.sin_addr.s_addr = inet_addr(pubsub_ip);
	dest_addr.sin_family = AF_INET;
	dest_addr.sin_port = htons(pubsub_port);

	if (bind(pubsub_socket, (struct sockaddr *) &src_addr, sizeof(src_addr)) < 0) {
		debug_msg(1, "setup_pubsub_socket: bind failed.\n");
		close(pubsub_socket);
		return -1;
	}
	connected = connect(pubsub_socket, (struct sockaddr *) &dest_addr, sizeof(dest_addr));
	if (connected == -1) {
		debug_msg(1, "setup_pubsub_socket: connect failed.\n");
		close(pubsub_socket);
		return -1;
	}

	return pubsub_socket;
}

/* Read one line from socket */
int read_line(int sock, char **str) {
  int i=0, n;
  char buf[1000];

  while(i<1000) {
    n = recv(sock, buf+i, 1, 0);
		if(n != 1)
			break;
    if(buf[i]=='\n') {
      break;
    }
    if(buf[i]=='\r')
      buf[i] = 0;
    i++;
  }
  buf[i] = 0;
	if(i==0)
    *str =  NULL;
  else
  	*str = strdup(buf);

  return i;
}

void handle_signal(char *str) {
	char name[1000];
	float value;

	sscanf(str, "%s %f", name, &value);
	if(strcmp(name, RPEX_SIGNAL1)==0) 
		debug_msg(1, "The value of signal 1 is %f\n", value);
	if(strcmp(name, RPEX_SIGNAL2)==0) 
		debug_msg(1, "The value of signal 2 is %f\n", value);
}

/**
 * Program entry point
 */
int main(int argc, char **argv) {
	int ret, n;
	struct timeval timeout;
	int pubsub_socket;
	fd_set readfds;
	int ready, count=0;
	char *buf;
	char str[1000];

	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-pubsub 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 pub/sub signal handling
	pubsub_socket = setup_pubsub_connection(ip_addr, port_num);
	if(pubsub_socket<0) {
		debug_msg(1, "Pubsub connection failed\n");
		exit(-1);
	}

	// Subscribe to signals
	sprintf(str, "subscribe %s %s\n", RPEX_SIGNAL1, RPEX_SIGNAL2);
	n = send(pubsub_socket, str, strlen(str), 0);
	if(n <= 0) {
		debug_msg(1, "Error subscribing to signals\n");
		exit(-1);
	}

	// Read signals 
	while(count < 100) {
		FD_ZERO(&readfds);
		FD_SET(pubsub_socket, &readfds);
		timeout.tv_sec = 1;
		timeout.tv_usec = 0;

		ready = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
		if (ready > 0) {
			n = read_line(pubsub_socket, &buf);
    	if (n > 0) {
				// Read successful
				handle_signal(buf);
				free(buf);
				count++;
			} else {
				debug_msg(1, "Error reading signal\n");
				break;
			}
		}
	}
	
	// Disconnect
	send(pubsub_socket, "quit", 4, 0);
	close(pubsub_socket);

	debug_msg(1, "Done.\n");
	return 0;
}

