#include "Vendor_Interface.h"
#include "TZ_Vendor_debug_tl.h"
#include "tl_tui_bc_error_msg.h"
#include "TuiTextBox.h"
#include "TuiPng.h"
#include "TuiScreenResource.h"
#include "TuiLayout.h"

#include <string.h>

#define TEXT_ALIGN_MARGIN 2
#define BLANK_IMAGE_SIZE_ITOR 36
#define QUIZ_WORD_COUNT 3

static uint8_t sTextBuffer[TEXT_BUFFER_SIZE][TEXT_MAX_SIZE] = { '\0', };
static uint32_t sTextBufferIndex = 0;
static uint8_t sQuizTextBuffer[QUIZ_WORD_COUNT][TEXT_MAX_SIZE] = { '\0', };
static uint32_t sQuizTextBufferIndex = 0;

Control* initTextBox(Control* control, uint32_t id, Location loc, uint32_t width, uint32_t height,
	uint8_t* eventImg, uint32_t eventImgLen, uint8_t* originImg, uint32_t originImgLen,
	TextInfo textInfo, EContentAlign align, EContentVerticalAlign verticalAlign) {
	control->id = id;
	control->location = loc;
	control->width = width;
	control->height = height;
	control->eventResource = eventImg;
	control->eventResourceSize = eventImgLen;
	control->originResource = originImg;
	control->originResourceSize = originImgLen;
	control->extraResource = NULL;
	control->extraResourceSize = 0;
	uint8_t* buffer;

	if (BACKUP_QUIZ_TEXTINPUT_WIDTH == width && BACKUP_QUIZ_TEXTINPUT_HEIGHT == height){
	    buffer = getQuizTextBuffer();
	}
	else {
	    buffer = getTextBuffer();
	}

	control->textInfo.text = copyTextBuffer(buffer, textInfo.text);
	control->textInfo.font = textInfo.font;
	control->align = align;
	control->verticalAlign = verticalAlign;
	control->childCount = 0;
	control->isDrawingWhenAdded = 1;
	control->isTouchable = 0;
	control->changeState = NONE_TUI_STATE;
	setFocus(control, FALSE);

	return control;
}

void addTextViewBox(Control *control, EXEC_FUNC func) {
	DBG_LOG("addTextViewBox()");
	if ((control->width == 0 || control->height == 0) && control->eventResourceSize == 0 && control->originResourceSize == 0) {
		TTY_LOG("TextViewBox size is invalid");
		return;
	}

	control->kindOfControl = TEXT_VIEW_BOX_CONTROL_TYPE;
	if (func == NULL) {
		control->execute = &executeTextViewBox;
	} else {
		control->execute = func;
	}

	if (control->isDrawingWhenAdded == 1) {
		if (control->isTouchable == 1) {
			control->state = ENABLE_CONTROL_STATE;
		} else {
			control->state = DISABLE_CONTROL_STATE;
		}
		drawTextBox(control);
	} else {
		control->state = INVISIBLE_CONTROL_STATE;
	}
	addControl(control);

	return;
}

void addTextEditBox(Control *control, EXEC_FUNC func) {
	DBG_LOG("addTextEditBox()");
	if (control->width == 0 || control->height == 0) {
		TTY_LOG("TextEditBox size is invalid");
		return;
	}

	control->kindOfControl = TEXT_EDIT_BOX_CONTROL_TYPE;
	if (func == NULL) {
		control->execute = &executeTextEditBox;
	} else {
		control->execute = func;
	}

	if (control->isDrawingWhenAdded == TRUE) {
		setFocus(control, TRUE);
		control->state = ENABLE_CONTROL_STATE;
		drawTextBox(control);
	} else {
		setFocus(control, FALSE);
		control->state = INVISIBLE_CONTROL_STATE;
	}
	addControl(control);

	return;
}

uint8_t* getBlankImage(EFontType fontType, uint32_t *imageSize_output) {
	switch (fontType) {
	case FONT_TYPE_01:
		*imageSize_output = font_01_sizes[img_font_01_blank_field];
		return font_01_resource[img_font_01_blank_field];
	case FONT_TYPE_02:
		*imageSize_output = font_02_sizes[img_font_02_blank];
		return font_02_resource[img_font_02_blank];
	case FONT_TYPE_03:
		*imageSize_output = font_03_sizes[img_font_03_blank];
		return font_03_resource[img_font_03_blank];
	case FONT_TYPE_04:
		*imageSize_output = font_04_sizes[img_font_04_blank];
		return font_04_resource[img_font_04_blank];
	case FONT_TYPE_07:
		*imageSize_output = font_07_sizes[img_font_07_blank];
		return font_07_resource[img_font_07_blank];
	case FONT_TYPE_08:
		*imageSize_output = font_08_sizes[img_font_08_blank];
		return font_08_resource[img_font_08_blank];
	default:
		*imageSize_output = font_01_sizes[img_font_01_blank_field];
		return font_01_resource[img_font_01_blank_field];
	}
}

uint8_t * getFontImageResource(uint8_t charValue, EFontType fontType, uint32_t *imageSize) {
    if (charValue == ' ') {
        return getBlankImage(fontType, imageSize);
    }

	switch (fontType) {
	case FONT_TYPE_01:
		if (charValue >= '0' && charValue <= '9') {
			*imageSize = font_01_sizes[charValue - '0'];
			return font_01_resource[charValue - '0'];
		} else if (charValue >= 'a' && charValue <= 'z') {
			*imageSize = font_01_sizes[(charValue - 'a') + 10];
			return font_01_resource[(charValue - 'a') + 10];
		}
		break;

	case FONT_TYPE_02:
		if (charValue >= '0' && charValue <= '9') {
			*imageSize = font_02_sizes[charValue - '0'];
			return font_02_resource[charValue - '0'];
		} else if (charValue >= 'a' && charValue <= 'z') {
			*imageSize = font_02_sizes[(charValue - 'a') + 10];
			return font_02_resource[(charValue - 'a') + 10];
        } else if (charValue >= 'A' && charValue <= 'Z') {
            *imageSize = font_02_sizes[(charValue - 'A') + 36];
            return font_02_resource[(charValue - 'A') + 36];
        } else if (charValue == '!') {
            *imageSize = font_02_sizes[img_font_02_exclamation];
            return font_02_resource[img_font_02_exclamation];
        } else if (charValue == '(') {
            *imageSize = font_02_sizes[img_font_02_left_parenthesis];
            return font_02_resource[img_font_02_left_parenthesis];
        } else if (charValue == ')') {
            *imageSize = font_02_sizes[img_font_02_right_parenthesis];
            return font_02_resource[img_font_02_right_parenthesis];
        } else if (charValue == ',') {
            *imageSize = font_02_sizes[img_font_02_comma];
            return font_02_resource[img_font_02_comma];
		} else if (charValue == '-') {
			*imageSize = font_02_sizes[img_font_02_hyphen];
			return font_02_resource[img_font_02_hyphen];
		}  else if (charValue == '\'') {
			*imageSize = font_02_sizes[img_font_02_apostrophe];
			return font_02_resource[img_font_02_apostrophe];
		} else if (charValue == ':') {
			*imageSize = font_02_sizes[img_font_02_colon];
			return font_02_resource[img_font_02_colon];
		} else if (charValue == '.') {
			*imageSize = font_02_sizes[img_font_02_dot];
			return font_02_resource[img_font_02_dot];
		}
		break;

	case FONT_TYPE_03:
		if (charValue >= '0' && charValue <= '9') {
			*imageSize = font_03_sizes[charValue - '0'];
			return font_03_resource[charValue - '0'];
		} else if (charValue >= 'a' && charValue <= 'z') {
			*imageSize = font_03_sizes[(charValue - 'a') + 10];
			return font_03_resource[(charValue - 'a') + 10];
		} else if (charValue == '.') {
			*imageSize = font_03_sizes[img_font_03_dot];
			return font_03_resource[img_font_03_dot];
		} else if (charValue == ':') {
			*imageSize = font_03_sizes[img_font_03_colon];
			return font_03_resource[img_font_03_colon];
		} 
		break;

	case FONT_TYPE_04:
		if (charValue >= '0' && charValue <= '9') {
			*imageSize = font_04_sizes[charValue - '0'];
			return font_04_resource[charValue - '0'];
		} else if (charValue >= 'a' && charValue <= 'z') {
			*imageSize = font_04_sizes[(charValue - 'a') + 10];
			return font_04_resource[(charValue - 'a') + 10];
		} else if (charValue >= 'A' && charValue <= 'Z') {
			*imageSize = font_04_sizes[(charValue - 'A') + 36];
			return font_04_resource[(charValue - 'A') + 36];
		} else if (charValue == '.') {
            *imageSize = font_04_sizes[img_font_04_dot];
            return font_04_resource[img_font_04_dot];
        } else if (charValue == '!') {
            *imageSize = font_04_sizes[img_font_04_exclamation];
            return font_04_resource[img_font_04_exclamation];
        } else if (charValue == '(') {
            *imageSize = font_04_sizes[img_font_04_left_parenthesis];
            return font_04_resource[img_font_04_left_parenthesis];
        } else if (charValue == ')') {
            *imageSize = font_04_sizes[img_font_04_right_parenthesis];
            return font_04_resource[img_font_04_right_parenthesis];
        } else if (charValue == ',') {
            *imageSize = font_04_sizes[img_font_04_comma];
            return font_04_resource[img_font_04_comma];
        } else if (charValue == '-') {
            *imageSize = font_04_sizes[img_font_04_hyphen];
            return font_04_resource[img_font_04_hyphen];
        }

		break;

	case FONT_TYPE_07:
		if (charValue >= '0' && charValue <= '9') {
			*imageSize = font_07_sizes[charValue - '0'];
			return font_07_resource[charValue - '0'];
		} else if (charValue >= 'a' && charValue <= 'z') {
			*imageSize = font_07_sizes[(charValue - 'a') + 10];
			return font_07_resource[(charValue - 'a') + 10];
		} else if (charValue >= 'A' && charValue <= 'Z') {
			*imageSize = font_07_sizes[(charValue - 'A') + 36];
			return font_07_resource[(charValue - 'A') + 36];
		} else if (charValue == '.') {
			*imageSize = font_07_sizes[img_font_07_dot];
			return font_07_resource[img_font_07_dot];
		}
		break;

	case FONT_TYPE_08:
		if (charValue >= '0' && charValue <= '9') {
			*imageSize = font_08_sizes[charValue - '0'];
			return font_08_resource[charValue - '0'];
		} else if (charValue >= 'A' && charValue <= 'Z') {
            *imageSize = font_08_sizes[(charValue - 'A') + 10];
            return font_08_resource[(charValue - 'A') + 10];
        } else if (charValue == '.') {
			*imageSize = font_08_sizes[img_font_08_dot];
			return font_08_resource[img_font_08_dot];
		}
		break;
    case FONT_TYPE_09:
    	if (charValue >= '1' && charValue <= '6') {
    		*imageSize = font_09_sizes[charValue - '1'];
    		return font_09_resource[charValue - '1'];
    	} else if (charValue == '/'){
    		*imageSize = font_09_sizes[img_font_09_slash];
    		return font_09_resource[img_font_09_slash];
    	}
    	break;
	default:
		TTY_LOG("Invalid font type for getting font image : %d", fontType);
		return getBlankImage(fontType, imageSize);
	}

	TTY_LOG("%d does not support %c", fontType, charValue);
	return getBlankImage(fontType, imageSize);
}

void coordinatePositionByAlign(Control* control, uint32_t* x, uint32_t* y) {
	uint32_t textWidth = 0;
	uint32_t fontHeight = 0;
	getTextWidthAndHeight(control->textInfo.text, control->textInfo.font, &textWidth, &fontHeight);
	
	if (control->align == CENTER_CONTENT_ALIGN) {
		*x += ((control->width / 2) - (textWidth / 2));
	} else if (control->align == RIGHT_CONTENT_ALIGN) {
		*x += (control->width - textWidth);
	}

	if (textWidth < control->width && control->verticalAlign == VERTICAL_CENTER_CONTENT_ALIGN) {
		*y += (control->height / 2) - (fontHeight / 2);
	}

}

void updateBufferCursorWithMargin(uint32_t x, uint32_t y, uint32_t margin) {
	uint32_t cursorWidth = 0;
	uint32_t cursorHeight = 0;
	getPngWidthAndHeight(font_01_resource[img_font_01_text_field_input_cursor], &cursorWidth, &cursorHeight);
	drawImage(x + margin, y, font_01_resource[img_font_01_text_field_input_cursor], font_01_sizes[img_font_01_text_field_input_cursor]);
}

void updateBufferCursor(uint32_t x, uint32_t y) {
	updateBufferCursorWithMargin(x, y, 0);
}

void clearCursor(uint32_t x, uint32_t y, uint32_t margin) {
	drawImage(x + margin, y, font_01_resource[img_font_01_blank_cursor], font_01_sizes[img_font_01_blank_cursor]);
}

void drawTextBox(Control *control) {
	DBG_LOG("drawTextBox() : %s, %d", control->textInfo.text, (uint32_t)strlen((char *)control->textInfo.text));
	uint32_t posX = control->location.x1;
	uint32_t posY = control->location.y1;

	switch (control->state) {
	case RELEASED_CONTROL_STATE:
	case ENABLE_CONTROL_STATE:
		if (control->focused == TRUE && control->eventResource != NULL) {
			drawImage(posX, posY, control->eventResource, control->eventResourceSize);
		} else {
			if (control->originResource != NULL) {
				drawImage(posX, posY, control->originResource, control->originResourceSize);
			} else {
				TTY_LOG("%s drawTextBox() originResource is NULL - control:%d, state:%d ", LOG_TAG, control->kindOfControl, control->state);
			}
		}
		control->state = ENABLE_CONTROL_STATE;
		break;
	case DISABLE_CONTROL_STATE:
		if (control->originResource != NULL) {
			drawImage(posX, posY, control->originResource, control->originResourceSize);
		} else {
			TTY_LOG("%s drawTextBox() originResource is NULL - control:%d, state:%d ", LOG_TAG, control->kindOfControl, control->state);
		}
		break;
	default:
		TTY_LOG("%s drawTextBox unknown state! : %d - control:%d", LOG_TAG, control->state, control->kindOfControl);
		return;
	}

	coordinatePositionByAlign(control, &posX, &posY);

	uint32_t index = 0;

	uint32_t lineIndex = 0;
	uint8_t tempText[TEXT_MAX_SIZE];
	uint32_t textWidth = 0;
	uint32_t textHeight = 0;
	uint32_t charWidth = 0;

	for (uint32_t i = 0; i < strlen((char *)control->textInfo.text); i++) {
		uint32_t imageSize;
		uint8_t *fontImage = getFontImageResource(*(control->textInfo.text + i), control->textInfo.font, &imageSize);
		if (fontImage != NULL) {
			memset(tempText, '\0', TEXT_MAX_SIZE);
			if (index != 0) {
				memcpy(tempText, &control->textInfo.text[lineIndex], index);
			}

			getCharWidthAndHeight(*(control->textInfo.text + i), control->textInfo.font, &charWidth, &textHeight);
			if (textWidth + charWidth > control->width) {
				if (control->align == LEFT_CONTENT_ALIGN) {
					lineIndex = index;
					index = 0;
					memset(tempText, '\0', TEXT_MAX_SIZE);
					textWidth = 0;
					posY += textHeight;
					control->height = posY + textHeight;
				} else {
					TTY_LOG("%d does not support multi line", control->align);
				}
			}
			else {
				DBG_LOG("%s drawTextBox() textWidth:%d > control->width:%d", LOG_TAG, textWidth, control->width);
			}
			drawImage(posX + textWidth, posY, fontImage, imageSize);
			textWidth += charWidth;
		}
		index++;
	}

	if (control->kindOfControl == TEXT_EDIT_BOX_CONTROL_TYPE) {
		EControlState state = control->state;
		control->state = UPDATED_CONTROL_STATE;
		control->execute(control);
		control->state = state;
	}
}

bool updateText(Control *control, uint8_t *text) {
	DBG_LOG("updateText()");
	
	if (strcmp((char *)control->textInfo.text, (char *)text) == 0) {
		DBG_LOG("%s updateText() string length is 0", LOG_TAG);
		return false;
	} 

	copyTextBuffer(control->textInfo.text, text);

	return true;
}

bool inputChar(uint8_t charValue) {
	DBG_LOG("inputChar() : %c", charValue);
	Node *node = pickingFocusedControl(TEXT_EDIT_BOX_CONTROL_TYPE);
	if (node == NULL) {
		TTY_LOG("FocusedControl is NULL");
		return false;
	}

	uint32_t strLen = (uint32_t)strlen((char *)node->control.textInfo.text);
	if (strLen >= MNEMONIC_MAX_SIZE) {
		TTY_LOG("Can not input because of buffer size");
		return false;
	}

	uint32_t textWidth;
	uint32_t textHeight;
	if (strLen == 0) {
		textWidth = 0;
		textHeight = 0;
	} else {
		getTextWidthAndHeight(node->control.textInfo.text, node->control.textInfo.font, &textWidth, &textHeight);
	}

	*((node->control.textInfo.text) + strLen) = charValue;

	uint32_t posX = node->control.location.x1;
	uint32_t posY = node->control.location.y1;
	coordinatePositionByAlign(&node->control, &posX, &posY);

	uint32_t imageSize;
	uint8_t *fontImage = getFontImageResource(charValue, node->control.textInfo.font, &imageSize);
	drawImage(posX + textWidth, node->control.location.y1, font_01_resource[img_font_01_blank_cursor], font_01_sizes[img_font_01_blank_cursor]);
	drawImage(posX + textWidth, posY, fontImage, imageSize);

	EControlState state = node->control.state;
	node->control.state = UPDATED_CONTROL_STATE;
	node->control.execute(&node->control);
	node->control.state = state;

	return true;
}

bool deleteChar() {
	DBG_LOG("deleteChar()");
	Node *node = pickingFocusedControl(TEXT_EDIT_BOX_CONTROL_TYPE);
	if (node == NULL) {
		TTY_LOG("%s FocusedControl is NULL", LOG_TAG);
		return false;
	}

	uint32_t strLen = (uint32_t)strlen((char *)node->control.textInfo.text);
	if (strLen == 0) {
		return false;
	}
	*((node->control.textInfo.text) + (strLen - 1)) = '\0';

	uint32_t posX = node->control.location.x1;
	uint32_t posY = node->control.location.y1;
	coordinatePositionByAlign(&node->control, &posX, &posY);

	uint32_t textWidth = 0;
	uint32_t textHeight = 0;
	getTextWidthAndHeight(node->control.textInfo.text, node->control.textInfo.font, &textWidth, &textHeight);
	drawImage(posX + textWidth, node->control.location.y1, font_01_resource[img_font_01_blank_cursor], font_01_sizes[img_font_01_blank_cursor]);

	EControlState state = node->control.state;
	node->control.state = UPDATED_CONTROL_STATE;
	node->control.execute(&node->control);
	node->control.state = state;

	return true;
}

bool clearTextEdit(uint8_t* clearImage, uint32_t imageSize) {
	DBG_LOG("clearTextEdit()");
	Node *node = pickingFocusedControl(TEXT_EDIT_BOX_CONTROL_TYPE);
	if (node == NULL) {
		TTY_LOG("FocusedControl is NULL");
		return false;
	}

	uint32_t strLen = (uint32_t)strlen((char *)node->control.textInfo.text);
	if (strLen > 0) {
		memset(node->control.textInfo.text, '\0', strLen);
		drawImage(node->control.location.x1, node->control.location.y1, clearImage, imageSize);
	}

	EControlState state = node->control.state;
	node->control.state = UPDATED_CONTROL_STATE;
	node->control.execute(&node->control);
	node->control.state = state;

	return true;
}

ResponseControl executeTextEditBox(Control *control) {
	DBG_LOG("executeTextEditBox");
	ResponseControl rc;
	initResponseControl(&rc);

	switch (control->state) {
	case PRESSED_CONTROL_STATE:
		break;
	case RELEASED_CONTROL_STATE:
		break;
	case NONE_CONTROL_STATE:
		break;
	default:
		break;
	}
	return rc;
}

ResponseControl executeTextViewBox(Control *control) {
	DBG_LOG("executeTextViewBox");
	ResponseControl rc;
	initResponseControl(&rc);

	switch (control->state) {
	case PRESSED_CONTROL_STATE:
		break;
	case RELEASED_CONTROL_STATE:
		break;
	case NONE_CONTROL_STATE:
		break;
	default:
		break;
	}
	return rc;
}

void initTextBuffer() {
	if (sTextBufferIndex != 0) {
		for (uint32_t i = 0; i < sTextBufferIndex; i++) {
			memset(sTextBuffer[i], '\0', TEXT_MAX_SIZE);
		}
		sTextBufferIndex = 0;
	}

	for (uint32_t i = 0; i < QUIZ_WORD_COUNT; i++) {
		memset(sQuizTextBuffer[i], '\0', TEXT_MAX_SIZE);
	}
	sQuizTextBufferIndex = 0;
}


uint8_t* getQuizTextBuffer() {
	sQuizTextBufferIndex %= QUIZ_WORD_COUNT;

	memset(sQuizTextBuffer[sQuizTextBufferIndex], '\0', TEXT_MAX_SIZE);

	return sQuizTextBuffer[sQuizTextBufferIndex++];
}

uint8_t* getTextBuffer() {
	if (sTextBufferIndex > TEXT_BUFFER_SIZE) {
		TTY_LOG("TextBuffer is over");
		return NULL;
	}
	memset(sTextBuffer[sTextBufferIndex], '\0', TEXT_MAX_SIZE);

	return sTextBuffer[sTextBufferIndex++];
}

uint8_t* copyTextBuffer(uint8_t *toBuffer, uint8_t *fromBuffer) {
	if (strlen((char *)toBuffer) != 0) {
		memset(toBuffer, '\0', TEXT_MAX_SIZE);
	}

	uint32_t textLen = (uint32_t)strlen((char *)fromBuffer);
	if (textLen > 0) {
		if (textLen > TEXT_MAX_SIZE) {
			TTY_LOG("Text size is bigger than TEXT_MAX_SIZE(255)");
			memcpy(toBuffer, fromBuffer, TEXT_MAX_SIZE);
		}
		else {
			memcpy(toBuffer, fromBuffer, strlen((char *)fromBuffer));
		}
	}

	return toBuffer;
}

uint32_t getFontHeight(EFontType fontType) {
	uint32_t width = 0;
	uint32_t height = 0;
	uint8_t* image;

	switch (fontType) {
	case FONT_TYPE_01:
		image = font_01_resource[0];
		break;
	case FONT_TYPE_02:
		image = font_02_resource[0];
		break;
	case FONT_TYPE_03:
		image = font_03_resource[0];
		break;
	case FONT_TYPE_04:
		image = font_04_resource[0];
		break;
	case FONT_TYPE_07:
		image = font_07_resource[0];
		break;
	case FONT_TYPE_08:
		image = font_08_resource[0];
		break;
	case FONT_TYPE_09:
		image = font_09_resource[0];
		break;
	default:
		TTY_LOG("Invalid font type for getting font image : %d", fontType);
		return 0;
	}

	getPngWidthAndHeight(image, &width, &height);

	return height;
}

void getCharWidthAndHeight(uint8_t charValue, EFontType fontType, uint32_t* width, uint32_t* height) {
	uint32_t size;
	uint8_t* image = getFontImageResource(charValue, fontType, &size);
	getPngWidthAndHeight(image, width, height);
}

void getTextWidthAndHeight(uint8_t* text, EFontType fontType, uint32_t* width, uint32_t* height) {
	if (strlen((char *)text) == 0) {
		*width = 0;
		*height = 0;
		return;
	}

	uint32_t textWidth = 0;
	uint32_t charWidth = 0;
	uint32_t charHeight = 0;
	for (uint32_t i = 0; i < strlen((char *)text); i++) {
		getCharWidthAndHeight(text[i], fontType, &charWidth, &charHeight);
		textWidth += charWidth;
	}

	*width = textWidth;
	*height = charHeight;
}
