#include "TuiPinpadScreen.h"
#include "TZ_Vendor_tl.h"
#include "tci.h"
#include "tl_tui_bc_error_msg.h"
#include "Vendor_Interface.h"
#include "TuiPng.h"
#include "TuiScreenProperty.h"
#include "TuiPinpadState.h"
#include "spay_pin_random_util_tl.h"
#include "ta_log.h"
#include "TuiLayout.h"


uint8_t *resource[36];
uint32_t sizes[36];

#if defined(USE_BF)
#include "TZ_Vendor_tl.h"
#include "BF_TZ_debug.h"
#include "bf_spay_tui.h"
#endif

#define CHECK_INT_OVERFLOW(a, b) \
        ((uint32_t)a + (uint32_t)b < (uint32_t)a || (uint32_t)a + (uint32_t)b < (uint32_t)b)

extern preference_t g_preference;

loadable_img_t g_loadable_img[RES_END - RES_BACKGROUND] = {0};
uint8_t g_pin[PIN_SIZE];
uint32_t g_pin_length = 0;
uint8_t g_pin_verify[PIN_SIZE];
uint32_t g_pin_verify_len = 0;
uint8_t g_min_pin_len = US_PIN_SIZE;
uint8_t g_pin_old[PIN_SIZE];    // existing pin
uint32_t g_pin_old_len = 0;
coordinate_t pin_coordinate[PIN_SIZE];    // the (x,y) for each star cache buffer. (optimization, only x needs to be an array. All y is the same)
uint8_t g_current_button;
uint64_t backspace_press_start;
uint8_t g_close_tui_after_verify;
uint64_t tui_verify_timestamp = 0;
bool g_display_normal_pinbox = true;
bool g_cancel_button_enabled = false;

static uint8_t *gBgImage = NULL;
static uint32_t gBgImageSize = 0;

static sPinSoftKey pinSoftKey;
static uint32_t currentPinScreenId = 0;

bool g_navigation_gesture = false;

uint32_t setNavigationGesture(uint8_t value) {
    uint32_t ret = TUI_SUCCESS;
    if (value == 0)
        g_navigation_gesture = false;
    else
        g_navigation_gesture = true;

    ret = setReleasedButtonImage(PIN_SOFTKEY_BACK_VAL);
    if (ret) {
        STORE_TA_ERROR("%s setNavigationGesture error. ret=0x%x", LOG_TAG, ret);
    }
    return ret;
}

bool validatePinPngResource(uint8_t *image, uint32_t imageSize, uint32_t imageWidth,
                            uint32_t imageHeight) {
    DBG_LOG("validatePinPngResource()");
    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 setPinScreenView(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("setPinScreenView : %d, %d, %d, %d", id, eventImageSize, originImageSize, extraImageSize);
    DBG_LOG("x : %d, y : %d", x, y);
    DBG_LOG("width : %d, height : %d", width, height);

    currentPinScreenId = id;

    pinSoftKey.softKey.xLeft = x;
    pinSoftKey.softKey.xRight = x + width;
    pinSoftKey.softKey.yTop = y;
    pinSoftKey.softKey.yBottom = y + height;
    pinSoftKey.softKey.val = PIN_SOFTKEY_BACK_VAL;
    pinSoftKey.softKey.type = PAD_SOFTKEY_BACK;
    pinSoftKey.id = id;
    pinSoftKey.eventResourceSize = eventImageSize;
    pinSoftKey.originResourceSize = originImageSize;
    pinSoftKey.extraResourceSize = extraImageSize;

    return TUI_SUCCESS;
}

bool isExsitPinScreenView() {
    DBG_LOG("isExsitScreenView()");
    if (currentPinScreenId == 0) {
        TTY_LOG("gScreenView is NULL");
        return false;
    } else {
        return true;
    }
}

uint32_t savePinScreenViewResource(uint8_t *viewData, uint32_t dataLen) {
    DBG_LOG("savePinScreenViewResource()");

    if (pinSoftKey.eventResourceSize != 0 && pinSoftKey.eventResource == NULL) {
        DBG_LOG("save eventResource");
        if (pinSoftKey.eventResourceSize < dataLen) {
            TTY_LOG("gScreenView warn overflow. buffer : %d, dataLen : %d",
                    pinSoftKey.eventResourceSize, dataLen);
            pinSoftKey.eventResourceSize = 0;
            return ERROR_BC_TUI;
        } else if (validatePinPngResource(viewData, dataLen,
                                          pinSoftKey.softKey.xRight - pinSoftKey.softKey.xLeft,
                                          pinSoftKey.softKey.yBottom - pinSoftKey.softKey.yTop) ==
                   false) {
            TTY_LOG("ScreenView.eventResource is invalid");
            pinSoftKey.eventResourceSize = 0;
            return ERROR_BC_TUI;
        }
        pinSoftKey.eventResource = TZ_malloc(pinSoftKey.eventResourceSize);
        memcpy((void *) pinSoftKey.eventResource, viewData, dataLen);
    } else if (pinSoftKey.originResourceSize != 0 && pinSoftKey.originResource == NULL) {
        DBG_LOG("save originResource");
        if (pinSoftKey.originResourceSize < dataLen) {
            TTY_LOG("gScreenView(origin) warn overflow. buffer : 0x%x, dataLen : %d",
                    pinSoftKey.originResource, dataLen);
            pinSoftKey.originResourceSize = 0;
            return ERROR_BC_TUI;
        } else if (validatePinPngResource(viewData, dataLen,
                                          pinSoftKey.softKey.xRight - pinSoftKey.softKey.xLeft,
                                          pinSoftKey.softKey.yBottom - pinSoftKey.softKey.yTop) ==
                   false) {
            pinSoftKey.originResourceSize = 0;
            return ERROR_BC_TUI;
        }
        pinSoftKey.originResource = TZ_malloc(pinSoftKey.originResourceSize);
        memcpy((void *) pinSoftKey.originResource, viewData, dataLen);
    } else if (pinSoftKey.extraResourceSize != 0 && pinSoftKey.extraResource == NULL) {
        DBG_LOG("save extraResource");
        if (pinSoftKey.extraResourceSize < dataLen) {
            TTY_LOG("gScreenView(extra) warn overflow. buffer : %d, dataLen : %d",
                    pinSoftKey.extraResource, dataLen);
            pinSoftKey.extraResourceSize = 0;
            return ERROR_BC_TUI;
        } else if (validatePinPngResource(viewData, dataLen,
                                          pinSoftKey.softKey.xRight - pinSoftKey.softKey.xLeft,
                                          pinSoftKey.softKey.yBottom - pinSoftKey.softKey.yTop) ==
                   false) {
            TTY_LOG("ScreenView.extraResource is invalid");
            pinSoftKey.extraResourceSize = 0;
            return ERROR_BC_TUI;
        }
        pinSoftKey.extraResource = TZ_malloc(pinSoftKey.extraResourceSize);
        memcpy((void *) pinSoftKey.extraResource, viewData, dataLen);
    }

    DBG_LOG("savePinScreenViewResource()");
    DBG_LOG("getState() : %x", getState());
    DBG_LOG("control id : %d", pinSoftKey.id);
    DBG_LOG("eventResourceSize : %d", pinSoftKey.eventResourceSize);
    DBG_LOG("originResourceSize : %d", pinSoftKey.originResourceSize);
    DBG_LOG("extraResourceSize : %d", pinSoftKey.extraResourceSize);

    return TUI_SUCCESS;
}

void clearPinBackgroundResource() {
    if (gBgImage != NULL) {
        TZ_bzero(gBgImage, gBgImageSize);
        TZ_free(gBgImage);
        gBgImage = NULL;
    }
    gBgImageSize = 0;
}

uint32_t setPinBackground(uint32_t imageSize, uint32_t imageWidth, uint32_t imageHeight) {

    clearPinBackgroundResource();

    if (imageSize == 0) {
        TTY_LOG("BgImage is invalid!");
        return ERROR_BC_TUI;
    }

    gBgImage = TZ_malloc(imageSize);
    gBgImageSize = imageSize;

    return TUI_SUCCESS;
}

bool isExsitPinBackground() {
    DBG_LOG("isExsitPinBackground()");
    if (gBgImage == NULL) {
        TTY_LOG("gBgImage is NULL");
        return false;
    } else {
        return true;
    }
}

uint32_t savePinBackgroundResource(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;
}

/** getTouchedKey() - Translate x,y coordinates into key
 *
 * @param x                 x coordinate of touch event
 * @param y                 y coordinate of touch event
 * @param[out] touched_key  key structure of touched key
 *
 * Loops over the layout structure to find the pressed key.
 * If found, copies the key description into touched_key.
 * Otherwise, <NULL> key is returned.
 *
 * @return  true upon success or false upon not found
 */
uint32_t getTouchedKey(
        uint32_t x,
        uint32_t y,
        sPinPadKey_t *const touched_key
) {
    const sPinPadKey_t *key;
    uint32_t ret = 0;
    sLayout_t *layout = (sLayout_t *) &g_pinpad_layout;
    *touched_key = layout->buttons_array[0];
    int i, j;

/*#if defined(SUPPORT_SOFTKEY) \
	|| defined(SUPPORT_SOFTKEY_PUNCHCUT) \
	|| defined(SUPPORT_SOFTKEY_PUNCHCUT_800) \
	|| defined(SUPPORT_SOFTKEY_PUNCHHOLE) \
	|| defined(SUPPORT_DISPLAY_HIDE_NOTCH) \
	|| defined(SUPPORT_DUAL_LCD_MAIN) \
	|| defined(SUPPORT_DUAL_LCD_SUB)*/
    // lookup softkey first:

    {
        key = &pinSoftKey.softKey;
/*
	for (i = 0; i < SOFTKEY_SIZE; i++) {
		key = g_preference.isBHR ? &g_softkey_bhr[i] : &g_softkey[i];
*/

    if(display_type == _SUPPORT_DUAL_LCD_MAIN) {
        DBG_LOG("%s softkey: New Compare (%d,%d) and (xLeft=%d yBottom=%d xRight=%d yTop=%d) isBHR=%d",
                LOG_TAG, x, y, getScreenWidth() - key->yBottom, key->xLeft, getScreenWidth() - key->yTop, key->xRight, g_preference.isBHR);
        if (y >= key->xLeft && y <= key->xRight &&
            x <= getScreenWidth() - key->yTop && x >= getScreenWidth() - key->yBottom) {
            memcpy(touched_key, key, sizeof(sPinPadKey_t));
            return 0;
        }
    } else {
        DBG_LOG("%s softkey: Compare (%d,%d) and (xLeft=%d yTop=%d xRight=%d yBottom=%d) isBHR=%d",
                LOG_TAG, x, y, key->xLeft, key->yTop, key->xRight, key->yBottom, g_preference.isBHR);
        if (x >= key->xLeft && x <= key->xRight &&
            y <= key->yBottom && y >= key->yTop) {
            memcpy(touched_key, key, sizeof(sPinPadKey_t));
            return 0;
        }
    }

	i = 1;
	j = PINPAD_SIZE - 2;

	for (; i <= j; i++) {
		key = &layout->buttons_array[i];
		if (display_type == _SUPPORT_DUAL_LCD_MAIN) {
            DBG_LOG("%s New Compare (%d,%d) and (xLeft=%d yBottom=%d xRight=%d yTop=%d)",
                    LOG_TAG, x, y, getScreenWidth() - key->yBottom, key->xLeft, getScreenWidth() - key->yTop, key->xRight);
            if (y >= key->xLeft && y <= key->xRight &&
                x <= getScreenWidth() - key->yTop && x >= getScreenWidth() - key->yBottom) {
                memcpy(touched_key, key, sizeof(sPinPadKey_t));
                break;
                //return true;
            }
		} else {
            DBG_LOG("%s Compare (%d,%d) and (xLeft=%d yTop=%d xRight=%d yBottom=%d)",
                    LOG_TAG, x, y, key->xLeft, key->yTop, key->xRight, key->yBottom);
            if (x >= key->xLeft && x <= key->xRight &&
                y <= key->yBottom && y >= key->yTop) {
                memcpy(touched_key, key, sizeof(sPinPadKey_t));
                break;
                //return true;
            }
		}
	}
	}
	return ret;
}


static int checkImageBounds(
        uint32_t type,
        uint32_t x,
        uint32_t y,
        uint32_t width,
        uint32_t height
) {
    int ret = 0;
    uint32_t screenWidth = getScreenWidth();
    uint32_t screenHeight = getScreenHeight();

    DBG_LOG("%s gets started!", __func__);

    if (x >= screenWidth || width > screenWidth ||
        CHECK_INT_OVERFLOW(x, width) || x + width > screenWidth ||
        y >= screenHeight || height > screenHeight ||
        CHECK_INT_OVERFLOW(y, height) ||
        y + height > screenHeight) {
        STORE_TA_ERROR(
                "%s invalid image size and/or coordinates! type[%d]x[%d]w[%d]y[%d]h[%d]gw[%d]gh[%d]",
                LOG_TAG, type, x, width, y, height, screenWidth, screenHeight);
        return TIMA_ERROR_TUI_IMG_BAD_FORMAT;
    }

	DBG_LOG("%s Type ACTION_BAR_TEXT[1], SECURE_MODE_TEXT[2], PROMPT_ABOVE_PINBOX[3], PROMPT_BELOW_PINBOX[4]", LOG_TAG);
	DBG_LOG("%s image size and/or coordinates! type[%d] x[%d] w[%d] y[%d] h[%d] gw[%d] gh[%d]",
		LOG_TAG, type, x, width, y, height, screenWidth, screenHeight);

    uint32_t y_bottom;
if (display_type == _SUPPORT_DUAL_LCD_MAIN) {
    y_bottom = screenWidth - x;
} else {
    y_bottom = y + height;
}

	uint32_t len = 0;
	switch (type) {
	case RES_ACTION_BAR_TEXT:
		if (y_bottom > PINPAD_SECURE_MODE_TEXT_TOP * SCREEN_PIX_PER_DP) {
			ret = TIMA_ERROR_TUI_IMG_BAD_FORMAT;
		}
		break;
	case RES_SECURE_MODE_TEXT:
		len = (PINPAD_SECURE_ICON_WIDTH + PINPAD_SECURE_ICON_TEXT_DISTANCE) * SCREEN_PIX_PER_DP;
		if (CHECK_INT_OVERFLOW(len, width)
			|| len + width > screenWidth
			|| y_bottom > PINPAD_LINE1_TOP_HD) {
			ret = TIMA_ERROR_TUI_IMG_BAD_FORMAT;
		}
		break;
	case RES_PROMPT_ABOVE_PINBOX:
	case RES_PROMPT_BELOW_PINBOX:
		if (y < PINPAD_ACTION_BAR_TOP + PINPAD_ACTION_BAR_HEIGHT
			|| y_bottom > PINPAD_LINE1_TOP_HD) {
			ret = TIMA_ERROR_TUI_IMG_BAD_FORMAT;
		}
		break;
	case RES_NORMAL_PINBOX:
	case RES_ERROR_PINBOX:
	case RES_DISABLED_PINBOX:
		if (y < PINPAD_ACTION_BAR_TOP + PINPAD_ACTION_BAR_HEIGHT
			|| y_bottom > PINPAD_LINE1_TOP_HD) {
			ret = TIMA_ERROR_TUI_IMG_BAD_FORMAT;
		}
		break;
	default:
		return TIMA_ERROR_TUI_IMG_BAD_FORMAT;

	}
	if (ret) {
	    DBG_LOG("%d\t%d", PINPAD_ACTION_BAR_TOP, PINPAD_ACTION_BAR_HEIGHT);
	    DBG_LOG("%d\t %d\t %d\t %d\t %d", y_bottom, PINPAD_LINE1_TOP_HD, screenWidth, len, width);
		STORE_TA_ERROR("%s: failed type:%d", __func__, type);
	}
	return ret;
}

/*
 * Load images from normal world
 */
uint32_t loadPinpadImage(
        uint32_t type,
        uint8_t *buf,
        uint32_t len,
        uint32_t x_dp,
        uint32_t y_dp
) {
    uint32_t width, height;
    uint8_t *p;
    uint32_t old_prompt_width = 0, old_prompt_height = 0;
    uint32_t screenWidth = getScreenWidth();
    uint32_t screenHeight = getScreenHeight();
    uint32_t softkeyHeight = PINPAD_SOFTKEY_HEIGHT;
    uint32_t bottomBarHeight = PINPAD_BOTTOM_BAR_HEIGHT;
    STORE_TA_ERROR("g_loadable_img[type].y %d", g_loadable_img[type].y);
    if (type >= RES_END) {
        STORE_TA_ERROR("%s invalid loadable resource type: %d", LOG_TAG, type);
        return TIMA_ERROR_INVALID_ARGUMENT;
    }
    // free old resource
    if (g_loadable_img[type].len > 0) {
        DBG_LOG("%s free old resource", LOG_TAG);
        if (type == RES_PROMPT_ABOVE_PINBOX
            || type == RES_PROMPT_BELOW_PINBOX) {
            old_prompt_width = g_loadable_img[type].width;
            old_prompt_height = g_loadable_img[type].height;
        }

        TZ_free(g_loadable_img[type].pointer);
        memset(&g_loadable_img[type], 0, sizeof(loadable_img_t));
    }
    // allocate for new resource
    DBG_LOG("%s allocate buffer %d", LOG_TAG, len);
    p = (uint8_t *) TZ_malloc(len);
    if (p == NULL) {
        STORE_TA_ERROR("%s Cannot allocate buffer (%d) for loadable images!",
                       LOG_TAG, len);
        return TIMA_ERROR_OUT_OF_MEMORY;
    }

    DBG_LOG("%s allocated pointer %p", LOG_TAG, p);

    memcpy(p, buf, len);

    if (validatePng(p, len, &width, &height)) {
        STORE_TA_ERROR("%s resource (%d) is not a valid png image!", LOG_TAG,
                       type);
        TZ_free(p);
        return TIMA_ERROR_TUI_IMG_BAD_FORMAT;
    }

    if (screenWidth < width || screenHeight < height) {
        STORE_TA_ERROR("%s Prompt width/height is greater than screen width/height!",
                       LOG_TAG);
        TZ_free(p);
        return TIMA_ERROR_TUI_IMG_BAD_FORMAT;
    }

    if (type == RES_PROMPT_ABOVE_PINBOX || type == RES_PROMPT_BELOW_PINBOX) {
        if (g_pinbox_y == 0) {
            STORE_TA_ERROR("%s Must set PIN box first!", LOG_TAG);
            TZ_free(p);
            return SPAY_TPP_ERROR_INVALID_NUMBER_OF_PIN;
        }

        DBG_LOG("%s screen width!, %d, Prompt width  %d", LOG_TAG, screenWidth, width);

        DBG_LOG("%s screen width!, %d, Prompt width  %d", LOG_TAG, screenWidth, width);

        if (display_type == _SUPPORT_DUAL_LCD_MAIN) {
            // x_dp = 0, y_dp = dis_to_pinbox_dp
            g_loadable_img[type].y = (screenHeight - height) >> 1;

            if (type == RES_PROMPT_ABOVE_PINBOX) {
                g_loadable_img[type].x = screenWidth -
                                         (g_pinbox_y - (y_dp * (screenHeight - softkeyHeight) /
                                                        (STANDARD_SCREEN_HEIGHT_DP -
                                                         STANDARD_SOFTKEY_HEIGHT_DP)) - width) -
                                         width;
            } else {
                g_loadable_img[type].x = screenWidth -
                                         (g_pinbox_y + (y_dp * (screenHeight - softkeyHeight) /
                                                        (STANDARD_SCREEN_HEIGHT_DP -
                                                         STANDARD_SOFTKEY_HEIGHT_DP)) +
                                          g_pinbox_height) - width;
            }
        } else {
            // x_dp = 0, y_dp = dis_to_pinbox_dp
            g_loadable_img[type].x = (screenWidth - width) >> 1;

            if (type == RES_PROMPT_ABOVE_PINBOX) {
                g_loadable_img[type].y = g_pinbox_y -
                                         y_dp * (screenHeight - softkeyHeight) /
                                         (STANDARD_SCREEN_HEIGHT_DP - STANDARD_SOFTKEY_HEIGHT_DP) -
                                         height;

                DBG_LOG("RES_PROMPT_ABOVE_PINBOX %d %d %d %d %d %d %d", g_pinbox_y, y_dp, screenHeight, softkeyHeight, STANDARD_SCREEN_HEIGHT_DP
                , STANDARD_SOFTKEY_HEIGHT_DP, height);
            } else {
                g_loadable_img[type].y = g_pinbox_y + g_pinbox_height +
                                         y_dp * (screenHeight - softkeyHeight) /
                                         (STANDARD_SCREEN_HEIGHT_DP - STANDARD_SOFTKEY_HEIGHT_DP);
            }
        }
        DBG_LOG("%s prompt: width=%d, height=%d, old_w=%d, old_h=%d", LOG_TAG, width, height,
                old_prompt_width, old_prompt_height);

        if (width >= old_prompt_width && height >= old_prompt_height) {
            g_loadable_img[type].new_prompt_covers_old_prompt = true;
        }
    } else if (type == RES_ACTION_BAR_TEXT) {
        if (display_type == _SUPPORT_DUAL_LCD_MAIN) {
            uint32_t x = 0;
            uint32_t y = y_dp * screenHeight / STANDARD_SCREEN_HEIGHT_DP;
            if (g_preference.isRTL) {
                if (screenHeight - height < y) {
                    return TIMA_ERROR_TUI_IMG_BAD_FORMAT;
                }
                g_loadable_img[type].y = screenHeight - (x_dp * 3) -
                                         height; //screenWidth - (x_dp * 3) - width; // Check DP : Tedd
            } else {
                g_loadable_img[type].y = x_dp * 3; // Check DP : Tedd
            }
            g_loadable_img[type].x =
                    screenWidth - PINPAD_ACTION_BAR_TOP - PINPAD_ACTION_BAR_HEIGHT +
                    ((PINPAD_ACTION_BAR_HEIGHT - width) / 2) - 4;
        } else {
		uint32_t x = x_dp * screenWidth / STANDARD_SCREEN_WIDTH_DP;
		if (g_preference.isRTL) {
			if (screenWidth - width < x) {
				return TIMA_ERROR_TUI_IMG_BAD_FORMAT;
			}
            g_loadable_img[type].x = screenWidth - x - width;
		}
		else {
			g_loadable_img[type].x = x;
		}
		g_loadable_img[type].y = PINPAD_ACTION_BAR_TOP + (PINPAD_ACTION_BAR_HEIGHT - height) / 2;
        }
    } else if (display_type == _SUPPORT_DUAL_LCD_MAIN && type == RES_SECURE_MODE_TEXT) {
        g_loadable_img[type].x = screenWidth - (y_dp * (screenHeight - softkeyHeight) /
                                                (STANDARD_SCREEN_HEIGHT_DP -
                                                 STANDARD_SOFTKEY_HEIGHT_DP));
        g_loadable_img[type].y = x_dp * screenWidth / STANDARD_SCREEN_WIDTH_DP;
    } else {
        g_loadable_img[type].x =
                x_dp * screenWidth / STANDARD_SCREEN_WIDTH_DP;
        if (display_type == _SUPPORT_DUAL_LCD_MAIN) {
            g_loadable_img[type].x =
                    screenWidth - (y_dp * screenHeight / STANDARD_SCREEN_HEIGHT_DP);
            g_loadable_img[type].y =
                    x_dp * (screenWidth - softkeyHeight) / (STANDARD_SCREEN_WIDTH_DP - STANDARD_SOFTKEY_HEIGHT_DP);
        } else {
            g_loadable_img[type].x =
                    x_dp * screenWidth / STANDARD_SCREEN_WIDTH_DP;
            g_loadable_img[type].y =
                    y_dp * (screenHeight - softkeyHeight) /
                    (STANDARD_SCREEN_HEIGHT_DP - STANDARD_SOFTKEY_HEIGHT_DP);
        }
    }

	// Out of buffer check
	int chk_ret = checkImageBounds(type, g_loadable_img[type].x,
		g_loadable_img[type].y, width, height);
	if (chk_ret != 0) {
		TZ_free(p);
		return chk_ret;
	}

	g_loadable_img[type].pointer = p;
	g_loadable_img[type].len = len;
	g_loadable_img[type].width = width;
	g_loadable_img[type].height = height;
	g_loadable_img[type].to_display = true;

	DBG_LOG("%s loadable image: len=%d, w=%d, h=%d, x=%d, y=%d", LOG_TAG,
		len, width, height, g_loadable_img[type].x,
		g_loadable_img[type].y);

	return TIMA_SUCCESS;
}

uint32_t drawPinpadImage(
        uint32_t id,
        uint32_t x,
        uint32_t y
) {
    uint8_t *png_buffer;
    uint32_t png_buffer_len;

    if (id < (sizeof(resource) / sizeof(resource[0]))) {
        png_buffer = resource[id];
        png_buffer_len = sizes[id];
    } else if (IDX_PIN_SOFTKEY_BACK == id) {
        png_buffer = pinSoftKey.originResource;
        png_buffer_len = pinSoftKey.originResourceSize;
        if (g_navigation_gesture) {
            png_buffer = pinSoftKey.extraResource;
            png_buffer_len = pinSoftKey.extraResourceSize;
        }
    } else if (IDX_PIN_SOFTKEY_BACK_PRESSED == id) {
        png_buffer = pinSoftKey.eventResource;
        png_buffer_len = pinSoftKey.eventResourceSize;
    } else if (id == IDX_PINPAD_PINPAD_BHR + g_preference.isRTL ||
               id == IDX_PINPAD_PINPAD + g_preference.isRTL) {
        png_buffer = gBgImage;
        png_buffer_len = gBgImageSize;
    } else {
        STORE_TA_ERROR("%s no image resource for %d", LOG_TAG, id);
        return TIMA_ERROR_TUI_NO_RESOURCE;
    }

	DBG_LOG("show image id = %d", id);

    if (id == IDX_CANCEL && g_loadable_img[RES_CANCEL_BUTTON].len > 0) {
        png_buffer = g_loadable_img[RES_CANCEL_BUTTON].pointer;
        png_buffer_len = g_loadable_img[RES_CANCEL_BUTTON].len;
    } else if (id == IDX_CANCEL_PRESSED
               && g_loadable_img[RES_CANCEL_BUTTON_PRESSED].len > 0) {
        png_buffer = g_loadable_img[RES_CANCEL_BUTTON_PRESSED].pointer;
        png_buffer_len = g_loadable_img[RES_CANCEL_BUTTON_PRESSED].len;
    }

    uint32_t ret;
    if ((IDX_PIN_SOFTKEY_BACK <= id && id <= IDX_PIN_SOFTKEY_BACK_PRESSED)
        || (id == IDX_PINPAD_PINPAD_BHR + g_preference.isRTL ||
            id == IDX_PINPAD_PINPAD + g_preference.isRTL)) {
        setTuiPinMode(true);
        ret = drawImage_vendor(x, y, png_buffer, png_buffer_len, (uint32_t) getRotationType());
    } else {
        ret = drawImage(x, y, png_buffer, png_buffer_len);
    }
    if (id == IDX_PINPAD_PINPAD_BHR + g_preference.isRTL ||
        id == IDX_PINPAD_PINPAD + g_preference.isRTL) {
        clearPinBackgroundResource();
    }
    setTuiPinMode(false);
    return ret;
}

/** add_pin() - Display PIN dot at index
 *
 * @param keynum    Digit that was entered
 *
 * @return  TLAPI_OK upon success or specific error
 */
uint32_t addPin(uint8_t keynum) {
    uint32_t x, y;
    uint32_t ret;
    uint32_t screenWidth = getScreenWidth();
    uint32_t screenHeight = getScreenHeight();

    if (display_type == _SUPPORT_DUAL_LCD_MAIN) {
        uint32_t len = (screenHeight - g_min_pin_len * g_pinbox_width - (g_min_pin_len - 1) * g_pinbox_space) >> 1;

        y = len;	// the first PIN box
        y = y + g_pin_length * (g_pinbox_width + g_pinbox_space) + ((g_pinbox_width - PINPAD_STAR_SIZE) >> 1);	// where the pin is
        x = screenWidth - (g_pinbox_y + ((g_pinbox_height - PINPAD_STAR_SIZE) >> 1)) - PINPAD_PIN_BOX_IMG_HEIGHT + g_pinbox_height - PINPAD_STAR_SIZE;
    } else {
        uint32_t len = (screenWidth - g_min_pin_len * g_pinbox_width - (g_min_pin_len - 1) * g_pinbox_space) >> 1;

        x = len;	// the first PIN box
        x = x + g_pin_length * (g_pinbox_width + g_pinbox_space) + ((g_pinbox_width - PINPAD_STAR_SIZE) >> 1);	// where the pin is
        y = g_pinbox_y + ((g_pinbox_height - PINPAD_STAR_SIZE) >> 1);
    }

	pin_coordinate[g_pin_length].x = x;
	pin_coordinate[g_pin_length].y = y;

	++g_pin_length;

	if (!g_display_normal_pinbox) {
		// display error pin dot if the png is available
		ret = drawPinpadImage(IDX_PINPAD_STAR_ERROR, x, y);
		if (ret) {
			return drawPinpadImage(IDX_PINPAD_STAR, x, y);
		} else {
			return 0;
		}
	} else {
		return drawPinpadImage(IDX_PINPAD_STAR, x, y);
	}
}

/** remove_pin() - Display blank image at index
 *
 * @return  TIMA_SUCCESS upon success or specific error
 */
uint32_t removePin() {
    --g_pin_length;
    return drawPinpadImage(IDX_PINPAD_BLANK, pin_coordinate[g_pin_length].x,
                           pin_coordinate[g_pin_length].y);
}

/**
 */
uint32_t removeAllPin() {
    uint32_t ret = 0;
    while (g_pin_length > 0) {
        ret = removePin();
        if (ret)
            break;
    }
    return ret;
}

/** setButtonImage() - set Image of one pinpad button
 *
 * @param touched_key    Button that was touched
 * @param pressed        Pressed or released
 *
 * Prepares the tlApiTuiSetImage() call to show the negative picture of the button.
 *
 * @return  TLAPI_OK upon success or specific error
 */
uint32_t setButtonImage(sPinPadKey_t key, bool pressed) {
    uint8_t provIdx;

    if (key.val == PIN_SOFTKEY_BACK_VAL) {
        provIdx = pressed ? IDX_PIN_SOFTKEY_BACK_PRESSED : IDX_PIN_SOFTKEY_BACK;
    } else {
        /* Calculate the provisionning index with the button value */
        provIdx = key.val + IDX_PINPAD_0;
        if (pressed) {
            provIdx += IDX_DELTA_PRESSED;
        }
    }

    if (display_type == _SUPPORT_DUAL_LCD_MAIN) {
        return drawPinpadImage(provIdx, getScreenWidth() - key.yBottom, key.xLeft);
    } else {
        return drawPinpadImage(provIdx, key.xLeft, key.yTop);
    }
}

/** setReleasedButtonImage() - set Image of one pinpad button
 *
 * @param value    Value of the button to display
 *
 * Prepares the tlApiTuiSetImage() call to show the picture of the released button.
 *
 * @return  TLAPI_OK upon success or specific error
 */
uint32_t setReleasedButtonImage(uint8_t value) {
    const sPinPadKey_t *key;
    uint32_t ret = TIMA_SUCCESS;
    sLayout_t *layout = (sLayout_t *) &g_pinpad_layout;
/*#if defined(SUPPORT_SOFTKEY) \
	|| defined(SUPPORT_SOFTKEY_PUNCHCUT) \
	|| defined(SUPPORT_SOFTKEY_PUNCHCUT_800) \
	|| defined(SUPPORT_SOFTKEY_PUNCHHOLE) \
	|| defined(SUPPORT_DISPLAY_HIDE_NOTCH) \
	|| defined(SUPPORT_DUAL_LCD_MAIN) \
	|| defined(SUPPORT_DUAL_LCD_SUB)*/
    // lookup softkey first:

    {
        key = &pinSoftKey.softKey;
/*
	for (int i = 0; i < SOFTKEY_SIZE; i++) {
		key = g_preference.isBHR ? &g_softkey_bhr[i] : &g_softkey[i];
*/
        TTY_LOG("key->val:%d value=%d", key->val, value);
        if (key->val == value) {
            ret = setButtonImage(*key, false);
            return ret;
        }
    }
//#endif
    for (int i = 1; i <= PINPAD_SIZE; i++) {
        key = &layout->buttons_array[i];
        if (key->val == value) {
            TTY_LOG("button val:%d value=%d", key->val, value);
            ret = setButtonImage(*key, false);
            break;
        }
    }
    return ret;
}

uint32_t processKeyEvent(uint32_t x, uint32_t y, bool pressed) {
    uint32_t ret = TIMA_SUCCESS;
    sPinPadKey_t touched_key;
    uint32_t ret_st = getPinpadState();
    DBG_LOG("%s processKeyEvent start: getPinpadState() = 0x%x", LOG_TAG, getPinpadState());
    getTouchedKey(x, y, &touched_key);

    DBG_LOG("%s touched_key.type = %d, touched_key.val = %d, pressed = %d",
            LOG_TAG, touched_key.type, touched_key.val, pressed);

#ifndef PIN_ENTER_BUTTON_ENABLED
    if (touched_key.type == PAD_VALIDATE) {
        // ignore the enter button. no state change
        return ret_st;
    }
#endif

    if (touched_key.type == PAD_CANCEL) {
        return SPAY_TUI_ST_CANCELLED;
    }

    if (touched_key.type == PAD_VALIDATE && g_pin_length < g_min_pin_len) {
        return ret_st;
    }

    if (pressed) {
        /* Set the pressed button image. But do not set it continuously during long press */
        if (touched_key.val != g_current_button
            && g_current_button != PAD_KEY_OUT_VAL) {
            if (g_current_button != PAD_NOKEY_VAL) {
                /* Reset the picture of the previous touched button */
                DBG_LOG("TUI: release %d", g_current_button);
                ret = setReleasedButtonImage(g_current_button);
                g_current_button = PAD_KEY_OUT_VAL;
            } else if (touched_key.type != NOKEY) {
                DBG_LOG("TUI: set %d", touched_key.val);
                ret = setButtonImage(touched_key, pressed);
                g_current_button = touched_key.val;
                if (touched_key.val == PAD_CORRECT_VAL) {
                    backspace_press_start = getUptime();
                }
            }
            showFrameBuffer();
        } else if (g_current_button == PAD_CORRECT_VAL &&
                   backspace_press_start > 0) {
            if (getUptime() - backspace_press_start >=
                BACKSPACE_LONG_PRESS) {
                DBG_LOG
                ("TUI: backspace is pressed over 2 seconds");
                backspace_press_start = 0;
                removeAllPin();
                showFrameBuffer();
            }
        }

        /* Return: don't process data on pressing event */
        return (ret == 0 ? getPinpadState() : SPAY_TUI_ST_CANCELLED);
    } else {
        /* Reset the picture of the previous touched button */
        if (g_current_button < PAD_KEY_OUT_VAL) {
            ret = setReleasedButtonImage(g_current_button);
            DBG_LOG("TUI: l=[%d] release=[%d]", __LINE__, g_current_button);
            if (ret != TIMA_SUCCESS) {
                return SPAY_TUI_ST_CANCELLED;
            }
        } else {
#define USE_MOBICORE_XX
#ifdef USE_MOBICORE_XX
            //STORE_TA_ERROR("%s temporary solution for tbase, set touched button to released button", LOG_TAG);
            if (g_current_button < PAD_KEY_OUT_VAL) {
                // skip the case when dragged out of the pressed key and then released
                g_current_button = touched_key.val;
                setButtonImage(touched_key, true);
                setReleasedButtonImage(g_current_button);
            }
#else
            //STORE_TA_ERROR("%s Release but no button to reset. Do nothing.", LOG_TAG);
            g_current_button = PAD_NOKEY_VAL;
            return getPinpadState();
#endif

        }

        // if the released button is not the pressed button, do nothing
        if (touched_key.val != g_current_button) {
            g_current_button = PAD_NOKEY_VAL;
            DBG_LOG("TUI: showFrameBuffer() call forcely");
            showFrameBuffer();  //PLM P170327-03217 issue fixed.
            return getPinpadState();
        }
        g_current_button = PAD_NOKEY_VAL;
    }

    switch (touched_key.type) {
        case PAD_NUM:
            if (g_pin_length < g_min_pin_len) {
                g_pin[g_pin_length] = touched_key.val;
                ret = addPin(touched_key.val);
                if (ret != TIMA_SUCCESS) {
                    ret_st = SPAY_TUI_ST_CANCELLED;
                    break;
                }
#ifndef PIN_ENTER_BUTTON_ENABLED
                if (g_pin_length == g_min_pin_len) {
                    ret_st = ((getPinpadState() & _SPAY_TUI_ST_REMOVE_START) |
                              _SPAY_TUI_ST_ENTER);    // switch from start to enter
                }
#endif
            } else {
                ////STORE_TA_ERROR("%s Ignoring entered key.", LOG_TAG);
            }
            break;
        case PAD_CORRECT:
            if (g_pin_length > 0) {
                ret = removePin();
                if (ret != TIMA_SUCCESS) {
                    ret_st = SPAY_TUI_ST_CANCELLED;
                }
            }
            break;
        case PAD_CANCEL:
            // Ignore cancel button
            //STORE_TA_ERROR("%s CANCEL", LOG_TAG);
            //ret_st = SPAY_TUI_ST_CANCELLED;
            break;

        case PAD_VALIDATE:
            //STORE_TA_ERROR("%s PIN entered", LOG_TAG);
            ret_st = ((getPinpadState() & _SPAY_TUI_ST_REMOVE_START) | _SPAY_TUI_ST_ENTER);
            break;
        case PAD_SOFTKEY_BACK:
            setPinpadState(SPAY_TUI_ST_CANCELLED);
            ret_st = getPinpadState();
            DBG_LOG("%s PAD_SOFTKEY_BACK", LOG_TAG);
        default:
            break;
    }            /* switch(key) */
    showFrameBuffer();

    return ret_st;
}

// Display all PIN buttons
uint32_t displayPinButtons() {
    uint32_t ret;
    uint32_t screenWidth = getScreenWidth();
    uint32_t width, height;

 
    sPinPadKey_t *key;
    sLayout_t *layout = (sLayout_t *)& g_pinpad_layout;

    setReleasedButtonImage(PIN_SOFTKEY_BACK_VAL);
    validatePng(resource[IDX_REGISTER_PIN_TOP], sizes[IDX_REGISTER_PIN_TOP], &width, &height);

    if (display_type == _SUPPORT_DUAL_LCD_MAIN) {
        ret = drawPinpadImage(IDX_INAPP_PIN_KEYPAD, PINPAD_SOFTKEY_DP * SCREEN_PIX_PER_DP, 0);
        if (ret) {
            STORE_TA_ERROR("%s cannot display IDX_INAPP_PIN_KEYPAD!. ret=0x%x", LOG_TAG, ret);
            return ret;
        }
        ret = drawPinpadImage(IDX_REGISTER_PIN_TOP, screenWidth - PINPAD_ACTION_BAR_TOP - PINPAD_ACTION_BAR_HEIGHT + ((PINPAD_ACTION_BAR_HEIGHT - width) / 2) - 4, 0);
    } else {
        ret = drawPinpadImage(IDX_INAPP_PIN_KEYPAD, 0, (STANDARD_SCREEN_HEIGHT_DP - PINPAD_SOFTKEY_DP - PINPAD_HEIGHT_DP) * SCREEN_PIX_PER_DP);
        if (ret) {
            STORE_TA_ERROR("%s cannot display IDX_INAPP_PIN_KEYPAD!. ret=0x%x", LOG_TAG, ret);
            return ret;
        }
        ret = drawPinpadImage(IDX_REGISTER_PIN_TOP, 0, PINPAD_ACTION_BAR_TOP + (PINPAD_ACTION_BAR_HEIGHT - height) / 2);
    }

    for (int i = 1; i < PINPAD_SIZE - 2; i++) {
        key = &layout->buttons_array[i];
#if defined(SUPPORT_DUAL_LCD_MAIN)
        ret = drawPinpadImage(key->val + IDX_PINPAD_0, getScreenWidth() - key->yTop - PINPAD_KEY_HEIGHT, key->xLeft);
#else
        ret = drawPinpadImage(key->val + IDX_PINPAD_0, key->xLeft, key->yTop);
#endif
        if (ret) {
            break;
        }
    }

    return ret;
}

// display PIN boxes
uint32_t displayPinBox(enum pin_box_type type) {
	uint32_t screenWidth = getScreenWidth();
	uint32_t screenHeight = getScreenHeight();

	if (display_type == _SUPPORT_DUAL_LCD_MAIN) {
        int i;
        uint32_t ret = 0;
        int y = 0;
        uint32_t len = 0;

        loadable_img_t *img = &(g_loadable_img[RES_NORMAL_PINBOX + type]);

        if (img->len == 0 || img->width == 0 || img->height == 0) {

            len = (screenHeight - g_min_pin_len * g_pinbox_width - (g_min_pin_len - 1) * g_pinbox_space) >> 1;
            if (len > (screenWidth >> 1)) {
                STORE_TA_ERROR("%s: invalid value! g_min_pin_len=%d g_pinbox_width=%d g_pinbox_space=%d",
                               __func__, g_min_pin_len, g_pinbox_width, g_pinbox_space);
                return TIMA_ERROR_TUI_DISPLAY_ERROR;
            }

            DBG_LOG("%s: g_min_pin_len=%d g_pinbox_y=%d, g_pinbox_width=%d g_pinbox_height=%d g_pinbox_space=%d",
                    __func__, g_min_pin_len, g_pinbox_y, g_pinbox_width, g_pinbox_height, g_pinbox_space);

            if (CHECK_INT_OVERFLOW(g_pinbox_y, g_pinbox_height)
                || g_pinbox_y + g_pinbox_height < PINPAD_PIN_BOX_IMG_HEIGHT
                || g_pinbox_y < PINPAD_ACTION_BAR_TOP + PINPAD_ACTION_BAR_HEIGHT
                || g_pinbox_y + g_pinbox_height > PINPAD_SECURE_MODE_TEXT_TOP * SCREEN_PIX_PER_DP) {
                STORE_TA_ERROR("%s: invalid value! g_pinbox_y=%d g_pinbox_height=%d",
                               __func__, g_pinbox_y, g_pinbox_height);
                return TIMA_ERROR_TUI_DISPLAY_ERROR;
            }
            DBG_LOG("%s display_pin_box len %d!", LOG_TAG, y);

            // display internal pin box
            for (i = 0; i < g_min_pin_len; i++) {
                y = len;	// the first PIN box
                drawPinpadImage(IDX_NORMAL_PINBOX + type,
                                screenWidth - (g_pinbox_y + g_pinbox_height - PINPAD_PIN_BOX_IMG_HEIGHT) - g_pinbox_height,
                                y + i * (g_pinbox_width + g_pinbox_space));
                /*
                            drawPinpadImage(IDX_NORMAL_PINBOX + type,
                                x + i * (g_pinbox_width + g_pinbox_space),
                                g_pinbox_y + g_pinbox_height - PINPAD_PIN_BOX_IMG_HEIGHT);
                */
                if (ret) {
                    STORE_TA_ERROR("%s unable to display internal PIN box!", LOG_TAG);
                    break;
                } else {
                    DBG_LOG("%s displayed internal PIN box at (%d, %d)!", LOG_TAG,
                            y + i * (g_pinbox_width + g_pinbox_space), g_pinbox_y + g_pinbox_height - PINPAD_PIN_BOX_IMG_HEIGHT);

                    DBG_LOG("screenHeight=%d, g_min_pin_len=%d, g_pinbox_width=%d, g_pinbox_space=%d, g_pinbox_width=%d. g_pinbox_y=%d, g_pinbox_height=%d",
                            screenHeight, g_min_pin_len,
                            g_pinbox_width, g_pinbox_space,
                            g_pinbox_width, g_pinbox_y,
                            g_pinbox_height);

                }

            }
        } else {

            DBG_LOG("%s display loadable pinbox: %p, %d", LOG_TAG, img->pointer, img->len);
            y = (screenWidth - g_min_pin_len * img->width - (g_min_pin_len - 1) * g_pinbox_space) >> 1;
            for (i = 0; i < g_min_pin_len; i++) {
                ret = drawImage(screenWidth - (img->y) - g_pinbox_height, y + i * (img->width + g_pinbox_space), img->pointer, img->len);
                if (ret) {
                    STORE_TA_ERROR("%s unable to display loadable PIN box!", LOG_TAG);
                    break;
                } else {
                    DBG_LOG("%s displayed PIN box!", LOG_TAG);
                }
            }
        }
        return ret;
	} else {
        int i;
        uint32_t ret = 0;
        int x = 0;
        uint32_t len = 0;

        loadable_img_t *img = &(g_loadable_img[RES_NORMAL_PINBOX + type]);

        if (img->len == 0 || img->width == 0 || img->height == 0) {

            len = (screenWidth - g_min_pin_len * g_pinbox_width - (g_min_pin_len - 1) * g_pinbox_space) >> 1;
            if (len > (screenWidth >> 1)) {
                STORE_TA_ERROR("%s: invalid value! g_min_pin_len=%d g_pinbox_width=%d g_pinbox_space=%d",
                               __func__, g_min_pin_len, g_pinbox_width, g_pinbox_space);
                return TIMA_ERROR_TUI_DISPLAY_ERROR;
            }
            if (CHECK_INT_OVERFLOW(g_pinbox_y, g_pinbox_height)
                || g_pinbox_y + g_pinbox_height < PINPAD_PIN_BOX_IMG_HEIGHT
                || g_pinbox_y < PINPAD_ACTION_BAR_TOP + PINPAD_ACTION_BAR_HEIGHT
                || g_pinbox_y + g_pinbox_height > PINPAD_SECURE_MODE_TEXT_TOP * SCREEN_PIX_PER_DP) {
                STORE_TA_ERROR("%s: invalid value! g_pinbox_y=%d g_pinbox_height=%d",
                               __func__, g_pinbox_y, g_pinbox_height);
                return TIMA_ERROR_TUI_DISPLAY_ERROR;
            }
            DBG_LOG("%s display_pin_box len %d!", LOG_TAG, x);

            // display internal pin box
            for (i = 0; i < g_min_pin_len; i++) {
                x = len;	// the first PIN box
                drawPinpadImage(IDX_NORMAL_PINBOX + type,
                                x + i * (g_pinbox_width + g_pinbox_space),
                                g_pinbox_y + g_pinbox_height - PINPAD_PIN_BOX_IMG_HEIGHT);

                if (ret) {
                    STORE_TA_ERROR("%s unable to display internal PIN box!", LOG_TAG);
                    break;
                } else {
                    DBG_LOG("%s displayed internal PIN box at (%d, %d)!", LOG_TAG,
                            x + i * (g_pinbox_width + g_pinbox_space), g_pinbox_y + g_pinbox_height - PINPAD_PIN_BOX_IMG_HEIGHT);

                    DBG_LOG
                    ("screenWidth=%d, g_min_pin_len=%d, g_pinbox_width=%d, g_pinbox_space=%d, g_pinbox_width=%d. g_pinbox_y=%d, g_pinbox_height=%d",
                     screenWidth, g_min_pin_len,
                     g_pinbox_width, g_pinbox_space,
                     g_pinbox_width, g_pinbox_y,
                     g_pinbox_height);

                }

            }
        } else {

            DBG_LOG("%s display loadable pinbox: %p, %d", LOG_TAG,
                    img->pointer, img->len);
            x = (screenWidth - g_min_pin_len * img->width -
                 (g_min_pin_len - 1) * g_pinbox_space) >> 1;
            for (i = 0; i < g_min_pin_len; i++) {
                ret = drawImage(x + i * (img->width + g_pinbox_space), img->y, img->pointer, img->len);
                if (ret) {
                    STORE_TA_ERROR
                            ("%s unable to display loadable PIN box!",
                             LOG_TAG);
                    break;
                } else {
                    //STORE_TA_ERROR("%s displayed PIN box!", LOG_TAG);
                }
            }
        }
        return ret;
	}
}

// Display loadable images except PIN boxes
uint32_t displayLoadableResource() {
    uint32_t ret = 0;
    uint32_t screenWidth = getScreenWidth();
    uint32_t screenHeight = getScreenHeight();
    // display loadable background, prompts and cancel button
    for (uint32_t i = RES_BACKGROUND; i <= RES_CANCEL_BUTTON; i++) {
        if (g_loadable_img[i].width > 0 &&
            g_loadable_img[i].height > 0 &&
            g_loadable_img[i].to_display) {

			if (i == RES_SECURE_MODE_TEXT) {
				// special case
/*
				if (display_type == _SUPPORT_DUAL_LCD_MAIN) {
                    uint32_t len = ((PINPAD_SECURE_ICON_WIDTH + PINPAD_SECURE_ICON_TEXT_DISTANCE) * SCREEN_PIX_PER_DP) + g_loadable_img[i].height;
                    uint32_t y = g_preference.isRTL ?
                                 screenHeight - (screenHeight - len) / 2 - PINPAD_SECURE_ICON_WIDTH * SCREEN_PIX_PER_DP :
                                 ((screenHeight - len) / 2); // (screenWidth - len) / 2;  // Tedd

                    drawPinpadImage(IDX_SECURE_ICON, screenWidth - ((PINPAD_SECURE_ICON_TOP + PINPAD_SECURE_ICON_WIDTH) * SCREEN_PIX_PER_DP), y);

                    ret = drawImage(screenWidth - ((PINPAD_SECURE_ICON_TOP + PINPAD_SECURE_ICON_WIDTH - 7) * SCREEN_PIX_PER_DP),
                                    g_preference.isRTL ?
                                    y - PINPAD_SECURE_ICON_TEXT_DISTANCE * SCREEN_PIX_PER_DP - g_loadable_img[i].height :
                                    y + (PINPAD_SECURE_ICON_WIDTH + PINPAD_SECURE_ICON_TEXT_DISTANCE) * SCREEN_PIX_PER_DP,
                                    g_loadable_img[i].pointer,
                                    g_loadable_img[i].len);
				} else {
                    uint32_t len =
                            (PINPAD_SECURE_ICON_WIDTH +
                             PINPAD_SECURE_ICON_TEXT_DISTANCE) * SCREEN_PIX_PER_DP +
                            g_loadable_img[i].width;
                    uint32_t x = g_preference.isRTL ?
                                 screenWidth - (screenWidth - len) / 2 - PINPAD_SECURE_ICON_WIDTH * SCREEN_PIX_PER_DP :
                                 (screenWidth - len) / 2;

                    drawPinpadImage(IDX_SECURE_ICON, x,
                                    PINPAD_SECURE_ICON_TOP * SCREEN_PIX_PER_DP);

                    ret = drawImage(g_preference.isRTL ?
                                    x - PINPAD_SECURE_ICON_TEXT_DISTANCE * SCREEN_PIX_PER_DP - g_loadable_img[i].width :
                                    x + (PINPAD_SECURE_ICON_WIDTH + PINPAD_SECURE_ICON_TEXT_DISTANCE) * SCREEN_PIX_PER_DP,
                                    g_loadable_img[i].y,
                                    g_loadable_img[i].pointer,
                                    g_loadable_img[i].len);
				}
*/
			} else {
				ret = drawImage(g_loadable_img[i].x,
					g_loadable_img[i].y,
					g_loadable_img[i].pointer,
					g_loadable_img[i].len);
			}

			g_loadable_img[i].to_display = false;

			if (ret) {
				STORE_TA_ERROR
				("%s failed to display loadable img %d: len %d, x %d, y %d!",
					LOG_TAG, i, g_loadable_img[i].len,
					g_loadable_img[i].x, g_loadable_img[i].y);
				// return ret;
			} else {
			    if (display_type == _SUPPORT_DUAL_LCD_MAIN) {
                    DBG_LOG("%s display loadable img %d: len %d, x %d, y %d!",
                            LOG_TAG, i, g_loadable_img[i].len,
                            g_loadable_img[i].x, g_loadable_img[i].y);
			    } else {
                    DBG_LOG("%s display loadable img %d!",
                            LOG_TAG, i);
			    }
			}
		} else {	/*
				//STORE_TA_ERROR("%s no loadable img (%d) to display: len=%d, w=%d, h=%d, x=%d, y=%d",
				LOG_TAG,
				i,
				g_loadable_img[i].len,
				g_loadable_img[i].width,
				g_loadable_img[i].height,
				g_loadable_img[i].x,
				g_loadable_img[i].y);
			  */
		}
	}
	return ret;
}

uint32_t launchPinpad(
        bool start_tui_session,
        enum pin_box_type pinbox_type
) {
    uint32_t ret;
    g_cancel_button_enabled = false;

    if (start_tui_session) {
        setTuiPinMode(true);
        ret = startTuiSession(gBgImage, gBgImageSize);
        setTuiPinMode(false);
        if (ret) {
            return ret;
        }
        if (isStartTuiWithBackground() == false) {
            // display PIN Pad
/*#if defined(SUPPORT_SOFTKEY) \
		|| defined(SUPPORT_SOFTKEY_PUNCHCUT) \
 		|| defined(SUPPORT_SOFTKEY_PUNCHCUT_800) \
		|| defined(SUPPORT_SOFTKEY_PUNCHHOLE) \
		|| defined(SUPPORT_DISPLAY_HIDE_NOTCH) \
		|| defined(SUPPORT_DUAL_LCD_MAIN) \
		|| defined(SUPPORT_DUAL_LCD_SUB)*/
            if (g_preference.isBHR) {
                ret = drawPinpadImage(IDX_PINPAD_PINPAD_BHR + g_preference.isRTL, 0, 0);
            } else
/*#endif*/
                ret = drawPinpadImage(IDX_PINPAD_PINPAD + g_preference.isRTL, 0, 0);
            if (ret) {
                STORE_TA_ERROR("%s cannot display PIN background!. ret=0x%x", LOG_TAG, ret);
                return ret;
            }
        }

        displayPinButtons();

        DBG_LOG("%s open session done", LOG_TAG);

        ret = displayLoadableResource();
        if (ret) {
            STORE_TA_ERROR("%s Unable to display loadable image! ret=0x%x",
                           LOG_TAG, ret);
            return ret;
        }

        ret = displayPinBox(pinbox_type);
        if (ret) {
            STORE_TA_ERROR("%s Unable to display pin box. ret=0x%x", LOG_TAG, ret);
        }
        DBG_LOG("%s display images done", LOG_TAG);
    } else {
        ret = updatePinpadScreen(pinbox_type);
    }

    showFrameBuffer();
    return ret;
}

uint32_t updatePinpadScreen(enum pin_box_type pinbox_type) {
    uint32_t ret;

    // update background and action bar text if changed
    for (uint32_t i = RES_BACKGROUND; i <= RES_ACTION_BAR_TEXT; i++) {
        if (g_loadable_img[i].width > 0 &&
            g_loadable_img[i].height > 0 &&
            g_loadable_img[i].to_display) {

            ret = drawImage(g_loadable_img[i].x,
                            g_loadable_img[i].y,
                            g_loadable_img[i].pointer,
                            g_loadable_img[i].len);

            g_loadable_img[i].to_display = false;
        } else {

            DBG_LOG("%s No need to display resource %d", LOG_TAG,
                    i);
        }

    }

    // TUI session is already started, check whether we need to clean all prompts first or not
    if ((g_loadable_img[RES_PROMPT_ABOVE_PINBOX].to_display &&
         g_loadable_img[RES_PROMPT_ABOVE_PINBOX].width > 0 &&
         g_loadable_img[RES_PROMPT_ABOVE_PINBOX].height > 0 &&
         !g_loadable_img
         [RES_PROMPT_ABOVE_PINBOX].new_prompt_covers_old_prompt)
        || (g_loadable_img[RES_PROMPT_BELOW_PINBOX].to_display
            && g_loadable_img[RES_PROMPT_BELOW_PINBOX].width > 0
            && g_loadable_img[RES_PROMPT_BELOW_PINBOX].height > 0
            &&
            !g_loadable_img
            [RES_PROMPT_BELOW_PINBOX].new_prompt_covers_old_prompt)) {
        DBG_LOG
        ("%s New prompt cannot fully cover the old prompt! above=%d, below=%d",
         LOG_TAG,
         g_loadable_img
         [RES_PROMPT_ABOVE_PINBOX].new_prompt_covers_old_prompt,
         g_loadable_img
         [RES_PROMPT_BELOW_PINBOX].new_prompt_covers_old_prompt);

        // remove prompts and pin boxes
        ret = drawPinpadImage(IDX_CLEAR_TEXT, 0, PINPAD_PROMPT_AREA_TOP);
        if (ret) {
            STORE_TA_ERROR("%s display error!", LOG_TAG);
            return ret;
        }
        // display prompts
        ret = displayLoadableResource();
        if (ret) {
            STORE_TA_ERROR("%s Unable to display loadable image!",
                           LOG_TAG);
            return ret;
        }
        // display pin boxes
        ret = displayPinBox(pinbox_type);
        if (ret) {
            STORE_TA_ERROR("%s Unable to display pin box", LOG_TAG);
        }
    } else {
        DBG_LOG
        ("%s New prompt can fully cover the old prompt! above=%d, below=%d",
         LOG_TAG,
         g_loadable_img
         [RES_PROMPT_ABOVE_PINBOX].new_prompt_covers_old_prompt,
         g_loadable_img
         [RES_PROMPT_BELOW_PINBOX].new_prompt_covers_old_prompt);

        // Only need to display prompts

        if (g_loadable_img[RES_PROMPT_ABOVE_PINBOX].to_display &&
            g_loadable_img[RES_PROMPT_ABOVE_PINBOX].width > 0 &&
            g_loadable_img[RES_PROMPT_ABOVE_PINBOX].height > 0) {
            ret =
                    drawImage(g_loadable_img[RES_PROMPT_ABOVE_PINBOX].x,
                              g_loadable_img[RES_PROMPT_ABOVE_PINBOX].y,
                              g_loadable_img[RES_PROMPT_ABOVE_PINBOX].pointer,
                              g_loadable_img[RES_PROMPT_ABOVE_PINBOX].len);
            g_loadable_img[RES_PROMPT_ABOVE_PINBOX].to_display =
                    false;
        }

        if (g_loadable_img[RES_PROMPT_BELOW_PINBOX].to_display &&
            g_loadable_img[RES_PROMPT_BELOW_PINBOX].width > 0 &&
            g_loadable_img[RES_PROMPT_BELOW_PINBOX].height > 0) {
            ret = drawImage(g_loadable_img[RES_PROMPT_BELOW_PINBOX].x,
                            g_loadable_img[RES_PROMPT_BELOW_PINBOX].y,
                            g_loadable_img[RES_PROMPT_BELOW_PINBOX].pointer,
                            g_loadable_img[RES_PROMPT_BELOW_PINBOX].len);
            g_loadable_img[RES_PROMPT_BELOW_PINBOX].to_display =
                    false;
        }

        ret = displayPinBox(pinbox_type);
        if (ret) {
            STORE_TA_ERROR("%s Unable to display pin box", LOG_TAG);
        }

    }

    ret = showFrameBuffer();
    if (ret) {
        STORE_TA_ERROR("%s showFrameBuffer - TIMA_ERROR_TUI_DISPLAY_ERROR", LOG_TAG);
    }
    return ret;
}

/*
  Clean PIN and PIN dots
*/
void clearPinData() {
    // clear input PIN buffer
    memset(g_pin, 0, PIN_SIZE);
    g_pin_length = 0;
    g_current_button = PAD_NOKEY_VAL;
}

void clearLoadableResource() {
    int type;
    for (type = 0; type < RES_END - RES_BACKGROUND; type++) {
        if (g_loadable_img[type].pointer != 0) {
            DBG_LOG("clear loadable resource: %d", type);
            TZ_free(g_loadable_img[type].pointer);
            memset(&g_loadable_img[type], 0, sizeof(loadable_img_t));
        }
    }
}

uint32_t setPinpadTimer() {
    uint32_t ret = TZ_get_secure_timestamp(&tui_verify_timestamp);
    if (ret != TZ_API_OK) {
        TTY_LOG("%s Error fetching secure timestamp, Error: %d",
                LOG_TAG, ret);
        tui_verify_timestamp = 0;
    }
    DBG_LOG("%s TUI timer starts at: %lld", LOG_TAG, tui_verify_timestamp);
    return ret;
}

bool isPinpadTimerExpired() {
    uint64_t current_timestamp = 0;
    uint32_t timer_val = 0;
    uint32_t ret = TZ_get_secure_timestamp(&current_timestamp);
    if (ret != TZ_API_OK) {
        TTY_LOG("%s Error fetching secure timestamp, Error: %d",
                LOG_TAG, ret);
        current_timestamp = tui_verify_timestamp = 0;
        return true;
    }

    if (tui_verify_timestamp == 0) {
        TTY_LOG("%s timer is not set, please set the timer", LOG_TAG);
        return true;
    }

    DBG_LOG("%s : Current timestamp = %lld", LOG_TAG, current_timestamp);
    timer_val = (current_timestamp - tui_verify_timestamp) / (TS_FORMAT);
    DBG_LOG("%s : Current timer val : %d", LOG_TAG, timer_val);
    if (timer_val >= SPAY_TUI_MAX_TIMER_VAL_SECS) {
        DBG_LOG("%s, *** Timer expired ***", LOG_TAG);
        tui_verify_timestamp = 0;
        return true;
    }
    DBG_LOG("%s : You have %d more sec(s)", LOG_TAG,
            (SPAY_TUI_MAX_TIMER_VAL_SECS - timer_val));
    return false;
}

#define COUNT_PINPAD_IMAGES 36
#define FREE_IMAGES 0xFF
void setPinpadImages(uint32_t type, uint8_t *buf) {
    if(type == FREE_IMAGES) {
        for (int i=0; i<COUNT_PINPAD_IMAGES; i++) {
            if(resource[i] != NULL) {
                TZ_free(resource[i]);
                resource[i] = NULL;
            }
        }
        return;
    }

    uint32_t total = *((uint32_t *) buf);

    uint32_t offset = 4;
    uint8_t count = buf[offset++];

    if (count != COUNT_PINPAD_IMAGES)
        TTY_LOG("setpinpadimages count error : %d", count);

    for (int i = 0; i < count; i++) {
        sizes[i] = (buf[offset] << 8) + buf[offset + 1];
        offset += 2;

        sizes[i] = (sizes[i]<<16) + (buf[offset] << 8) + buf[offset + 1];
        offset += 2;

        if(resource[i] != NULL) {
            TZ_free(resource[i]);
        }

        resource[i] = TZ_malloc(sizes[i]);

        memcpy(resource[i], buf + offset, sizes[i]);
        offset += sizes[i];
    }

    if (total != offset - 4) {
        TTY_LOG("total size %d is not matched with written bytes %d", total, offset - 4);
    }
}
