/* Reflow Oven Controller
*
* Copyright (C) 2020  Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/

#include <reflow-controller/reflow-menu.h>
#include <reflow-controller/ui/menu.h>
#include <reflow-controller/ui/lcd.h>
#include <reflow-controller/rotary-encoder.h>
#include <reflow-controller/systick.h>
#include <reflow-controller/adc-meas.h>
#include <reflow-controller/safety-adc.h>
#include <reflow-controller/temp-converter.h>
#include <helper-macros/helper-macros.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>

static char __attribute__((section(".ccmram"))) display_buffer[4][21] = {0};
static struct lcd_menu reflow_menu;
static struct lcd_menu *reflow_menu_ptr = &reflow_menu;

static void update_display_buffer(uint8_t row, const char *data)
{
	int i;

	if (row > 4)
		return;
	if (!data)
		return;

	for (i = 0; data[i] && i < LCD_CHAR_WIDTH; i++) {
		display_buffer[row][i] = data[i];
	}

	display_buffer[row][i] = 0;
}

static void reflow_menu_monitor(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent)
{
	static void *my_parent;
	static uint64_t my_timestamp = 0;
	char line[17];
	float tmp;
	int res;
	const char *prefix;

	if (entry_type == MENU_ENTRY_FIRST_ENTER) {
		my_parent = parent;
		menu_display_clear(menu);
	}

	if (systick_ticks_have_passed(my_timestamp, 250)) {
		my_timestamp = systick_get_global_tick();
		adc_pt1000_get_current_resistance(&tmp);
		snprintf(line, sizeof(line), "Res: %.1f", tmp);
		menu->update_display(0, line);

		res = temp_converter_convert_resistance_to_temp(tmp, &tmp);
		switch (res) {
		case -1:
			prefix = "<";
			break;
		case 1:
			prefix = ">";
			break;
		default:
			prefix = "";
			break;
		}

		snprintf(line, sizeof(line), "Temp: %s%.1f " LCD_DEGREE_SYMBOL_STRING "C", prefix, tmp);
		menu->update_display(1, line);

		tmp = safety_adc_get_temp();
		snprintf(line, sizeof(line), "Tj: %.1f " LCD_DEGREE_SYMBOL_STRING "C", tmp);
		menu->update_display(2, line);

		tmp = safety_adc_get_vref();
		snprintf(line, sizeof(line), "Vref: %.1f mV", tmp);
		menu->update_display(3, line);
	}

	if (menu->inputs.push_button == BUTTON_SHORT_RELEASED || menu->inputs.push_button == BUTTON_LONG) {
		menu_entry_dropback(menu, my_parent);
	}
}

static void reflow_menu_about(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent)
{
	static void *my_parent;
	static bool button_ready;
	char buff[20];

	if (entry_type == MENU_ENTRY_FIRST_ENTER) {
		my_parent = parent;
		button_ready = false;
		menu_display_clear(menu);

		menu->update_display(0, LCD_SHIMATTA_STRING " Reflow");
		snprintf(buff, sizeof(buff), "%.*s", LCD_CHAR_WIDTH, xstr(GIT_VER));
		menu->update_display(1, buff);
		if (strlen(xstr(GIT_VER)) > LCD_CHAR_WIDTH) {
			snprintf(buff, sizeof(buff), "%s", &xstr(GIT_VER)[LCD_CHAR_WIDTH]);
			menu->update_display(2, buff);
		}

		menu->update_display(3, __DATE__);
	}

	if (menu->inputs.push_button == BUTTON_IDLE)
		button_ready = true;

	if (button_ready &&
		(menu->inputs.push_button == BUTTON_SHORT_RELEASED || menu->inputs.push_button == BUTTON_LONG)) {
		menu_entry_dropback(menu, my_parent);
	}
}

static void reflow_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent)
{
	(void)parent;
	static struct menu_list list;
	static bool button_valid;
	static const char * const root_entry_names[] = {
		"About",
		"Monitoring",
		NULL
	};

	static const menu_func_t root_entry_funcs[] = {
		reflow_menu_about,
		reflow_menu_monitor
	};

	if (entry_type != MENU_ENTRY_CONTINUE) {
		menu_display_clear(menu);
		update_display_buffer(0, "Main Menu");
		menu_ack_rotary_delta(menu);
		if (entry_type == MENU_ENTRY_FIRST_ENTER) {
			button_valid = false;
			list.entry_names = root_entry_names;
			list.submenu_list = root_entry_funcs;
			list.update_display = menu->update_display;
			list.currently_selected = 0;
			menu_list_compute_count(&list);
		}
	}

	if (menu->inputs.push_button == BUTTON_IDLE) {
		button_valid = true;
	} else if (button_valid && menu->inputs.push_button == BUTTON_SHORT_RELEASED) {
		/* Enter currently selected menu_entry */
		menu_list_enter_selected_entry(&list, menu);
	}



	if (menu->inputs.rotary_encoder_delta >= 4) {
		menu_list_scroll_down(&list);
		menu_ack_rotary_delta(menu);
	} else if (menu->inputs.rotary_encoder_delta <= -4) {
		menu_list_scroll_up(&list);
		menu_ack_rotary_delta(menu);
	}

	menu_list_display(&list, 1, 3);
}


int reflow_menu_handle()
{
	int32_t rot_delta;
	enum button_state button;
	static enum lcd_fsm_ret lcd_ret = LCD_FSM_NOP;

	rot_delta = rotary_encoder_get_change_val();
	button = button_read_event();

	menu_handle(reflow_menu_ptr, (int16_t)rot_delta, button);

	if (lcd_ret != LCD_FSM_WAIT_CALL || lcd_tick_ms >= 1) {
		lcd_ret = lcd_fsm_write_buffer(display_buffer);
		lcd_tick_ms = 0UL;
	}

	if (lcd_ret == LCD_FSM_WAIT_CALL)
		return 1;
	else
		return 0;
}

void reflow_menu_init()
{
	rotary_encoder_setup();
	button_init();
	lcd_init();

	menu_init(reflow_menu_ptr, reflow_menu_root_entry, update_display_buffer);
}
