/*
 * kernel/command/cmd-test.c
 */

#include <xboot.h>
#include <command/command.h>

#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>
#include <framebuffer/framebuffer.h>


#include <cairo-ft.h>
#include <shell/ctrlc.h>
#include <input/input.h>
#include <input/keyboard.h>
#include "lvgl/lvgl.h"
#include "benchmark.h"
#include "demo.h"
#include "sysmon.h"
#include "terminal.h"
#include "tpcal.h"

#include "lv_test.h"


struct lvgui_pdata_t
{
	struct window_t * w;
    struct timer_t timer;    
};

struct lvgui_pdata_t lvgl_pdata;
/**********************
 *   STATIC FUNCTIONS
 **********************/
static int lvgui_timer_function(struct timer_t * timer, void * data)
{
    //struct lvgui_pdata_t * pdat = (struct lvgui_pdata_t *)(data);
    timer_forward_now(timer, ms_to_ktime(1));
    
    lv_tick_inc(1);    
    return 1;
}
    struct input_t * input;
    struct event_t e;
static bool lvgl_input_mouse(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
    {

		if(window_pump_event(lvgl_pdata.w, &e))
		{
			input = (struct input_t *)(e.device);
			switch(e.type)
			{
			case EVENT_TYPE_KEY_DOWN:
				printf("[%s]: [KeyDown] [%d]\r\n", input->name, e.e.key_down.key);
				break;
			case EVENT_TYPE_KEY_UP:
				printf("[%s]: [KeyUp] [%d]\r\n", input->name, e.e.key_up.key);
				break;
			case EVENT_TYPE_ROTARY_TURN:
				printf("[%s]: [RotaryTurn] [%d]\r\n", input->name, e.e.rotary_turn.v);
				break;
			case EVENT_TYPE_MOUSE_DOWN:
                data->point.x = e.e.mouse_down.x;
                data->point.y = e.e.mouse_down.y;
                data->state = 1;
				printf("[%s]: [MouseDown] [%d][%d][0x%x]\r\n", input->name, e.e.mouse_down.x, e.e.mouse_down.y, e.e.mouse_down.button);
				break;
			case EVENT_TYPE_MOUSE_MOVE:
                printf("[%s]: [MouseMove] [%d][%d]\r\n", input->name, e.e.mouse_move.x, e.e.mouse_move.y);
				break;
			case EVENT_TYPE_MOUSE_UP:
                data->point.x = e.e.mouse_down.x;
                data->point.y = e.e.mouse_down.y;
                data->state = 0;
				printf("[%s]: [MouseUp] [%d][%d][0x%x]\r\n", input->name, e.e.mouse_up.x, e.e.mouse_up.y, e.e.mouse_up.button);
				break;
			case EVENT_TYPE_MOUSE_WHEEL:
				printf("[%s]: [MouseWheel] [%d][%d]\r\n", input->name, e.e.mouse_wheel.dx, e.e.mouse_wheel.dy);
				break;
			case EVENT_TYPE_TOUCH_BEGIN:
				printf("[%s]: [TouchBegin] [%d][%d][%d]\r\n", input->name, e.e.touch_begin.x, e.e.touch_begin.y, e.e.touch_begin.id);
				break;
			case EVENT_TYPE_TOUCH_MOVE:
				printf("[%s]: [TouchMove] [%d][%d][%d]\r\n", input->name, e.e.touch_move.x, e.e.touch_move.y, e.e.touch_move.id);
				break;
			case EVENT_TYPE_TOUCH_END:
				printf("[%s]: [TouchEnd] [%d][%d][%d]\r\n", input->name, e.e.touch_end.x, e.e.touch_end.y, e.e.touch_end.id);
				break;
			default:
				printf("[%s]: [Unkown]\r\n", input->name);
				break;
			}
		}
        return false;
    }
        

/* Flush the content of the internal buffer the specific area on the display
 * You can use DMA or any hardware acceleration to do this operation in the background but
 * 'lv_disp_flush_ready()' has to be called when finished
 * This function is required only when LV_VDB_SIZE != 0 in lv_conf.h*/
static void ex_disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
    /*Return if the area is out the screen*/
    lv_coord_t hres = disp_drv->rotated == 0 ? disp_drv->hor_res : disp_drv->ver_res;
    lv_coord_t vres = disp_drv->rotated == 0 ? disp_drv->ver_res : disp_drv->hor_res;
    /*Return if the area is out the screen*/
    if (area->x2 < 0 || area->y2 < 0 || area->x1 > hres - 1 || area->y1 > vres - 1)
    {

        lv_disp_flush_ready(disp_drv);
        return;
    }
    int32_t y;
    lv_color_t *pixels=lvgl_pdata.w->s->pixels;
    uint32_t w = lv_area_get_width(area);
    for (y = area->y1; y <= area->y2 && y < disp_drv->ver_res; y++)
    {
        memcpy(&pixels[y * lvgl_pdata.w->s->width + area->x1], color_p, w * sizeof(lv_color_t));
        color_p += w;
    }
    framebuffer_present_surface(lvgl_pdata.w->wm->fb, lvgl_pdata.w->s ,lvgl_pdata.w->rl); 

    /* IMPORTANT!!!
     * Inform the graphics library that you are ready with the flushing*/
    lv_disp_flush_ready(disp_drv);  
}
static void hal_init(void)
{    

    struct lvgui_pdata_t * pdat=&lvgl_pdata;
    /***********************
     * Tick interface
     ***********************/
    /* Initialize a Timer for 1 ms period and
     * in its interrupt call
     * lv_tick_inc(1); */
    timer_init(&pdat->timer, lvgui_timer_function, pdat);
    timer_start_now(&pdat->timer, ms_to_ktime(5));

	pdat->w = window_alloc(NULL, NULL, pdat);

     /***********************
     * Display interface
     ***********************/
    static lv_color_t buffer[LV_HOR_RES_MAX * LV_VER_RES_MAX];
    static lv_disp_buf_t disp_buf;
    lv_disp_buf_init(&disp_buf, buffer, NULL, LV_HOR_RES_MAX * LV_VER_RES_MAX);   
    lv_disp_drv_t disp_drv;                         /*Descriptor of a display driver*/
    lv_disp_drv_init(&disp_drv);                    /*Basic initialization*/
    /*Set up the functions to access to your display*/
    disp_drv.hor_res = pdat->w->s->width;
    disp_drv.ver_res = pdat->w->s->height;
    disp_drv.flush_cb = ex_disp_flush;            /*Used in buffered mode (LV_VDB_SIZE != 0  in lv_conf.h)*/   
    disp_drv.buffer = &disp_buf;                    /*Set a display buffer*/
    /*Finally register the driver*/
    lv_disp_drv_register(&disp_drv);

    /*************************
     * Input device interface
     *************************/
    /*Add a mouse in the example*/
    /*mouse_init();*/                              /*Initialize your mouse*/
   lv_indev_drv_t indev_drv;                       /*Descriptor of an input device driver*/
   lv_indev_drv_init(&indev_drv);                  /*Basic initialization*/
   indev_drv.type = LV_INDEV_TYPE_POINTER;         /*The mouse is pointer type device*/
   indev_drv.read_cb = lvgl_input_mouse;           /*Library ready your mouse via this function*/
   lv_indev_drv_register(&indev_drv);              /*Finally register the driver*/
}


static int do_test(int argc, char ** argv)
{
    /***********************
     * Initialize LittlevGL
     ***********************/
    lv_init();  
    hal_init(); 

    //lv_obj_t * label = lv_label_create(lv_scr_act(), NULL);
    {
        /*Create a Label on the currently active screen*/
        lv_obj_t * label1 =  lv_label_create(lv_scr_act(), NULL);

        /*Modify the Label's text*/
        lv_label_set_text(label1, "Hello world!");

        /* Align the Label to the center
         * NULL means align on parent (which is the screen now)
         * 0, 0 at the end means an x, y offset after alignment*/
        lv_obj_align(label1, NULL, LV_ALIGN_CENTER, 0, 0);
        /*Add a button*/
        lv_obj_t * btn1 = lv_btn_create(lv_scr_act(), NULL);             /*Add to the active screen*/
        lv_obj_set_pos(btn1, 100, 100);                                    /*Adjust the position*/
        lv_obj_set_size(btn1, 100, 50);
       // lv_btn_set_action(btn1, LV_BTN_ACTION_CLICK, my_click_action);   /*Assign a callback for clicking*/

        /*Add text*/
        lv_obj_t * label = lv_label_create(btn1, NULL);                  /*Put on 'btn1'*/
        lv_label_set_text(label, "Click me");
    } 

    /*Load a demo*/

	// benchmark_create();

	demo_create(); 
 
	// sysmon_create();

	// terminal_create();
	// tpcal_create();

	// lv_test_theme_1(lv_theme_default_init(210, NULL));

	// lv_test_theme_2();

    while(1)
    {        
        lv_task_handler();
        mdelay(5);
 		if(ctrlc())
			break;       
    }
    window_free(lvgl_pdata.w);
    return 0;
}
static void usage(void)
{
	printf("usage:\r\n");
	printf("    test [args ...]\r\n");
}

static struct command_t cmd_test = {
	.name	= "test",
	.desc	= "debug command for programmer",
	.usage	= usage,
	.exec	= do_test,
};

static __init void test_cmd_init(void)
{
	register_command(&cmd_test);
}

static __exit void test_cmd_exit(void)
{
	unregister_command(&cmd_test);
}

command_initcall(test_cmd_init);
command_exitcall(test_cmd_exit);
