#include <TuiScreenResource.h>
#include "TZ_Vendor_tl.h"
#include "TZ_Vendor_debug_tl.h"
#include "tl_tui_bc_error_msg.h"
#include "Vendor_Interface.h"

#include "TuiScreen.h"
#include "TuiBackupScreenController.h"
#include "TuiRestoreScreenController.h"
#include "TuiPopupScreenController.h"
#include "TuiControl.h"
#include "TuiButton.h"
#include "TuiImageView.h"
#include "TuiTouch.h"
#include "TuiPng.h"
#include "TuiState.h"

static uint8_t *gBgImage = NULL;
static uint32_t gBgImageSize = 0;
static uint32_t gBgImageWidth = 0;
static uint32_t gBgImageHeight = 0;

static Control gScreenView;


static ScreenState screenState = NONE_SCREEN_STATE;
static ScreenState preScreenState = NONE_SCREEN_STATE;

typedef enum ScreenType {
	NONE_SCREEN_TYPE, BACKUP_SCREEN_TYPE, RESTORE_SCREEN_TYPE, CONFIRM_SCREEN_TYPE
} EScreenType;

uint32_t drawTextImage(uint32_t x, uint32_t y, uint8_t *img, uint32_t length) {
	drawImage(x, y, img, length);
	return TUI_SUCCESS;
}

void initScreenState(){
	screenState = NONE_SCREEN_STATE;
	preScreenState = NONE_SCREEN_STATE;
}

uint8_t* setBackground(uint32_t imageSize, uint32_t imageWidth, uint32_t imageHeight) {
	clearBackgroundResource();

	if (imageSize == 0) {
		TTY_LOG("BgImage is invalid!");
		return NULL;
	}

	gBgImage = TZ_malloc(imageSize);
	gBgImageSize = imageSize;
	gBgImageWidth = imageWidth;
	gBgImageHeight = imageHeight;

	return gBgImage;
}

uint8_t* getBackground(uint32_t* imageSize, uint32_t* imageWidth, uint32_t* imageHeight) {
	*imageSize = gBgImageSize;
	*imageWidth = gBgImageWidth;
	*imageHeight = gBgImageHeight;

	return gBgImage;
}

bool isExsitBackground() {
	DBG_LOG("isExsitBackground()");
	if (gBgImage == NULL) {
		TTY_LOG("gBgImage is NULL");
		return false;
	} else {
		return true;
	}
}

uint32_t saveBackgroundResource(uint32_t index, uint8_t* bgData, uint32_t dataLen) {
	if (gBgImageSize - index < dataLen) {
		TTY_LOG("%s BackgroundResource buffer is small! gBgImage-index : %d, dataLen : %d",LOG_TAG, sizeof(gBgImage)-index, dataLen );
		return ERROR_BC_TUI;
	}
	memcpy((gBgImage + index), bgData, dataLen);
	return TUI_SUCCESS;
}

void clearBackgroundResource() {
	if (gBgImage != NULL) {
		TZ_bzero(gBgImage, gBgImageSize);
		TZ_free(gBgImage);
		gBgImage = NULL;
	}
	gBgImageSize = 0;
	gBgImageWidth = 0;
	gBgImageHeight = 0;
}

bool validatePngResource(uint8_t* image, uint32_t imageSize, uint32_t imageWidth, uint32_t imageHeight) {
	DBG_LOG("validatePngResource()");
	uint32_t width = 0;
	uint32_t height = 0;

	if (validatePng(image, imageSize, &width, &height) != TIMA_SUCCESS) {
		TTY_LOG("PNG format is invalid");
		return false;
	}

	if (width != imageWidth) {
		TTY_LOG("Resource width is not proper");
		return false;
	}
	if (height != imageHeight) {
		TTY_LOG("Resource height is not proper");
		return false;
	}

	DBG_LOG("ResourceSize : %d, width:%d, height:%d", imageSize, imageWidth, imageHeight);
	//DBG_DUMP(image, imageSize); // It can happen kernel panic.

	return true;
}

uint32_t showBackground() {
	DBG_LOG("showBackground()");
	if (gBgImage == NULL) {
		TTY_LOG("BgImage is null");
		clearBackgroundResource();
		return ERROR_BC_TUI;
	}

	if (validatePngResource(gBgImage, gBgImageSize, gBgImageWidth, gBgImageHeight) == false) {
		TTY_LOG("BgImage resource is invalid");
		clearBackgroundResource();
		return ERROR_BC_TUI;
	}

	uint32_t result = showPng(0, 0, gBgImage, gBgImageSize);
	clearBackgroundResource();

	return result;
}

ResponseControl executeTouchableView(Control *control) {
	DBG_LOG("executeTouchableView() : %d", control->state);
	ResponseControl rc;
	initResponseControl(&rc);

	DBG_LOG("control id : %d", control->id);
	DBG_LOG("control eventResourceSize : %d", control->eventResourceSize);
	DBG_LOG("control originResourceSize : %d", control->originResourceSize);
	DBG_LOG("screen state : %d", getScreenState());

	switch (control->state) {
	case PRESSED_CONTROL_STATE:
		break;
	case RELEASED_CONTROL_STATE:
		switch (control->id) {
		case ID_CONTROL_BUTTON_RESTORE_TEXTLINK:
			changeScreenState(RESTORE_SCREEN_TEXT_LINK_STATE);
			break;
		case ID_CONTROL_BUTTON_CONFIRM_OK:
			changeState(OK_TUI_STATE);
			break;
		case ID_CONTROL_BUTTON_BACKUP_OK:
			if (getScreenState() == BACKUP_SCREEN_STATE) {
				changeScreenState(BACKUP_SCREEN_QUIZ_STATE);
			} else {
				changeState(OK_TUI_STATE);
			}
			break;
		case ID_CONTROL_BUTTON_SOFTKEY_CANCEL:
		case ID_CONTROL_BUTTON_TITLEBAR_CANCEL:
			if (getScreenState() == BACKUP_SCREEN_STATE) {
				changeScreenState(POPUP_SCREEN_BACKUP_EXIT_STATE);
			} else if (getScreenState() == BACKUP_SCREEN_QUIZ_STATE) {
				changeScreenState(BACKUP_SCREEN_STATE);
			} else if(getScreenState() == RESTORE_SCREEN_STATE) {
				changeScreenState(POPUP_SCREEN_RESTORE_EXIT_STATE);
			} else {
				changeState(CANCELLED_TUI_STATE);
			}
			break;
		case ID_CONTROL_BUTTON_POPUP_OK:
			if (getScreenState() == POPUP_SCREEN_RESTORE_FAILED_CHECKSUM_STATE) {
				changeScreenState(RESTORE_SCREEN_STATE);
			} else {
				changeState(CANCELLED_TUI_STATE);
			}
			break;
		case ID_CONTROl_BUTTON_POPUP_CANCEL:
			if (getScreenState() == POPUP_SCREEN_BACKUP_EXIT_STATE && getPreScreenState() == BACKUP_SCREEN_TYPE) {
				changeScreenState(BACKUP_SCREEN_STATE);
			} else if (getScreenState() == POPUP_SCREEN_RESTORE_EXIT_STATE) {
				changeScreenState(RESTORE_SCREEN_STATE);
			} else {
				changeState(CANCELLED_TUI_STATE);
			}
			break;
		default:
			changeState(CANCELLED_TUI_STATE);
			break;
		}
		break;
	case NONE_CONTROL_STATE:
		break;
	default:
		break;
	}
	drawButton(control);

	return rc;
}

EXEC_FUNC getExecuteFunction(uint32_t id) { // exceute then draw.
	switch (id) {
	case ID_CONTROL_BUTTON_RESTORE_NEXT:
		return executeButtonNext;
	case ID_CONTROL_BUTTON_RESTORE_ENTER:
		return executeButtonEnterMnemonic;
	case ID_CONTROL_BUTTON_RESTORE_TEXTLINK:
	case ID_CONTROL_BUTTON_CONFIRM_OK:
	case ID_CONTROL_BUTTON_BACKUP_OK:
	case ID_CONTROL_BUTTON_SOFTKEY_CANCEL:
	case ID_CONTROL_BUTTON_TITLEBAR_CANCEL:
	case ID_CONTROL_BUTTON_POPUP_OK:
	case ID_CONTROl_BUTTON_POPUP_CANCEL:
		return executeTouchableView;
	case ID_CONTROL_IMAGE_CHECKBOX_OFF:
	case ID_CONTROL_IMAGE_CHECKBOX_ON:
	case ID_CONTROL_IMAGE_WRITE_IT_DOWN:
		return executeButtonCheckbox;
	case ID_CONTROL_BUTTON_MNEMONIC_PHRASE_NEXT:
		return executeButtonMnemonicPhraseNext;
	case ID_CONTROL_BUTTON_MNEMONIC_PHRASE_PREVIOUS:
		return executeButtonMnemonicPhrasePrevious;
	default:
		return executeButton;
	}
}

EControlState getDefaultControlState(uint32_t id) {
	switch (id) {
	case ID_CONTROL_BUTTON_RESTORE_NEXT:
	case ID_CONTROL_BUTTON_RESTORE_ENTER:
	case ID_CONTROL_BUTTON_BACKUP_OK:
		return DISABLE_CONTROL_STATE;
	case ID_CONTROL_IMAGE_VIEW_BACKUP_QUIZ_FIRST_MESSAGE:
	case ID_CONTROL_IMAGE_VIEW_BACKUP_QUIZ_SECOND_MESSAGE:
	case ID_CONTROL_IMAGE_VIEW_BACKUP_QUIZ_THIRD_MESSAGE:
	case ID_CONTROL_BUTTON_MNEMONIC_PHRASE_PREVIOUS:
	case ID_CONTROL_BUTTON_MNEMONIC_PHRASE_NEXT:
	case ID_CONTROL_IMAGE_CHECKBOX_ON:
	case ID_CONTROL_IMAGE_CHECKBOX_OFF:
    case ID_CONTROL_IMAGE_WRITE_IT_DOWN:
	case ID_CONTROL_IMAGE_CHECKBOX_SCREENING:
	case ID_CONTROL_IMAGE_VIEW_MNEMONIC_PHRASE_SCREENING:
	case ID_CONTROL_IMAGE_VIEW_MNEMONIC_PHRASE_NUMBER_SCREENING:
		return INVISIBLE_CONTROL_STATE;
	case ID_CONTROL_BUTTON_RESTORE_TEXTLINK:
	case ID_CONTROL_BUTTON_CONFIRM_OK:
	case ID_CONTROL_BUTTON_SOFTKEY_CANCEL:
	case ID_CONTROL_BUTTON_TITLEBAR_CANCEL:
	case ID_CONTROL_BUTTON_POPUP_OK:
	case ID_CONTROl_BUTTON_POPUP_CANCEL:
	case ID_CONTROL_IMAGE_VIEW_RESTORE_WRONG_INPUT:
	case ID_CONTROL_IMAGE_VIEW_BACKUP_QUIZ_FIRST_WRONG_INPUT:
	case ID_CONTROL_IMAGE_VIEW_BACKUP_QUIZ_SECOND_WRONG_INPUT:
	case ID_CONTROL_IMAGE_VIEW_BACKUP_QUIZ_THIRD_WRONG_INPUT:
	default:
		return ENABLE_CONTROL_STATE;
	}
}

void initScreenViewResource() {
	gScreenView.id = 0;
	gScreenView.eventResource = NULL;
	gScreenView.eventResourceSize = 0;
	gScreenView.originResource = NULL;
	gScreenView.originResourceSize = 0;
	gScreenView.extraResource = NULL;
	gScreenView.extraResourceSize = 0;
}

uint32_t setScreenView(uint32_t id, uint32_t x, uint32_t y, uint32_t width, uint32_t height, uint32_t eventImageSize, uint32_t originImageSize, uint32_t extraImageSize) {
	DBG_LOG("setScreenView : %d, %d, %d, %d", id, eventImageSize, originImageSize, extraImageSize);
	DBG_LOG("x : %d, y : %d", x, y);
	DBG_LOG("width : %d, height : %d", width, height);
	Control control;
	Location loc;

	initScreenViewResource();

	Node* node = pickingControlById(id);
	if (node != NULL) {
		if (node->control.location.x1 == x && node->control.location.y1 == y && node->control.width == width && node->control.height == height &&
			node->control.originResourceSize == originImageSize && node->control.eventResourceSize == eventImageSize && node->control.extraResourceSize == extraImageSize) {
			DBG_LOG("ScreenView control was not updated : %d", id);
			return TUI_SUCCESS;
		} else {
			DBG_LOG("ScreenView control was updated : %d", id);
			deleteControl(id);
		}
	}

	loc.x1 = x;
	loc.y1 = y;
	if ((getState() & SETTING_TOUCHABLE_VIEW_TUI_STATE) == SETTING_TOUCHABLE_VIEW_TUI_STATE) {
		initButton(&control, id, loc, width, height, NULL, eventImageSize, NULL, originImageSize, NULL, extraImageSize, 0);
		if (eventImageSize == 0 && originImageSize == 0) {
			DBG_LOG("added TouchableView");
			control.state = getDefaultControlState(id);
			control.isDrawingWhenAdded = 0;
			addButton(&control, getExecuteFunction(id));
		} else {
			gScreenView = control;
		}
	} else if ((getState() & SETTING_DRAWABLE_VIEW_TUI_STATE) == SETTING_DRAWABLE_VIEW_TUI_STATE) {
		initImageView(&control, id, loc, width, height, NULL, eventImageSize, NULL, originImageSize);
		if (eventImageSize == 0 && originImageSize == 0) {
			TTY_LOG("DrawableView had invalid view size");
			return TUI_SUCCESS;
		} else {
			gScreenView = control;
		}
	}
	return TUI_SUCCESS;
}

bool isExsitScreenView() {
	DBG_LOG("isExsitScreenView()");
	if (gScreenView.id == 0) {
		TTY_LOG("gScreenView is NULL");
		return false;
	} else {
		return true;
	}
}

uint32_t saveScreenViewResource(uint8_t* viewData, uint32_t dataLen) {
	DBG_LOG("saveScreenViewResource()");
	DBG_LOG("getState() : %x", getState());
	DBG_LOG("control id : %d", gScreenView.id);
	DBG_LOG("eventResourceSize : %d", gScreenView.eventResourceSize);
	DBG_LOG("originResourceSize : %d", gScreenView.originResourceSize);
	DBG_LOG("extraResourceSize : %d", gScreenView.extraResourceSize);

	if (gScreenView.eventResourceSize != 0 && gScreenView.eventResource == NULL) {
		DBG_LOG("save eventResource");
		if (gScreenView.eventResourceSize < dataLen) {
			TTY_LOG("gScreenView warn overflow. buffer : %d, dataLen : %d", gScreenView.eventResourceSize, dataLen);
			gScreenView.eventResourceSize = 0;
			return ERROR_BC_TUI;
		} else if (validatePngResource(viewData, dataLen, gScreenView.width, gScreenView.height) == false) {
			TTY_LOG("ScreenView.eventResource is invalid");
			gScreenView.eventResourceSize = 0;
			return ERROR_BC_TUI;
		}
		gScreenView.eventResource = TZ_malloc(gScreenView.eventResourceSize);
		memcpy((void*)gScreenView.eventResource, viewData, dataLen);

		if (gScreenView.originResourceSize == 0) {
			if ((getState() & SETTING_TOUCHABLE_VIEW_TUI_STATE) == SETTING_TOUCHABLE_VIEW_TUI_STATE) {
				DBG_LOG("added TouchableView");
				gScreenView.state = getDefaultControlState(gScreenView.id);
				gScreenView.isDrawingWhenAdded = 0;
				addButton(&gScreenView, getExecuteFunction(gScreenView.id));
			} else if ((getState() & SETTING_DRAWABLE_VIEW_TUI_STATE) == SETTING_DRAWABLE_VIEW_TUI_STATE) {
				DBG_LOG("added DrawableView");
				Node* node = pickingControlById(gScreenView.id);
				if (node == NULL) {
					if (getDefaultControlState(gScreenView.id) == ENABLE_CONTROL_STATE) {
						gScreenView.isDrawingWhenAdded = 1;
					} else {
						gScreenView.isDrawingWhenAdded = 0;
					}
					addImageView(&gScreenView);
				} else {
					updateImageViewControl(&(node->control), &gScreenView);
				}
			}
		}
	} else if (gScreenView.originResourceSize != 0 && gScreenView.originResource == NULL) {
		DBG_LOG("save originResource");
		if (gScreenView.originResourceSize < dataLen) {
			TTY_LOG("gScreenView(origin) warn overflow. buffer : 0x%x, dataLen : %d", gScreenView.originResource, dataLen);
			gScreenView.originResourceSize = 0;
			return ERROR_BC_TUI;
		} else if (validatePngResource(viewData, dataLen, gScreenView.width, gScreenView.height) == false) {
			gScreenView.originResourceSize = 0;
			return ERROR_BC_TUI;
		}
		gScreenView.originResource = TZ_malloc(gScreenView.originResourceSize);
		memcpy((void*)gScreenView.originResource, viewData, dataLen);

		if ((getState() & SETTING_TOUCHABLE_VIEW_TUI_STATE) == SETTING_TOUCHABLE_VIEW_TUI_STATE) {
			DBG_LOG("added TouchableView");
			if (gScreenView.extraResourceSize == 0) {
				gScreenView.state = getDefaultControlState(gScreenView.id);
				gScreenView.isDrawingWhenAdded = 0;
				addButton(&gScreenView, getExecuteFunction(gScreenView.id));
			}
		} else if ((getState() & SETTING_DRAWABLE_VIEW_TUI_STATE) == SETTING_DRAWABLE_VIEW_TUI_STATE) {
			DBG_LOG("added DrawableView");
			Node* node = pickingControlById(gScreenView.id);
			if (node == NULL) {
				if (getDefaultControlState(gScreenView.id) == ENABLE_CONTROL_STATE) {
					gScreenView.isDrawingWhenAdded = 1;
				} else {
					gScreenView.isDrawingWhenAdded = 0;
				}
				addImageView(&gScreenView);
			} else {
				updateImageViewControl(&(node->control), &gScreenView);
			}
			showFrameBuffer();
		}
	} else if (gScreenView.extraResourceSize != 0 && gScreenView.extraResource == NULL) {
		DBG_LOG("save extraResource");
		if (gScreenView.extraResourceSize < dataLen) {
			TTY_LOG("gScreenView(extra) warn overflow. buffer : %d, dataLen : %d", gScreenView.extraResource, dataLen);
			gScreenView.extraResourceSize = 0;
			return ERROR_BC_TUI;
		} else if (validatePngResource(viewData, dataLen, gScreenView.width, gScreenView.height) == false) {
			TTY_LOG("ScreenView.extraResource is invalid");
			gScreenView.extraResourceSize = 0;
			return ERROR_BC_TUI;
		}
		gScreenView.extraResource = TZ_malloc(gScreenView.extraResourceSize);
		memcpy((void*)gScreenView.extraResource, viewData, dataLen);

		if ((getState() & SETTING_TOUCHABLE_VIEW_TUI_STATE) == SETTING_TOUCHABLE_VIEW_TUI_STATE) {
			DBG_LOG("added TouchableView");
			gScreenView.state = getDefaultControlState(gScreenView.id);
			gScreenView.isDrawingWhenAdded = 0;
			addButton(&gScreenView, getExecuteFunction(gScreenView.id));
		}
	}
	return TUI_SUCCESS;
}

uint32_t initTuiBackground() {
	if (isStartTuiWithBackground() == false) {
		uint32_t ret = showBackground();
		if (ret != TUI_SUCCESS) {
			TTY_LOG("Failed to ShowBackground : %d", ret);
			return ret;
		}
	}
	else {
		clearBackgroundResource();
	}
	return TUI_SUCCESS;
}

uint32_t startBackupScreen(uint8_t* mnemonicData) {
	DBG_LOG("startBackupScreen()");
	initBackupScreenController();
	uint32_t ret = initTuiBackground();
	if (ret != TUI_SUCCESS) {
		TTY_LOG("Failed to ShowBackground at BackupScreen : %d", ret);
		return ret;
	}
	initBackupScreen(mnemonicData);

	return TUI_SUCCESS;
}


uint32_t startRestoreScreen() {
	DBG_LOG("startRestoreScreen()");
	initRestoreScreenController();
	uint32_t ret = initTuiBackground();
	if (ret != TUI_SUCCESS) {
		TTY_LOG("Failed to ShowBackground at RestoreScreen : %d", ret);
		return ret;
	}
	drawRestoreScreen();

	return TUI_SUCCESS;
}

bool setResourceDataArray(uint32_t type, uint8_t *buf) {
	if (type < 9 || type == 0xFF) {
		setResources(type, buf);
	} else {
	    TTY_LOG("error : type is not valid!");
		return false;
	}
	return true;
}

void setPreScreenState(ScreenState preState) {
	preScreenState = preState;
}

ScreenState getPreScreenState() {
	return preScreenState;
}

void changeScreenState(ScreenState state) {
	if (getScreenState() == state) {
		DBG_LOG("Don't need to change ScreenState");
		return;
	}

	setPreScreenState(getScreenState());
	screenState = state;
	DBG_LOG("changeScreenState() : %d to %d", getPreScreenState, screenState);
	switch (state) {
	case BACKUP_SCREEN_STATE:
        changeState(UPDATE_SCREEN_BACKUP_TUI_STATE);
        setControlNode(getBackupScreenControlNode(state));
	    break;
	case BACKUP_SCREEN_QUIZ_STATE:
		changeState(UPDATE_SCREEN_BACKUP_QUIZ_TUI_STATE);
		setControlNode(getBackupScreenControlNode(state));
		break;
	case RECOVERY_SCREEN_STATE:
		changeState(UPDATE_SCREEN_RECOVERY_TUI_STATE);
		setControlNode(getBackupScreenControlNode(state));
		break;
	case RESTORE_SCREEN_STATE:
		if (getPreScreenState() != RESTORE_SCREEN_TEXT_LINK_STATE) {
			changeState(UPDATE_SCREEN_RESTORE_TUI_STATE);
		}
		setControlNode(getRestoreScreenControlNode());
		break;
	case RESTORE_SCREEN_TEXT_LINK_STATE:
		changeState(TEXT_LINK_TUI_STATE);
		setControlNode(getRestoreScreenControlNode());
		break;
	case POPUP_SCREEN_BACKUP_EXIT_STATE:
		changeState(UPDATE_SCREEN_POPUP_BACKUP_EXIT_TUI_STATE);
		setControlNode(getPopupScreenControlNode());
		break;
	case POPUP_SCREEN_RESTORE_EXIT_STATE:
		changeState(UPDATE_SCREEN_POPUP_RESTORE_EXIT_TUI_STATE);
		setControlNode(getPopupScreenControlNode());
		break;
	case POPUP_SCREEN_RESTORE_FAILED_CHECKSUM_STATE:
		changeState(UPDATE_SCREEN_POPUP_RESTORE_FAILED_CHECKSUM_TUI_STATE);
		setControlNode(getPopupScreenControlNode());
		break;
	case NONE_SCREEN_STATE:
	default:
		changeState(FAILED_TUI_STATE);
		break;
	}
}

ScreenState getScreenState() {
	return screenState;
}

void redrawScreen(ScreenState screenState) {
	switch (screenState) {
	case BACKUP_SCREEN_QUIZ_STATE:
		drawBackupScreenQuiz();
		showFrameBuffer();
		break;
	case BACKUP_SCREEN_STATE:
	    initCheckboxAndNextButton();
        drawControlNode();
		break;
	case RECOVERY_SCREEN_STATE:
	case RESTORE_SCREEN_STATE:
	default:
		TTY_LOG("Failed to redraw screen : %d", screenState);
		changeState(FAILED_TUI_STATE);
		break;
	}
}

void setBackupQuizOrder(uint32_t first, uint32_t second, uint32_t third) {
	uint32_t* quizOrder = getBackupQuizOrderArray();
	quizOrder[0] = first;
	quizOrder[1] = second;
	quizOrder[2] = third;
}
