#include "TZ_Vendor_debug_tl.h"
#include "tl_tui_bc_error_msg.h"
#include "Vendor_Interface.h"

#include "TuiButton.h"
#include "TuiConfirmScreenController.h"
#include "TuiTextBox.h"
#include "TuiImageView.h"
#include "TuiScreenResource.h"
#include "TuiPng.h"
#include <string.h>

#include "TuiLayout.h"

#if defined(W1536)
#include "TuiLayout_w1536.h"
#elif defined(DPI640)
#include "TuiLayout_w1440.h"
#elif defined(DPI480)
#include "TuiLayout_w1080.h"
#elif defined(DPI320)
#include "TuiLayout_w720.h"
#else
#include "TuiLayout_w1080.h"
#endif

#define MAX_LENGTH 35

#define MAX_STRING 100
#define MAX_DATA_STRING 210
#define MAX_VALUE 100
#define MAX_UNIT 4
#define MAX_TO_ADDRESS 10

#define MAX_AMOUNT_STR_LENGTH 27
#define MAX_TOKEN_STR_LENGTH 22

#ifdef _WIN32
#define strtok_r strtok_s
#endif

typedef enum COIN_TYPE {
	NONE_COIN, BTC, ETH, UNKNOWN_COIN
} CoinType;

typedef enum METHOD {
	Transfer, Transferfrom, approve, allowance
} Method;

typedef enum AMOUNT_TYPE {
    AMOUNT_NONE, AMOUNT_ETH, AMOUNT_TOKEN
} AmountType;

typedef struct {
	CoinType coinType; // can use it for unit.
	uint8_t method[MAX_STRING];
	uint8_t toAddress[MAX_TO_ADDRESS][MAX_STRING];
	uint32_t toAddressCount;
	uint32_t addressNumber[MAX_STRING]; // for BTC

	uint8_t amount[MAX_VALUE];
	uint8_t amountUnit[MAX_UNIT];

	uint8_t tokenName[MAX_STRING];

	uint8_t tokenAmount[MAX_VALUE];
	uint8_t tokenAmountUnit[MAX_VALUE];

	uint8_t gasPrice[MAX_VALUE];
	uint8_t gasPriceUnit[MAX_UNIT];

	uint8_t gasLimit[MAX_VALUE];

	uint8_t dataLength[MAX_VALUE];
	uint8_t data[MAX_DATA_STRING];

	uint8_t fee[MAX_VALUE];
	uint8_t feeUnit[MAX_UNIT];

	uint8_t total[MAX_VALUE];
	uint8_t totalUnit[MAX_UNIT];

} TransactionStr;

static char emptyString[1] = { '\0' };

Node gConfirmScreenControlNode[MAX_CONTROL];

uint32_t initConfirmScreenController() {
	initLayout();
	initTextBuffer();

	return TIMA_SUCCESS;
}

Node* getConfirmScreenControlNode() {
	return gConfirmScreenControlNode;
}

uint32_t paserTransactionStr(TransactionStr* tranStr, uint8_t* str) {
	char* ret_itor = (char*)str;
	char* remain_itor = (char*)str;
	char* itor_unit;
	char* itor_number;

	const char unit_delimit[] = " ";
	const char delimit[] = ",:";
	uint32_t ret = 0;
	size_t length = 0;
	
	//init
	memset(tranStr, 0, sizeof(TransactionStr));

	do {
		ret_itor = strtok_r(remain_itor, delimit, &remain_itor);
		if (ret_itor == NULL)
			break;

		if (strcmp(ret_itor, "type") == 0) {
			ret_itor = strtok_r(remain_itor, delimit, &remain_itor);
			if (strcmp(ret_itor, "BTC") == 0) {
				tranStr->coinType = BTC;
			}
			else if (strcmp(ret_itor, "ETH") == 0) {
				tranStr->coinType = ETH;
			}
			else {
				TTY_LOG("%s unknown coin type : %s", LOG_TAG, ret_itor);
				return ERROR_BC_TUI;
			}
			continue;
		}

		if (strcmp(ret_itor, "method") == 0) {
			ret_itor = strtok_r(remain_itor, delimit, &remain_itor);
			strcpy((char*)tranStr->method, ret_itor);
			continue;
		}

		if (strncmp(ret_itor, "recipientaddress", strlen("recipientaddress")) == 0) {
			if (tranStr->coinType == BTC) {
				strtok_r(ret_itor, " ", &itor_number);
				strcpy((char*)tranStr->addressNumber, itor_number);
			}

			ret_itor = strtok_r(remain_itor, delimit, &remain_itor);
			strcpy((char*)tranStr->toAddress[tranStr->toAddressCount], ret_itor);
			//addressNumber = ;
			++tranStr->toAddressCount;
			continue;
		}

		if (strcmp(ret_itor, "contractaddress") == 0) {
			ret_itor = strtok_r(remain_itor, delimit, &remain_itor);
			strcpy((char*)tranStr->toAddress[tranStr->toAddressCount], ret_itor);
			++tranStr->toAddressCount;
			continue;
		}

		if (strcmp(ret_itor, "amount") == 0) {
			ret_itor = strtok_r(remain_itor, delimit, &remain_itor);
			//ret_itor = strtok_r(ret_itor, unit_delimit, &itor_unit);
			strcpy((char*)tranStr->amount, ret_itor);
			//strcpy((char*)tranStr->amountUnit, itor_unit);
			continue;
		}

		if (strcmp(ret_itor, "tokenamount") == 0) {
			ret_itor = strtok_r(remain_itor, delimit, &remain_itor);
			//ret_itor = strtok_r(ret_itor, unit_delimit, &itor_unit);
			strcpy((char*)tranStr->tokenAmount, ret_itor);
			//strcpy((char*)tranStr->tokenAmountUnit, itor_unit);
			continue;
		}

		if (strcmp(ret_itor, "tokenname") == 0) {
			ret_itor = strtok_r(remain_itor, delimit, &remain_itor);
			//ret_itor = strtok_r(ret_itor, unit_delimit, &itor_unit);
			strcpy((char*)tranStr->tokenName, ret_itor);
			//strcpy((char*)tranStr->tokenAmountUnit, itor_unit);
			continue;
		}

		if (strcmp(ret_itor, "gasprice") == 0) {
			ret_itor = strtok_r(remain_itor, delimit, &remain_itor);
			//ret_itor = strtok_r(ret_itor, unit_delimit, &itor_unit);
			strcpy((char*)tranStr->gasPrice, ret_itor);
			//strcpy((char*)tranStr->gasPriceUnit, itor_unit);
			continue;
		}

		if (strcmp(ret_itor, "gaslimit") == 0) {
			ret_itor = strtok_r(remain_itor, delimit, &remain_itor);
			//ret_itor = strtok_r((char*)ret_itor, unit_delimit, &itor_unit);
			strcpy((char*)tranStr->gasLimit, ret_itor);
			continue;
		}
	
		if (strcmp(ret_itor, "data") == 0) {
			ret_itor = strtok_r(remain_itor, delimit, &remain_itor);
			strcpy((char*)tranStr->data, ret_itor);
			continue;
		}

		if (strcmp(ret_itor, "datalength") == 0) {
			ret_itor = strtok_r(remain_itor, delimit, &remain_itor);
			strcpy((char*)tranStr->dataLength, ret_itor);
			continue;
		}

		if (strcmp(ret_itor, "fee") == 0) {
			ret_itor = strtok_r(remain_itor, delimit, &remain_itor);
			//ret_itor = strtok_r((char*)ret_itor, unit_delimit, &itor_unit);
			strcpy((char*)tranStr->fee, ret_itor);
			//strcpy((char*)tranStr->feeUnit, itor_unit);
			continue;
		}

		if (strcmp(ret_itor, "total") == 0) {
			ret_itor = strtok_r(remain_itor, delimit, &remain_itor);
			//ret_itor = strtok_r((char*)ret_itor, unit_delimit, &itor_unit);
			strcpy((char*)tranStr->total, ret_itor);
			//strcpy((char*)tranStr->feeUnit, itor_unit);
			continue;
		}

	} while (remain_itor != '\0');

	return TIMA_SUCCESS;
}

bool isZeroAmount(char* str) {
	char amountStr[3] = { str[0], str[1], '\0' };
	char zeroAmountStr[3] = "0 \0";

	if (strcmp(amountStr, zeroAmountStr) == 0) {
		return true;
	}
	return false;
}

void showUnit(EFontType fontType, Location loc, uint8_t* unit, uint32_t* width, uint32_t* height) {
	uint8_t* resource;
	uint32_t resourceSize = 0;

	resource = getTextImageResource(unit, fontType, &resourceSize);
	getPngWidthAndHeight(resource, width, height);

	showPng(SCREEN_WIDTH - (*width + CONFIRM_DIVIDER_SIDE_MARGIN), loc.y1, resource, resourceSize);

	return;
}

void showDivider(Location *loc, uint32_t topMargin) {
	uint8_t* resource;
	uint32_t resourceSize = 0;
	uint32_t resourceWidth = 0;
	uint32_t resourceHeight = 0;

	loc->y1 += topMargin;
	resource = getScreenResource(ID_RESOURCE_CONFIRM_DIVIDER, &resourceSize);
	showPng(CONFIRM_DIVIDER_SIDE_MARGIN, loc->y1, resource, resourceSize);
	getPngWidthAndHeight(resource, &resourceWidth, &resourceHeight);

	loc->y1 += resourceHeight;

	return;
}

bool isEmptyStr(char* str) {
	if (strcmp(str, emptyString) == 0) {
		return true;
	}
	return false;
}

bool drawAddresses(uint32_t id, TransactionStr* tranStr, Location *loc) {
	TextInfo textInfo;
	Control control;

	for (uint32_t i = 0; i < tranStr->toAddressCount && i < MAX_TO_ADDRESS; i++) {
		if (isEmptyStr((char*)tranStr->toAddress[i]) == true) {
			return false;
		}

		if (i > 0) {
			loc->y1 += CONFIRM_TEXT_2ND_ADDRESS_Y_DISTANCE;
		}

		textInfo.font = getScreenFontType(ID_FONT_TYPE_CONFIRM_ADDRESS);
		textInfo.text = tranStr->toAddress[i];
		initTextBox(&control, id, *loc, CONFIRM_TEXT_AREA_WIDTH, CONFIRM_TEXT_ADDRESS_HEIGHT, NULL, 0, NULL, 0, textInfo, LEFT_CONTENT_ALIGN);
		addTextViewBox(&control, executeTextViewBox);
		loc->y1 += CONFIRM_TEXT_ADDRESS_HEIGHT;
	}
	return false;
}

bool drawTokenName(uint32_t id, TransactionStr* tranStr, Location *loc) {
	TextInfo textInfo;
	Control control;
	uint32_t fontHeight;
	
	if (isEmptyStr((char*)tranStr->tokenName) == false) {
		textInfo.text = tranStr->tokenName;
		textInfo.font = getScreenFontType(ID_FONT_TYPE_CONFIRM_AMOUNT);

		fontHeight = getFontHeight(textInfo.font);
		loc->y1 += CONFIRM_TEXT_TOKEN_NAME_LINE_DISTANCE;

		initTextBox(&control, id, *loc, CONFIRM_TEXT_AREA_WIDTH, fontHeight, NULL, 0, NULL, 0, textInfo, RIGHT_CONTENT_ALIGN);
		addTextViewBox(&control, executeTextViewBox);
		loc->y1 += fontHeight;

		return true;
	}
	return false;
}
uint32_t drawAmount(uint32_t id, uint8_t* str, Location *loc, AmountType type) {
	TextInfo textInfo;
	Control control;
	uint32_t fontHeight;
	
	uint32_t resourceWidth = 0;
	uint32_t resourceHeight = 0;

	if (isEmptyStr((char*)str) == false) {
		textInfo.text = str;
		textInfo.font = getScreenFontType(ID_FONT_TYPE_CONFIRM_AMOUNT);

		loc->y1 += CONFIRM_TEXT_AMOUNT_LINE_DISTANCE;
		fontHeight = getFontHeight(textInfo.font);

		// if str length > MAX_AMOUNT_STR_LENGTH, show below
		int maxSize  = MAX_AMOUNT_STR_LENGTH;
		if(type == AMOUNT_TOKEN){
		    maxSize = MAX_TOKEN_STR_LENGTH;
		}
		if(strlen((char*)str) > maxSize) {
			loc->y1 += (fontHeight + CONFIRM_TEXT_GAS_PRICE_LINE_DISTANCE);
		}

		initTextBox(&control, id, *loc, CONFIRM_TEXT_AREA_WIDTH - resourceWidth - (resourceHeight / 2), fontHeight, NULL, 0, NULL, 0, textInfo, RIGHT_CONTENT_ALIGN);
		addTextViewBox(&control, executeTextViewBox);
		loc->y1 += fontHeight;

		return true;
	}
	return false;
}

uint32_t drawGasPrice(uint32_t id, uint8_t* str, Location *loc) {
	TextInfo textInfo;
	Control control;
	uint32_t fontHeight;

	uint32_t resourceWidth = 0;
	uint32_t resourceHeight = 0;

	if (isEmptyStr((char*)str) == false && strcmp((char*)str, "0\0") != 0) {
		textInfo.text = str;
		textInfo.font = getScreenFontType(ID_FONT_TYPE_CONFIRM_GAS);

		loc->y1 += CONFIRM_TEXT_GAS_PRICE_LINE_DISTANCE;
		fontHeight = getFontHeight(textInfo.font);

		initTextBox(&control, id, *loc, CONFIRM_TEXT_AREA_WIDTH - resourceWidth - (resourceHeight / 2), fontHeight, NULL, 0, NULL, 0, textInfo, RIGHT_CONTENT_ALIGN);
		addTextViewBox(&control, executeTextViewBox);
		loc->y1 += fontHeight;

		return true;
	}
	return false;
}

uint32_t drawGasLimit(uint32_t id, uint8_t* str, Location *loc) {
	TextInfo textInfo;
	Control control;
	uint32_t fontHeight;

	uint32_t resourceWidth = 0;
	uint32_t resourceHeight = 0;

	if (isEmptyStr((char*)str) == false && strcmp((char*)str, "0\0") != 0) {
		textInfo.text = str;
		textInfo.font = getScreenFontType(ID_FONT_TYPE_CONFIRM_GAS);

		loc->y1 += CONFIRM_TEXT_GAS_LIMIT_LINE_DISTANCE;
		fontHeight = getFontHeight(textInfo.font);

		initTextBox(&control, id, *loc, CONFIRM_TEXT_AREA_WIDTH - resourceWidth - (resourceHeight / 2), fontHeight, NULL, 0, NULL, 0, textInfo, RIGHT_CONTENT_ALIGN);
		addTextViewBox(&control, executeTextViewBox);
		loc->y1 += fontHeight;

		return true;
	}
	return false;
}

bool drawTokenAmount(uint32_t id, TransactionStr* tranStr, Location *loc) {
	return drawAmount(id, tranStr->tokenAmount, loc, AMOUNT_TOKEN);
}

bool drawFee(uint32_t id, TransactionStr* tranStr, Location *loc) {
	return drawAmount(id, tranStr->fee, loc, AMOUNT_NONE);
}

bool drawDataLength(uint32_t id, TransactionStr* tranStr, Location loc) {
	TextInfo textInfo;
	Control control;
	uint32_t fontHeight;

	uint32_t resourceWidth = 0;
	uint32_t resourceHeight = 0;

	Location dataLengthLoc;
	dataLengthLoc.x1 = loc.x1;
	dataLengthLoc.y1 = loc.y1;

	if (isEmptyStr((char*)tranStr->dataLength) == false) {
		textInfo.font = getScreenFontType(ID_FONT_TYPE_CONFIRM_DATA);
		textInfo.text = tranStr->dataLength;

		dataLengthLoc.y1 = loc.y1 + CONFIRM_TEXT_DATA_LENGTH_LINE_DISTANCE;
		fontHeight = getFontHeight(textInfo.font);

		initTextBox(&control, id, dataLengthLoc, CONFIRM_TEXT_AREA_WIDTH, fontHeight, NULL, 0, NULL, 0, textInfo, RIGHT_CONTENT_ALIGN);
		addTextViewBox(&control, executeTextViewBox);

		return true;
	}
	return false;
}


bool drawData(uint32_t id, TransactionStr* tranStr, Location *loc) {
	TextInfo textInfo;
	Control control;
	uint32_t fontHeight;

	uint32_t resourceWidth = 0;
	uint32_t resourceHeight = 0;

	if (isEmptyStr((char*)tranStr->data) == false) {
		textInfo.font = getScreenFontType(ID_FONT_TYPE_CONFIRM_DATA);
		textInfo.text = tranStr->data;
		
		loc->y1 += CONFIRM_TEXT_DATA_LINE_DISTANCE;
		fontHeight = getFontHeight(textInfo.font);

		initTextBox(&control, id, *loc, CONFIRM_TEXT_AREA_WIDTH, fontHeight, NULL, 0, NULL, 0, textInfo, LEFT_CONTENT_ALIGN);
		addTextViewBox(&control, executeTextViewBox);

		return true;
	}
	return false;
}

uint32_t drawTotal(uint32_t id, uint8_t* str, Location *loc) {
	TextInfo textInfo;
	Control control;
	uint32_t fontHeight;

	uint32_t resourceWidth = 0;
	uint32_t resourceHeight = 0;

	if (isEmptyStr((char*)str) == false && strcmp((char*)str, "0\0") != 0) {
		textInfo.text = str;
		textInfo.font = getScreenFontType(ID_FONT_TYPE_CONFIRM_AMOUNT);

		loc->y1 += CONFIRM_DIVIDER_LINE_DISTANCE2;
		fontHeight = getFontHeight(textInfo.font);

        // if str length > MAX_AMOUNT_STR_LENGTH, show below
        if(strlen((char*)str) > MAX_AMOUNT_STR_LENGTH) {
            loc->y1 += (fontHeight + CONFIRM_TEXT_GAS_PRICE_LINE_DISTANCE);
        }

		initTextBox(&control, id, *loc, CONFIRM_TEXT_AREA_WIDTH - resourceWidth - (resourceHeight / 2), fontHeight, NULL, 0, NULL, 0, textInfo, RIGHT_CONTENT_ALIGN);
		addTextViewBox(&control, executeTextViewBox);
		loc->y1 += fontHeight;

		return true;
	}
	return false;
}

uint32_t drawScreenForETH(TransactionStr tranStr) {
	Control control;
	Location loc;
	uint32_t controlId = 1;
	TextInfo textInfo;
	uint32_t fontHeight = 0;

	uint32_t resourceWidth = 0;
	uint32_t resourceHeight = 0;
	bool ret = 0;

	loc.x1 = CONFIRM_TEXT_SIDE_MARGIN;
	loc.y1 = CONFIRM_TEXT_ADDRESS_TOP_MARGIN;

	ret = drawAddresses(controlId++, &tranStr, &loc);

	drawTokenName(controlId++, &tranStr, &loc);

	ret = drawTokenAmount(controlId++, &tranStr, &loc);
	
	showDivider(&loc, CONFIRM_DIVIDER_LINE_DISTANCE);
	
	if (isEmptyStr((char*)tranStr.method) == true || isZeroAmount((char*)tranStr.amount) == false) {
		ret = drawAmount(controlId++, tranStr.amount, &loc, AMOUNT_ETH);
	}

	ret = drawFee(controlId++, &tranStr, &loc);

	ret = drawGasPrice(controlId++, tranStr.gasPrice, &loc);

	ret = drawGasLimit(controlId++, tranStr.gasLimit, &loc);
	
	if (isEmptyStr((char*)tranStr.data) == false) {
		showDivider(&loc, CONFIRM_DIVIDER_LINE_DISTANCE);

		drawDataLength(controlId++, &tranStr, loc);
		drawData(controlId++, &tranStr, &loc);
		loc.y1 += CONFIRM_TEXT_DATA_LINE_FIXED_DISTANCE;
	}
	if (isEmptyStr((char*)tranStr.total) == false) {
		showDivider(&loc, CONFIRM_DIVIDER_LINE_DISTANCE);
		drawTotal(controlId++, tranStr.total, &loc);
	}
	
		
	return TIMA_SUCCESS;
}

uint32_t drawScreenForBTC(TransactionStr tranStr) {
	Control control;
	Location loc;
	uint32_t controlId = 1;
	TextInfo textInfo;
	uint32_t fontHeight = 0;

	uint32_t resourceWidth = 0;
	uint32_t resourceHeight = 0;
	bool ret = 0;

	loc.x1 = CONFIRM_TEXT_SIDE_MARGIN;
	loc.y1 = CONFIRM_TEXT_ADDRESS_TOP_MARGIN;

	if (tranStr.toAddressCount > 1)
		return FAILED_TUI_STATE;

	ret = drawAddresses(controlId++, &tranStr, &loc);

	showDivider(&loc, CONFIRM_DIVIDER_LINE_DISTANCE);

	if (isEmptyStr((char*)tranStr.method) == true || isZeroAmount((char*)tranStr.amount) == false) {
		ret = drawAmount(controlId++, tranStr.amount, &loc, AMOUNT_NONE);
	}

	ret = drawFee(controlId++, &tranStr, &loc);

	if (isEmptyStr((char*)tranStr.total) == false) {
		showDivider(&loc, CONFIRM_DIVIDER_LINE_DISTANCE);
		drawTotal(controlId++, tranStr.total, &loc);
	}

	return TIMA_SUCCESS;
}

uint32_t drawConfirmScreen(uint8_t* str) {
	TransactionStr tranStr;
	uint32_t ret = TIMA_SUCCESS;

	ret = paserTransactionStr(&tranStr, str);
	if (ret != TIMA_SUCCESS) {
		TTY_LOG("%s failed parsing", LOG_TAG);
		return ERROR_BC_TUI;
	}

	switch (tranStr.coinType) {
	case ETH:
		ret = drawScreenForETH(tranStr);
		break;
	case BTC:
		ret = drawScreenForBTC(tranStr);
		break;
	case NONE_COIN:
		break;
	case UNKNOWN_COIN:
		break;
	default:
		TTY_LOG("%d does not support", tranStr.coinType);
		break;
	}

	return ret;
}