/**********************************************************
* File:             screen.c
*
* Description:      Helper for screen
*********************************************************/
#include "lvgl/lvgl.h" // IWYU pragma: keep
#include "lvgl/src/tick/lv_tick.h"
#include <stdint.h>
#include <time.h>
#include <unistd.h>

#define ARROW_DURATION 395
#define CARDS_DURATION 4000
#define CARDS_RATE 3.0
#define CARDS_MID 0.5
#define CARDS_DELAY 0.09
#define WHITE lv_color_hex(0xffffff)

LV_FONT_DECLARE(montserrat_15);
LV_FONT_DECLARE(dejavu_28);
LV_FONT_DECLARE(dejavu_52);
LV_FONT_DECLARE(dejavu_bold_10);
LV_FONT_DECLARE(dejavu_bold_73);
LV_FONT_DECLARE(dejavu_cbold_52);
LV_IMG_DECLARE(hop_arrow);
LV_IMG_DECLARE(hop_logo);
LV_IMG_DECLARE(hop_cards);
LV_IMG_DECLARE(hop_check);
LV_IMG_DECLARE(hop_id_required);

static lv_anim_t arrow_anim_template;
static lv_anim_t *running_arrow_anim;

static lv_anim_t cards_anim_template;
static lv_anim_t *running_cards_anim;

lv_obj_t *label_time;
lv_obj_t *label_date;
lv_obj_t *label_tap;
lv_obj_t *label_sku;
lv_obj_t *label_contactless;
lv_obj_t *label_reply_big;
lv_obj_t *label_reply_small;
lv_obj_t *img_arrow;
lv_obj_t *img_logo;
lv_obj_t *img_cards;
lv_obj_t *img_check;
lv_obj_t *img_id_required;

time_t curr_time;
struct tm *local_time;
char buf[100];
uint32_t response_timer = 0;
uint8_t id_required = 0;

void update_response();

double clamp(double d, double min, double max)
{
	const double t = d < min ? min : d;
	return t > max ? max : t;
}

void set_opa(void *var, int32_t value)
{
	lv_obj_t *obj = (lv_obj_t *)var;
	lv_obj_set_style_image_opa(obj, value, 0);
}

void screen_response(char *big, char *small, uint8_t id_req, uint32_t time)
{
	lv_label_set_text(label_reply_big, big);
	lv_label_set_text(label_reply_small, small);
	id_required = id_req;
	response_timer = lv_tick_get() + time;
	update_response();
}

void cards_anim_cb(void *var, int32_t value)
{
	double progress = (double)value / 0x7fff;
	double prompt_opa = progress - (CARDS_MID - CARDS_DELAY / 2);
	prompt_opa *= CARDS_RATE;
	prompt_opa = clamp(prompt_opa, 0.0, 1.0) * 255;
	double cards_opa = -progress + (CARDS_MID - CARDS_DELAY / 2);
	cards_opa *= CARDS_RATE;
	cards_opa = clamp(cards_opa, 0.0, 1.0) * 255;
	lv_obj_set_style_text_opa(label_tap, (int32_t)prompt_opa, 0);
	lv_obj_set_style_image_opa(img_cards, (int32_t)cards_opa, 0);
	lv_obj_set_style_text_opa(label_contactless, (int32_t)cards_opa, 0);
}

void screen_idle_update(lv_timer_t *data)
{
	time(&curr_time);
	local_time = localtime(&curr_time);
	strftime(buf, 100, "%m-%d-%y", local_time);
	lv_label_set_text(label_date, buf);
	strftime(buf, 90, "%l:%M %P", local_time);
	int i = 0;
	while (buf[i])
		i++;
	if (i > 0) {
		buf[i - 1] = '.';
		buf[i] = 'm';
		buf[i + 1] = '.';
		buf[i + 2] = '\0';
	}
	lv_label_set_text(label_time, buf);
	update_response();
}

void update_response() {
	if (response_timer < lv_tick_get()) {
		lv_obj_add_flag(img_check, LV_OBJ_FLAG_HIDDEN);
		lv_obj_add_flag(img_id_required, LV_OBJ_FLAG_HIDDEN);
		lv_obj_add_flag(label_reply_big, LV_OBJ_FLAG_HIDDEN);
		lv_obj_add_flag(label_reply_small, LV_OBJ_FLAG_HIDDEN);
		lv_obj_remove_flag(img_arrow, LV_OBJ_FLAG_HIDDEN);
		lv_obj_remove_flag(img_cards, LV_OBJ_FLAG_HIDDEN);
		lv_obj_remove_flag(img_logo, LV_OBJ_FLAG_HIDDEN);
		lv_obj_remove_flag(label_contactless, LV_OBJ_FLAG_HIDDEN);
		lv_obj_remove_flag(label_tap, LV_OBJ_FLAG_HIDDEN);
	} else {
		lv_obj_remove_flag(img_check, LV_OBJ_FLAG_HIDDEN);
		lv_obj_remove_flag(img_id_required, LV_OBJ_FLAG_HIDDEN);
		lv_obj_remove_flag(label_reply_big, LV_OBJ_FLAG_HIDDEN);
		lv_obj_remove_flag(label_reply_small, LV_OBJ_FLAG_HIDDEN);
		lv_obj_add_flag(img_arrow, LV_OBJ_FLAG_HIDDEN);
		lv_obj_add_flag(img_cards, LV_OBJ_FLAG_HIDDEN);
		lv_obj_add_flag(img_logo, LV_OBJ_FLAG_HIDDEN);
		lv_obj_add_flag(label_contactless, LV_OBJ_FLAG_HIDDEN);
		lv_obj_add_flag(label_tap, LV_OBJ_FLAG_HIDDEN);
	}
}

void screen_create(lv_obj_t *screen)
{
	/*Change the active screen's background color*/
	lv_obj_set_style_bg_color(screen, lv_color_hex(0), LV_PART_MAIN);

	// hop logo
	img_logo = lv_img_create(screen);
	lv_img_set_src(img_logo, &hop_logo);
	lv_obj_align(img_logo, LV_ALIGN_CENTER, 0, -100);

	// flickering arrow
	img_arrow = lv_img_create(screen);
	lv_img_set_src(img_arrow, &hop_arrow);
	lv_obj_align(img_arrow, LV_ALIGN_CENTER, 0, 155);
	lv_obj_set_style_img_opa(img_arrow, 100, 0);

	lv_anim_init(&arrow_anim_template);
	lv_anim_set_exec_cb(&arrow_anim_template, set_opa);
	lv_anim_set_values(&arrow_anim_template, 0, 255);
	lv_anim_set_duration(&arrow_anim_template, ARROW_DURATION);
	lv_anim_set_path_cb(&arrow_anim_template, lv_anim_path_ease_out);
	lv_anim_set_reverse_duration(&arrow_anim_template, ARROW_DURATION);
	lv_anim_set_repeat_count(&arrow_anim_template, LV_ANIM_REPEAT_INFINITE);
	lv_anim_set_var(&arrow_anim_template, img_arrow);

	running_arrow_anim = lv_anim_start(&arrow_anim_template);

	// tap prompt
	label_tap = lv_label_create(screen);
	lv_label_set_text(label_tap, "Tap card or phone below");
	lv_obj_set_style_text_font(label_tap, &dejavu_cbold_52, 0);
	lv_obj_set_style_text_color(label_tap, WHITE, 0);
	lv_obj_align(label_tap, LV_ALIGN_CENTER, 0, 83);

	// card acceptance logos
	img_cards = lv_img_create(screen);
	lv_img_set_src(img_cards, &hop_cards);
	lv_obj_align(img_cards, LV_ALIGN_CENTER, 0, 75);

	// contactless payments note
	label_contactless = lv_label_create(screen);
	lv_label_set_text(label_contactless, "Contactless payment only");
	lv_obj_set_style_text_font(label_contactless, &montserrat_15, 0);
	lv_obj_set_style_text_color(label_contactless, WHITE, 0);
	lv_obj_align(label_contactless, LV_ALIGN_CENTER, 0, 118);

	// animation for tap prompt/card acceptance
	lv_anim_init(&cards_anim_template);
	lv_anim_set_exec_cb(&cards_anim_template, cards_anim_cb);
	lv_anim_set_values(&cards_anim_template, 0, 0x7fff);
	lv_anim_set_duration(&cards_anim_template, CARDS_DURATION);
	lv_anim_set_path_cb(&cards_anim_template, lv_anim_path_linear);
	lv_anim_set_reverse_duration(&cards_anim_template, CARDS_DURATION);
	lv_anim_set_repeat_count(&cards_anim_template, LV_ANIM_REPEAT_INFINITE);
	lv_anim_set_var(&cards_anim_template, label_contactless);
	running_cards_anim = lv_anim_start(&cards_anim_template);

	// device number at bottom
	label_sku = lv_label_create(screen);
	lv_label_set_text(label_sku, "DEMO-DEV-LVGL");
	lv_obj_set_style_text_font(label_sku, &dejavu_bold_10, 0);
	lv_obj_set_style_text_color(label_sku, WHITE, 0);
	lv_obj_align(label_sku, LV_ALIGN_CENTER, 0, 233);

	// date in bottom left
	label_date = lv_label_create(screen);
	lv_label_set_text(label_date, "01-01-99");
	lv_obj_set_style_text_font(label_date, &dejavu_28, 0);
	lv_obj_set_style_text_color(label_date, WHITE, 0);
	lv_obj_align(label_date, LV_ALIGN_CENTER, -305, 226);

	// time in bottom right
	label_time = lv_label_create(screen);
	lv_label_set_text(label_time, "12:59 a.m.");
	lv_obj_set_style_text_font(label_time, &dejavu_28, 0);
	lv_obj_set_style_text_color(label_time, WHITE, 0);
	lv_obj_align(label_time, LV_ALIGN_CENTER, 305, 226);

	// scan response large
	label_reply_big = lv_label_create(screen);
	lv_label_set_text(label_reply_big, "Valid until Mar. 31");
	lv_obj_set_style_text_font(label_reply_big, &dejavu_bold_73, 0);
	lv_obj_set_style_text_color(label_reply_big, WHITE, 0);
	lv_obj_align(label_reply_big, LV_ALIGN_CENTER, 0, 47);
	lv_obj_add_flag(label_reply_big, LV_OBJ_FLAG_HIDDEN);

	// scan response small
	label_reply_small = lv_label_create(screen);
	lv_label_set_text(label_reply_small, "at 3:00am");
	lv_obj_set_style_text_font(label_reply_small, &dejavu_52, 0);
	lv_obj_set_style_text_color(label_reply_small, WHITE, 0);
	lv_obj_align(label_reply_small, LV_ALIGN_CENTER, 0, 111);
	lv_obj_add_flag(label_reply_small, LV_OBJ_FLAG_HIDDEN);

	// ID required symbol
	img_id_required = lv_img_create(screen);
	lv_img_set_src(img_id_required, &hop_id_required);
	lv_obj_align(img_id_required, LV_ALIGN_CENTER, 315, -165);
	lv_obj_add_flag(img_id_required, LV_OBJ_FLAG_HIDDEN);

	// Response check
	img_check = lv_img_create(screen);
	lv_img_set_src(img_check, &hop_check);
	lv_obj_align(img_check, LV_ALIGN_CENTER, 0, -124);
	lv_obj_add_flag(img_check, LV_OBJ_FLAG_HIDDEN);

	// timer to update clock
	lv_timer_create(screen_idle_update, 100, NULL);
}
