/*
 * main.cpp
 *
 *  Created on: Nov 2, 2015
 *      Author: Philipp Hinz
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdint.h>
#include <wiringPi.h>
#include <pthread.h>
#include <unistd.h>
#include <iostream>
#include <csignal>
#include "global.h"
#include "timer.h"
#include "database.h"
#include "logger.h"
#include "lcd.h"
#include "hal.h"
#include "stripe.h"
#include "coffee.h"
#include "display.h"
#include "server.h"
#include "events.h"
#include "DS18B20.h"

const int buildno = (1+(int)(
#include "buildno"
		));

int verbose = 0;
bool optDate = false;
bool optPower = false;
bool optNoNet = false;

void *mainLoop(void *threadid);
void terminationHandler(int signum);
void usr1Handler(int signum);
void hupHandler(int signum);
pthread_t thread[4];
pthread_mutex_t mutex_spi;
pthread_mutex_t mutex_i2c;
pthread_mutex_t mutex_logger;

int main(int argc, char *argv[]) {
	pthread_attr_t attr;
	pthread_mutexattr_t mutexattr;
	void *status;
	int rc;
	int opt;
	int prev_ind;

	logger(V_NONE, LOG_INFO, "CoffeePi by Philipp Hinz and Sebastian Vendt - "
			"Build number %d\n", buildno);

	// Argument handling
	while (prev_ind = optind, (opt = getopt(argc, argv, "?hvVdpn")) != -1) {
		if (argc > 3)
			if (optind == prev_ind + 2 && *optarg == '-') {
				opt = '?';
				--optind;
			}
		switch (opt) {
		case 'v': // be verbose
			verbose++;
			break;
		case 'V':
			//printf("Build number %d\n", buildno);
			return EXIT_SUCCESS;
			break;
		case 'd': // Print timestamp in every line
			optDate = true;
			break;
		case 'p': // power machine on program start
			optPower = true;
			break;
		case 'n': // disable network functionality
			optNoNet = true;
			break;

		case '?':
		case 'h':
			printf("Usage: %s [-hvVdp]\n", argv[0]);
			printf("\t-h\t\tPrints this help\n");
			printf("\t-v\t\tSets verbose output, can be used multiple times\n");
			printf("\t-V\t\tPrints only the version and exits\n");
			printf("\t-d\t\tPrints a timestamp before every line\n");
			printf("\t-p\t\tPowers the machine on program start\n");
			printf("\t-n\t\tDisable network functionality\n");

			printf("Listening to the following signals:\n");
			printf("\tSIGUSR1(%u)\tPrints monitor data of all nodes\n",
			SIGUSR1);
			printf("\tSIGHUP(%u)\tRuns a speedtest starting at default speed\n",
			SIGHUP);
			return EXIT_SUCCESS;
		default:
			fprintf(stderr, "Usage: %s [-hvVdpn]\n", argv[0]);
			return EXIT_FAILURE;
			break;
		}
	}

	if (signal(SIGINT, terminationHandler)) { // Calls terminationHandler if SIGINT is received
		fprintf(stderr, "Unable to set signal handler for SIGINT: %s\n",
				strerror(errno));
	}
	if (signal(SIGUSR1, usr1Handler)) { // Calls terminationHandler if SIGINT is received
		fprintf(stderr, "Unable to set signal handler for SIGUSR1: %s\n",
				strerror(errno));
	}
	if (signal(SIGHUP, hupHandler)) { // Calls terminationHandler if SIGINT is received
		fprintf(stderr, "Unable to set signal handler for SIGHUP: %s\n",
				strerror(errno));
	}
	// sets up the wiringPi library
	if (wiringPiSetup() < 0) {
		fprintf(stderr, "Unable to setup wiringPi: %s\n", strerror(errno));
		return 1;
	}

	logger_reset();
	initTimers();
	displayInit();
	//temperatur_init();
	DS18B20_init();
	sqlOpen();
	sqlSetup();


	// http://www.tutorialspoint.com/cplusplus/cpp_multithreading.htm

	// Initialize and set thread joinable
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
	rc = pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE_NP);
	if (rc != 0)
		throw(L"pthread_mutexattr_settype returns " + rc);
	pthread_mutex_init(&mutex_spi, &mutexattr);
	pthread_mutex_init(&mutex_i2c, &mutexattr);
	pthread_mutex_init(&mutex_logger, &mutexattr);
	rc = pthread_mutexattr_destroy(&mutexattr);
	if (rc != 0)
		throw(L"pthread_mutexattr_destroy returns " + rc);

	for (int i = 0; i < 5; i++) {//TODO this can be simplified!
		if (i == THREAD_MAIN)
			rc = pthread_create(&thread[i], &attr, mainLoop, (void *) i);
		else if (i == THREAD_STRIPE)
			rc = pthread_create(&thread[i], NULL, stripeThread, (void *) i);
		else if (i == THREAD_DISPLAY)
			rc = pthread_create(&thread[i], NULL, displayThread, (void *) i);
		else if (i == THREAD_COFFEE)
			rc = pthread_create(&thread[i], NULL, coffeeThread, (void *) i);
		else if (i == THREAD_SERVER && !optNoNet)
			rc = pthread_create(&thread[i], NULL, serverThread, (void *) i);
		if (rc) {
			logger_error("Error:unable to create thread %d (%d)\n", i, rc);
			exit(-1);
		}
	}
	//hal might send signals to processes which must exist!
	halInit();


	// free attribute and wait for the other threads
	pthread_attr_destroy(&attr);

	sleep(2);

	rc = pthread_join(thread[0], &status);
	if (rc) {
		logger_error("Error:unable to join, %d\n", rc);
		exit(-1);
	}
	logger(V_BASIC, "Completed thread with status %d\n", rc);
	pthread_exit(NULL);

	return EXIT_FAILURE;
}

/**
 * Handler for program termination caught via signal
 */

void terminationHandler(int signum) {
	logger(V_NONE, "Caught signal %d, exiting gracefully..\n", signum);
	stopTimers();

	event_trigger("terminate");

	/*logger(V_NONE, "Saving my state to the database..\n");
	 sqlExecute("begin");
	 // Save stuff here
	 sqlExecute("end");*/

	sqlClose(); // Closing DB connection

	exit(EXIT_SUCCESS);
}

/**
 * Handles the signal USR2
 */

void usr1Handler(int signum) {
	// Do something here?
	logger_reset();
}

/**
 * Handles the signal HUP and starts a speed test
 */

void hupHandler(int signum) {
	// Do something here?
}

/**
 * Sends a signal to a thread
 * @param threadid ID of the thread
 * @param sig Signal to send
 */

void killThread(int threadid, int sig) {
	//logger(V_BASIC, "pthread_kill on thread %d (%d)\n", threadid, sig);
	if (pthread_kill(thread[threadid], sig)) {
		logger_error("pthread_kill on thread %d failed (%d)\n", threadid, sig);
	}
}

/**
 * Main Thread, used for some initializations and error detection
 * @param threadid Thread ID
 */

void *mainLoop(void *threadid) {


	// Do more stuff here
	while (1){
		sleep(4);
		int8_t temp = DS18B20_readTemp();
		logger(V_BASIC, "Temperature: %d\n", temp);
	}

	logger(V_BASIC, "Thread goes Sleeping..\n");
	while (1) {
		pause();
		logger(V_NONE, LOG_ERROR, "Whoops. Something went wrong..\n");
	}
	pthread_exit(NULL);
}

