/*
 * server.cpp
 *
 *  Created on: Jan 4, 2018
 *      Author: Philipp Hinz
 *      based on: https://gist.github.com/suyash/2488ff6996c98a8ee3a84fe3198a6f85
 */

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

#include "server.h"
#include "logger.h"
#include "hal.h"
#include "coffee.h"
#include "stripe.h"
#include "events.h"

/**
 * TCP Uses 2 types of sockets, the connection socket and the listen socket.
 * The Goal is to separate the connection phase from the data exchange phase.
 * */

int listen_sock;
int sock;
char buffer_ok[] = "OK\n";

/**
 * Just sends an ACK message back
 */

void sendACK() {
	send(sock, buffer_ok, strlen(buffer_ok), 0);
}

/**
 * Closes all active connections
 */

void serverTerminate(event_t *e) {
	close(sock);
	close(listen_sock);
}

/**
 * Main thread to handle networking stuff
 * @param *threadid Thread ID
 */

void* serverThread(void* threadid) {
	sleep(1); // Wait for other components to initialize

	// socket address used for the server
	struct sockaddr_in server_address;
	memset(&server_address, 0, sizeof(server_address));
	server_address.sin_family = AF_INET;

	// htons: host to network short: transforms a value in host byte
	// ordering format to a short value in network byte ordering format
	server_address.sin_port = htons(SERVER_PORT);

	// htonl: host to network long: same as htons but to long
	server_address.sin_addr.s_addr = htonl(INADDR_ANY);

	// create a TCP socket, creation returns -1 on failure
	if ((listen_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
		logger_error("could not create listen socket\n");
		pthread_exit(0);
	}

	// bind it to listen to the incoming connections on the created server
	// address, will return -1 on error
	if ((bind(listen_sock, (struct sockaddr *) &server_address,
			sizeof(server_address))) < 0) {
		logger_error("could not bind socket\n");
		pthread_exit(0);
	}

	int wait_size = 16;  // maximum number of waiting clients, after which
						 // dropping begins
	if (listen(listen_sock, wait_size) < 0) {
		logger_error("could not open socket for listening\n");
		pthread_exit(0);
	}

	event_subscribe("terminate", &serverTerminate);
	logger(V_BASIC, "Now listening for commands on port %d\n", SERVER_PORT);

	// socket address used to store client address
	struct sockaddr_in client_address;
	unsigned int client_address_len = 0;

	// run indefinitely
	while (true) {
		// open a new socket to transmit data per connection
		if ((sock = accept(listen_sock, (struct sockaddr *) &client_address,
				&client_address_len)) < 0) {
			logger_error("could not open a socket to accept data\n");
			pthread_exit(0);
		}

		int n = 0;
		int len = 0, maxlen = 100, lastlen = 0;
		char buffer[maxlen];
		char cmd[maxlen];
		char *pcmd = cmd;
		char *pbuffer = buffer;
		strncpy(buffer, "", maxlen);
		strncpy(cmd, "", maxlen);

		logger(V_HAL, "client connected with ip address: %s\n",
				inet_ntoa(client_address.sin_addr));

		// keep running as long as the client keeps the connection open
		while ((n = recv(sock, pbuffer, maxlen, 0)) > 0) {
			pbuffer += n;
			maxlen -= n;
			len += n;

			strncpy(cmd, buffer, len - 1);
			cmd[len - 1] = '\0';
			pcmd = &cmd[lastlen];
			logger(V_SPI, "received %d bytes: '%s' (cmd: %s)\n", len, buffer,
					pcmd);

			if (!strcmp(SERVER_CMD_ON, pcmd)) {
				if (getState() == STATE_OFF) {
					halSendSignal(SigInt0Rls);
					sendACK();
				}
			} else if (!strcmp(SERVER_CMD_OFF, pcmd)) {
				if (getState() != STATE_OFF) {
					halSendSignal(SigInt1RlsLong);
					sendACK();
				}
			} else if (!strcmp(SERVER_CMD_BREW, pcmd)) {
				if (getState() == STATE_IDLE || getState() == STATE_HEATING) {
					halSendSignal(SigInt1Rls);
					sendACK();
				}
			} else if (!strcmp(SERVER_CMD_STATUS, pcmd)) {
				char tmp[] = "0\n";
				tmp[0] += getState();
				send(sock, tmp, sizeof(tmp), 0);
			} else if (!strcmp(SERVER_CMD_STRIPEON, pcmd)) {
				stripeOn();
				sendACK();
			} else if (!strcmp(SERVER_CMD_STRIPEOFF, pcmd)) {
				stripeOff();
				sendACK();
			}


			lastlen = len;
			// echo received content back
			//send(sock, buffer, len, 0);
		}

		close(sock);
	}

	close(listen_sock);
	pthread_exit(EXIT_SUCCESS);
}

