/*
 * stripe.cpp
 *
 *  Created on: Sep 25, 2017
 *      Author: Philipp Hinz
 */

#include <wiringPiI2C.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include "stripe.h"
#include "global.h"
#include "timer.h"
#include "logger.h"
#include "coffee.h"

stripe_color currentColor;
stripe_color effectColor;
int currentWhite = 0, currentDim = 0;
stripe_transient_t currentTransient = TRANS_DIRECT;
timer stripeTimer(stripeTimerHandler);
int i2cfd;
coffee_status_t lastState = STATE_OFF;

/**
 * This is the handler for time based stripe effects
 * @param *threadid Thread ID
 */
void *stripeTimerHandler(void *threadid) {
	if (currentColor.red == effectColor.red
			&& currentColor.green == effectColor.green
			&& currentColor.blue == effectColor.blue) {
		stripeSetRGB(0, 0, 0);
	} else {
		stripeSetColor(effectColor);
	}
	pthread_exit(NULL);
}

/**
 * Thread for the stripe control. Requests data from other threads or HAL
 * @param *threadid Thread ID
 */
void *stripeThread(void *threadid) {
	logger(V_STRIPE, "Initializing Stripe thread...\n");
	stripeInit();
	stripeTimer.setDivider(40);
	stripeTimer.stop();
	logger(V_BASIC, "Initialized Stripe thread\n");

	stripeOn();
	stripeSetRGB(0, 0, 0);
	stripe_color col;
	//int tmp = 0, inc = 1;
	while (1) {
		if (getState() != lastState) {
			lastState = getState();
			switch (lastState) {
			case STATE_IDLE:
			case STATE_HEATING:
			case STATE_BREW:
			case STATE_BREWMANUAL:
			case STATE_CLEANING:
				col.red = 255;
				col.green = 0;
				col.blue = 0;
				stripeEffectPulse(col);
				break;

			case STATE_INITALHEATING:
				col.red = 0;
				col.green = 0;
				col.blue = 255;
				stripeEffectPulse(col);
				break;

			case STATE_ERROR:
				stripeEffectDisable();
				stripeSetTransient(TRANS_FAST);
				stripeSetRGB(255, 0, 0);
				break;

			case STATE_OFF:
				usleep(50000);
				stripeEffectDisable();
				stripeSetTransient(TRANS_SLOW);
				stripeSetRGB(0, 0, 0);
				stripeSetRGB(0, 0, 0); // do it twice, else it doesn't work?! dafuq..
				break;
			}
		}
		usleep(100000);
		/*
		 pause();
		 stripeEffectHeating(tmp);
		 logger(V_NONE, LOG_ERRORC, "Simulating %d%% percent heating\n", tmp);
		 sleep(1);
		 if (inc) {
		 if (++tmp >= 100)
		 inc = 0;
		 } else {
		 if (--tmp <= 0)
		 inc = 1;
		 }*/
	}

	pthread_exit(NULL);
}

/**
 * Initializes i2c communication to stripe controller
 */
void stripeInit(void) {
	i2cfd = wiringPiI2CSetup(I2C_ADDRESS_STRIPE);
	if (i2cfd < 0) {
		logger_error("Could not initialize i2c with device ID 0x%.2x (%s)\n",
		I2C_ADDRESS_STRIPE, strerror(errno));
		pthread_exit(NULL);
	}
	logger(V_STRIPE, "Initialized i2c device on address 0x%.2x\n",
	I2C_ADDRESS_STRIPE);
	currentColor.red = 0;
	currentColor.green = 0;
	currentColor.blue = 0;
	currentWhite = 0;
	currentDim = 0xff;
	currentTransient = TRANS_DIRECT;
}

/**
 * Sends out command data to the stripe controller via i2c
 * @param len Length of the data array
 * @param *data Pointer to data array
 * @return Number of failed bytes, 0 if successful
 */
int stripeCommand(int len, char* data) {
	int ret = 0;
	int i = 0;
	pthread_mutex_lock(&mutex_i2c);
	for (i = 0; i < len; i++) {
		ret -= wiringPiI2CWriteReg8(i2cfd, i, data[i]);
	}
	logger(V_STRIPE, "Wrote %d bytes to i2c device\n", len);
	if (ret)
		logger(V_NONE, LOG_WARN,
				"Failed to write %d of %d bytes on i2c device\n", ret, len);
	pthread_mutex_unlock(&mutex_i2c);
	return ret;
}

/**
 * Updates the color data and prepares the stripe command
 */
void stripeUpdate(void) {
	char data[7];
	int len = 7;

	if (currentTransient == TRANS_DIRECT) {
		data[0] = STRIPE_CMD_SET;
		data[1] = currentDim;
		data[2] = currentColor.red;
		data[3] = currentColor.green;
		data[4] = currentColor.blue;
		data[5] = currentWhite;
		len = 6;
	} else {
		data[0] = STRIPE_CMD_FADETO;
		data[1] = currentColor.red;
		data[2] = currentColor.green;
		data[3] = currentColor.blue;
		data[4] = currentWhite;
		data[5] = (currentTransient >> 8) & 0xff;
		data[6] = currentTransient & 0xff;
	}

	stripeCommand(len, data);
	logger(V_STRIPE, "Updated stripe to color r %d g %d b %d\n", currentColor.red,
			currentColor.green, currentColor.blue);
}

/**
 * Updates the dimmer value of the stripe
 */
void stripeUpdateDim(void) {
	char data[2];
	int len = 2;

	data[0] = STRIPE_CMD_SETDIM;
	data[1] = currentDim;

	stripeCommand(len, data);
}

/**
 * Let the stripe fade out and turn off
 */
void stripeFadeOut(void) {
	char data[3];
	int len = 3;

	data[0] = STRIPE_CMD_FADEOUT;
	data[1] = (currentTransient >> 8) & 0xff;
	data[2] = currentTransient & 0xff;

	stripeCommand(len, data);
}

/**
 * Stops the current fading
 */
void stripeFadeStop(void) {
	char data[1];
	int len = 1;

	data[0] = STRIPE_CMD_FADESTOP;

	stripeCommand(len, data);
}

/**
 * Updates the color of the stripe
 * @param red Red value (max 255)
 * @param green Green value (max 255)
 * @param blue Blue value (max 255)
 */
void stripeSetRGB(int red, int green, int blue) {
	currentColor.red = red & 0xff;
	currentColor.green = green & 0xff;
	currentColor.blue = blue & 0xff;
	stripeUpdate();
}

/**
 * Updates the color of the stripe
 * @param color Color struct
 */
void stripeSetColor(stripe_color color) {
	stripeSetRGB(color.red, color.green, color.blue);
}

/**
 * Updates the transient time for the stripe (fading)
 * @param transient Transient time in ticks
 */
void stripeSetTransient(stripe_transient_t transient) {
	currentTransient = transient;
}

/**
 * Updates the dimmer value of the stripe
 * @param dim Dimmer value
 */
void stripeSetDim(int dim) {
	dim = dim & 0xff;
}

/**
 * Updates the white value of the stripe (4th channel)
 * @param white White value
 */
void stripeSetWhite(int white) {
	currentWhite = white & 0xff;
}

/**
 * Turns the stripe on (Sets dimmer to 100%)
 */
void stripeOn(void) {
	if (!currentDim) {
		currentDim = 0xff;
	}
	stripeUpdateDim();
}

/**
 * Turns the stripe on (Sets dimmer to 0%)
 */
void stripeOff(void) {
	currentDim = 0;
	stripeUpdateDim();
}

/**
 * Reads the current stripe color
 * @param *red Pointer to red variable
 * @param *green Pointer to green variable
 * @param *blue Pointer to blue variable
 */
void stripeGetRGB(int* red, int* green, int* blue) {
	*red = currentColor.red;
	*green = currentColor.green;
	*blue = currentColor.blue;
}

/**
 * Reads the current stripe dimmer value
 * @param *dim Pointer to dimmer variable
 */
void stripeGetDim(int* dim) {
	*dim = currentDim;
}

/**
 * Reads the current stripe white value (4th channel)
 * @param *white Pointer to white variable
 */
void stripeGetWhite(int* white) {
	*white = currentWhite;
}

/**
 * Stops current running effects
 */
void stripeEffectDisable(void) {
	stripeTimer.stop();
	stripeFadeStop();
}

/**
 * Sets the stripe to a correspondending color to the heating status
 * @param percent Heating level (0-100%)
 */
void stripeEffectHeating(int percent) {
	long red, blue;

	if (percent > 100)
		percent = 100;

	red = percent * 255;
	blue = (100 - percent) * 255;

	stripeSetTransient(TRANS_FAST);
	stripeSetRGB(red / 100, 0, blue / 100);
}

/**
 * Enables pulsing with a given color of the stripe
 * @param color Effect color
 */
void stripeEffectPulse(stripe_color color) {
	effectColor = color;
	stripeSetTransient(TRANS_MEDIUM);
	stripeTimer.setDivider(43);
	stripeTimer.start();
}

