#include <bcm2835.h>

#include <stdio.h>  //printf()
#include <stdlib.h> //exit()
#include <time.h>

#include "obj/DEV_Config.h"
#include "obj/LCD_Driver.h"
#include "obj/LCD_GUI.h"
#include "obj/LCD_BMP.h"
//#include "../Fonts/fonts.h"
//#include <string>
#include <node.h>
// //#include "nan.h"

using v8::Exception;
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::String;
using v8::Value;

void init(const FunctionCallbackInfo<Value> &args)
{
	if (System_Init())
		exit(0);

	//2.show
	printf("**********Init LCD**********\r\n");
	LCD_SCAN_DIR LCD_ScanDir = SCAN_DIR_DFT; //SCAN_DIR_DFT = D2U_L2R
	LCD_Init(LCD_ScanDir);

	printf("LCD Show \r\n");
	//LCD_Clear(GUI_BACKGROUND);
	//GUI_Show();
}

void Listen_Key_node(const FunctionCallbackInfo<Value> &args)
{
	// Listen_Key();
}

void read_Key1_node(const FunctionCallbackInfo<Value> &args)
{
	Isolate *isolate = args.GetIsolate();
	if (GET_KEY1 == 0)
	{
		args.GetReturnValue().Set(Number::New(isolate, 0));
	}
	else
	{
		args.GetReturnValue().Set(Number::New(isolate, 1));
	}
}

void clear_node(const FunctionCallbackInfo<Value> &args)
{
	LCD_Clear(GUI_BACKGROUND);
	// GUI_DrawRectangle(0,0,128,128,WHITE, DRAW_FULL, DOT_PIXEL_1X1);
}
void clear_fill_node(const FunctionCallbackInfo<Value> &args)
{
	GUI_DrawRectangle(0, 0, 128, 128, WHITE, DRAW_FULL, DOT_PIXEL_1X1);
}
void read_Key2_node(const FunctionCallbackInfo<Value> &args)
{
	Isolate *isolate = args.GetIsolate();
	if (GET_KEY2 == 0)
	{
		args.GetReturnValue().Set(Number::New(isolate, 0));
	}
	else
	{
		args.GetReturnValue().Set(Number::New(isolate, 1));
	}
}
void read_Key3_node(const FunctionCallbackInfo<Value> &args)
{
	Isolate *isolate = args.GetIsolate();
	if (GET_KEY3 == 0)
	{
		args.GetReturnValue().Set(Number::New(isolate, 0));
	}
	else
	{
		args.GetReturnValue().Set(Number::New(isolate, 1));
	}
}

void drawstring(const FunctionCallbackInfo<Value> &args)
{
	Isolate *isolate = args.GetIsolate();

	// Check the number of arguments passed.
	if (args.Length() < 3)
	{
		// Throw an Error that is passed back to JavaScript
		isolate->ThrowException(Exception::TypeError(
			String::NewFromUtf8(isolate, "Wrong number of arguments")));
		return;
	}

	// Check the argument types
	if (!args[0]->IsNumber() || !args[1]->IsNumber() || !args[2]->IsString())
	{
		isolate->ThrowException(Exception::TypeError(
			String::NewFromUtf8(isolate, "Wrong arguments")));
		return;
	}

	v8::String::Utf8Value arg2(args[2]);
	const char *s = *arg2;

	uint16_t font_number = 12;
	if (args[3]->IsNumber())
	{
		font_number = args[3]->NumberValue();
	}
	uint16_t n_color = BLACK;
	if (args[4]->IsNumber())
	{
		n_color = args[4]->NumberValue();
	}
	GUI_DisStringSize_EN(
		args[0]->NumberValue(),
		args[1]->NumberValue(),
		s ? s : "",
		font_number,
		GUI_BACKGROUND,
		n_color);
}

void LCD_ShowBmp_node(const FunctionCallbackInfo<Value> &args)
{
	Isolate *isolate = args.GetIsolate();
	if (!args[0]->IsString() && !args[1]->IsNumber())
	{
		isolate->ThrowException(Exception::TypeError(
			String::NewFromUtf8(isolate, "Wrong arguments")));
		return;
	}

	v8::String::Utf8Value arg2(args[0]);
	const char *s = *arg2;
	uint16_t duration = 500;

	duration = args[1]->NumberValue();
	LCD_ShowBmp(s, duration);
}
void DrawCircle_node(const FunctionCallbackInfo<Value> &args)
{
	Isolate *isolate = args.GetIsolate();
	if (args.Length() < 3)
	{
		// Throw an Error that is passed back to JavaScript
		isolate->ThrowException(Exception::TypeError(
			String::NewFromUtf8(isolate, "Wrong number of arguments")));
		return;
	}
	if (!args[0]->IsNumber() || !args[1]->IsNumber() || !args[2]->IsNumber())
	{
		isolate->ThrowException(Exception::TypeError(
			String::NewFromUtf8(isolate, "Wrong arguments")));
		return;
	}
	uint16_t Cx = 0, Cy = 0, Cr = 0, Ccolor = BLACK, isOrigin = 0;
	
	Cx = args[0]->NumberValue();
	Cy = args[1]->NumberValue();
	Cr = args[2]->NumberValue();
	if (args[3]->IsNumber())
		Ccolor = args[3]->NumberValue();
	if (args[4]->IsNumber())
	{
		isOrigin = args[4]->NumberValue();
	}

	GUI_DrawCircle(Cx, Cy, Cr, Ccolor, DRAW_EMPTY, DOT_PIXEL_DFT);
	if(isOrigin == 1){
		GUI_DrawCircle(Cx, Cy, 2, Ccolor, DRAW_FULL, DOT_PIXEL_DFT);
	}
}
void DrawCheck(uint16_t x, uint16_t y, uint16_t color)
{
	GUI_DrawLine(x, y, x + 5, y + 5, color, LINE_SOLID, DOT_PIXEL_DFT);
	GUI_DrawLine(x + 5, y + 5, x + 12, y - 8, color, LINE_SOLID, DOT_PIXEL_DFT);
}

void DrawCheckFalse(uint16_t x, uint16_t y, uint16_t color)
{
	GUI_DrawLine(x, y, x + 9, y + 9, color, LINE_SOLID, DOT_PIXEL_DFT);
	GUI_DrawLine(x, y + 9, x + 9, y, color, LINE_SOLID, DOT_PIXEL_DFT);
}

void DrawCheck_node(const FunctionCallbackInfo<Value> &args)
{
	Isolate *isolate = args.GetIsolate();
	if (args.Length() < 3)
	{
		// Throw an Error that is passed back to JavaScript
		isolate->ThrowException(Exception::TypeError(
			String::NewFromUtf8(isolate, "Wrong number of arguments")));
		return;
	}
	if (!args[0]->IsNumber() || !args[1]->IsNumber() || !args[2]->IsNumber())
	{
		isolate->ThrowException(Exception::TypeError(
			String::NewFromUtf8(isolate, "Wrong arguments")));
		return;
	}
	uint16_t x = 0, y = 0, Ccolor = BLACK;
	if (args[2]->IsNumber())
	{
		Ccolor = args[2]->NumberValue();
	}
	x = args[0]->NumberValue();
	y = args[1]->NumberValue();
	if (args[3]->IsNumber())
	{
		DrawCheckFalse(x, y, Ccolor);
		return;
	}
	DrawCheck(x, y, Ccolor);
}
void CpuInfo_node(const FunctionCallbackInfo<Value> &args){
	GUI_CPU();
}
void Initialize(Local<Object> exports)
{
	NODE_SET_METHOD(exports, "init", init);
	NODE_SET_METHOD(exports, "drawstring", drawstring);
	NODE_SET_METHOD(exports, "clear", clear_node);
	NODE_SET_METHOD(exports, "fill_white_recg", clear_fill_node);
	NODE_SET_METHOD(exports, "Listen_Key", Listen_Key_node);
	NODE_SET_METHOD(exports, "read_Key1", read_Key1_node);
	NODE_SET_METHOD(exports, "read_Key2", read_Key2_node);
	NODE_SET_METHOD(exports, "read_Key3", read_Key3_node);
	NODE_SET_METHOD(exports, "LCD_ShowBmp", LCD_ShowBmp_node);
	NODE_SET_METHOD(exports, "DrawCircle", DrawCircle_node);
	NODE_SET_METHOD(exports, "DrawCheck", DrawCheck_node);
	NODE_SET_METHOD(exports, "CpuInfo", CpuInfo_node);
}

NODE_MODULE(my_lcd_bcm2835, Initialize)

int main(void)
{
	//1.System Initialization
	if (System_Init())
		exit(0);

	//2.show
	printf("**********Init LCD**********\r\n");
	LCD_SCAN_DIR LCD_ScanDir = SCAN_DIR_DFT; //SCAN_DIR_DFT = D2U_L2R
	LCD_Init(LCD_ScanDir);
	LCD_Clear(WHITE);

	printf("Listen Key\r\n");
	//Listen_Key();

	//3.System Exit
	System_Exit();
	return 0;
}
