/*
 * Copyright (c) 2009 Openmoko Inc.
 *
 * Authors   Holger Hans Peter Freyther <zecke@openmoko.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "wl-keyboard.h"

#include <string.h>
#include <ctype.h>
#include "guilib.h"
#include "input.h"
#include "wikilib.h"
#include "msg.h"
#include "wiki_info.h"
#include "search.h"
#include "glyph.h"

// definition for all the keyboard images generated by the Makefile
// files line keyboard_abc.png will result in a keyboard_abc_image variable
// included via all_images.h (see mahatma/build for generated headers)
#define LCD_ImageType static struct guilib_image
#define bytes data
#include "all_images.h"
#undef LCD_ImageType
#undef bytes

#define DBG_KEYBOARD 0

/* some control commands */
#define INTERNAL_SHIFT   (-23)
#define INTERNAL_NUMBER  (-42)

static struct guilib_image *image_data;
int keyboard_type = 0;

/* qwerty keyboard by columns */
#define KEY(l_x, l_y, r_x, r_y, keycode) { .left_x = l_x, .right_x = r_x, .left_y = l_y, .right_y = r_y, .key = keycode, }
/*
static struct keyboard_key qwerty_char[] = {
	KEY(0, 126, 23, 152, 'q'),
	KEY(0+12, 153, 23+12, 180, 'a'),
	KEY(0, 181, 30, 207, ' '),

	KEY(24, 126, 47, 152, 'w'),
	KEY(24+12, 153, 47+12, 180, 's'),
	KEY(24+12, 181, 47+12, 207, 'z'),

	KEY(48, 126, 71, 152, 'e'),
	KEY(48+12, 153, 71+12, 180, 'd'),
	KEY(48+12, 181, 71+12, 207, 'x'),

	KEY(72, 126, 95, 152, 'r'),
	KEY(72+12, 153, 95+12, 180, 'f'),
	KEY(72+12, 181, 95+12, 207, 'c'),

	KEY(96, 126, 119, 152, 't'),
	KEY(96+12, 153, 119+12, 180, 'g'),
	KEY(96+12, 181, 119+12, 207, 'v'),

	KEY(120, 126, 143, 152, 'y'),
	KEY(120+12, 153, 143+12, 180, 'h'),
	KEY(120+12, 181, 143+12, 207, 'b'),

	KEY(144, 126, 167, 152, 'u'),
	KEY(144+12, 153, 167+12, 180, 'j'),
	KEY(144+12, 181, 167+12, 207, 'n'),

	KEY(168, 126, 191, 152, 'i'),
	KEY(168+12, 153, 191+12, 180, 'k'),
	KEY(168+12, 181, 191+12, 207, 'm'),

	KEY(192, 126, 215, 152, 'o'),
	KEY(192+12, 153, 215+12, 180, 'l'),

	KEY(216, 126, 239, 152, 'p'),
	KEY(209, 181, 239, 207, WL_KEY_BACKSPACE),

	KEY(199, 84, 233, 118, WL_KEY_NLS),
};
*/
static struct keyboard_key qwerty_char[] = {
	KEY(0, 126, 23, 152, 'q'),
	KEY(0, 153, 23, 180, 'a'),
	KEY(0, 181, 23, 207, 'z'),

	KEY(24, 126, 47, 152, 'w'),
	KEY(24, 153, 47, 180, 's'),
	KEY(24, 181, 47, 207, 'x'),

	KEY(48, 126, 71, 152, 'e'),
	KEY(48, 153, 71, 180, 'd'),
	KEY(48, 181, 71, 207, 'c'),

	KEY(72, 126, 95, 152, 'r'),
	KEY(72, 153, 95, 180, 'f'),
	KEY(72, 181, 95, 207, 'v'),

	KEY(96, 126, 119, 152, 't'),
	KEY(96, 153, 119, 180, 'g'),
	KEY(96, 181, 143, 207, ' '),

	KEY(120, 126, 143, 152, 'y'),
	KEY(120, 153, 143, 180, 'h'),

	KEY(144, 126, 167, 152, 'u'),
	KEY(144, 153, 167, 180, 'j'),
	KEY(144, 181, 167, 207, 'b'),

	KEY(168, 126, 191, 152, 'i'),
	KEY(168, 153, 191, 180, 'k'),
	KEY(168, 181, 191, 207, 'n'),

	KEY(192, 126, 215, 152, 'o'),
	KEY(192, 153, 215, 180, 'l'),
	KEY(192, 181, 215, 207, 'm'),

	KEY(216, 126, 239, 152, 'p'),
	KEY(216, 153, 239, 180, WL_KEY_BACKSPACE),
	KEY(216, 181, 239, 207, INTERNAL_NUMBER),

	KEY(199, 84, 233, 118, WL_KEY_NLS),
};
static struct keyboard_key qwerty_num[] = {
	KEY(0, 126, 23, 152, '1'),
	KEY(0, 153, 23, 180, '*'),
	KEY(0, 181, 23, 207, '@'),

	KEY(24, 126, 47, 152, '2'),
	KEY(24, 153, 47, 180, '$'),
	KEY(24, 181, 47, 207, '?'),

	KEY(48, 126, 71, 152, '3'),
	KEY(48, 153, 71, 180, '%'),
	KEY(48, 181, 71, 207, '!'),

	KEY(72, 126, 95, 152, '4'),
	KEY(72, 153, 95, 180, '#'),
	KEY(72, 181, 95, 207, '&'),

	KEY(96, 126, 119, 152, '5'),
	KEY(96, 153, 119, 180, '('),
	KEY(96, 181, 143, 207, ' '),

	KEY(120, 126, 143, 152, '6'),
	KEY(120, 153, 143, 180, ')'),

	KEY(144, 126, 167, 152, '7'),
	KEY(144, 153, 167, 180, '-'),
	KEY(144, 181, 167, 207, ','),

	KEY(168, 126, 191, 152, '8'),
	KEY(168, 153, 191, 180, '+'),
	KEY(168, 181, 191, 207, '.'),

	KEY(192, 126, 215, 152, '9'),
	KEY(192, 153, 215, 180, '='),
	KEY(192, 181, 215, 207, '\''),

	KEY(216, 126, 239, 152, '0'),
	KEY(216, 153, 239, 180, WL_KEY_BACKSPACE),
	KEY(216, 181, 239, 207, INTERNAL_NUMBER),

	KEY(199, 84, 233, 118, WL_KEY_NLS),
};
static struct keyboard_key password_char[] = {
	KEY(175, 81, 204, 105, 'Y'),

	KEY(0, 126, 23, 152, 'q'),
	KEY(0, 153, 23, 180, 'a'),
	KEY(0, 181, 23, 207, 'z'),

	KEY(24, 126, 47, 152, 'w'),
	KEY(24, 153, 47, 180, 's'),
	KEY(24, 181, 47, 207, 'x'),

	KEY(48, 126, 71, 152, 'e'),
	KEY(48, 153, 71, 180, 'd'),
	KEY(48, 181, 71, 207, 'c'),

	KEY(72, 126, 95, 152, 'r'),
	KEY(72, 153, 95, 180, 'f'),
	KEY(72, 181, 95, 207, 'v'),

	KEY(96, 126, 119, 152, 't'),
	KEY(96, 153, 119, 180, 'g'),
	KEY(96, 181, 143, 207, ' '),

	KEY(120, 126, 143, 152, 'y'),
	KEY(120, 153, 143, 180, 'h'),

	KEY(144, 126, 167, 152, 'u'),
	KEY(144, 153, 167, 180, 'j'),
	KEY(144, 181, 167, 207, 'b'),

	KEY(168, 126, 191, 152, 'i'),
	KEY(168, 153, 191, 180, 'k'),
	KEY(168, 181, 191, 207, 'n'),

	KEY(192, 126, 215, 152, 'o'),
	KEY(192, 153, 215, 180, 'l'),
	KEY(192, 181, 215, 207, 'm'),

	KEY(216, 126, 239, 152, 'p'),
	KEY(216, 153, 239, 180, WL_KEY_BACKSPACE),
	KEY(216, 181, 239, 207, INTERNAL_NUMBER),
};
static struct keyboard_key password_num[] = {
	KEY(175, 81, 204, 105, 'Y'),

	KEY(0, 126, 23, 152, '1'),
	KEY(0, 153, 23, 180, '*'),
	KEY(0, 181, 23, 207, '@'),

	KEY(24, 126, 47, 152, '2'),
	KEY(24, 153, 47, 180, '$'),
	KEY(24, 181, 47, 207, '?'),

	KEY(48, 126, 71, 152, '3'),
	KEY(48, 153, 71, 180, '%'),
	KEY(48, 181, 71, 207, '!'),

	KEY(72, 126, 95, 152, '4'),
	KEY(72, 153, 95, 180, '#'),
	KEY(72, 181, 95, 207, '&'),

	KEY(96, 126, 119, 152, '5'),
	KEY(96, 153, 119, 180, '('),
	KEY(96, 181, 143, 207, ' '),

	KEY(120, 126, 143, 152, '6'),
	KEY(120, 153, 143, 180, ')'),

	KEY(144, 126, 167, 152, '7'),
	KEY(144, 153, 167, 180, '-'),
	KEY(144, 181, 167, 207, ','),

	KEY(168, 126, 191, 152, '8'),
	KEY(168, 153, 191, 180, '+'),
	KEY(168, 181, 191, 207, '.'),

	KEY(192, 126, 215, 152, '9'),
	KEY(192, 153, 215, 180, '='),
	KEY(192, 181, 215, 207, '\''),

	KEY(216, 126, 239, 152, '0'),
	KEY(216, 153, 239, 180, WL_KEY_BACKSPACE),
	KEY(216, 181, 239, 207, INTERNAL_NUMBER),
};
static struct keyboard_key clear_history[] = {
	KEY(147, 181, 192, 207, 'Y'),
	KEY(193, 181, 238, 207, 'N'),
};
static struct keyboard_key restriction[] = {
	KEY(12, 148, 227, 170, 'Y'),
};
static struct keyboard_key filter_on_off[] = {
	KEY(12, 134, 227, 157, 'Y'),
	KEY(12, 163, 227, 186, 'N'),
};
static struct keyboard_key filter_option[] = {
	KEY(12, 105, 227, 128, 'Y'),
	KEY(12, 134, 227, 157, 'N'),
	KEY(12, 163, 227, 187, 'P'),
};
/*
 * The secret of the position and size of the keyboard
 * is shared between search.c and this file.
 */

#define KEY_BUBBLE_STEM_UNDER_KEYPAD_PIXELS 4
#define KEY_BUBBLE_TOTAL_HEIGHT 57
#define KEY_BUBBLE_TOTAL_WIDTH 28
#define KEY_BUBBLE_TOTAL_WIDTH_BYTES ((KEY_BUBBLE_TOTAL_WIDTH + 7) / 8 + 1)
#define KEY_BUBBLE_STEM_WIDTH 22
#define KEY_BUBBLE_STEM_HEIGHT 18
#define KEY_BUBBLE_STAY_TIME 0.6
static struct keyboard_key *pre_key = NULL;
static int keyboard_key_invert_dalay = 0;
static char key_bubble_save[KEY_BUBBLE_TOTAL_HEIGHT * KEY_BUBBLE_TOTAL_WIDTH_BYTES];
static int key_bubble_save_x_start_byte;
static int key_bubble_save_y_start;
static int key_bubble_save_width_bytes;

static int kb_mode = KEYBOARD_CHAR;
extern unsigned char *framebuffer;

void keyboard_set_mode(int mode)
{
	kb_mode = mode;

	if(kb_mode == KEYBOARD_CHAR) {
		image_data = &keyboard_abc_image;
	} else if(kb_mode == KEYBOARD_NUM) {
		image_data = &keyboard_123_image;
//      } else if(kb_mode == KEYBOARD_CLEAR_HISTORY) {
//              image_data = &image_data_clear_history;
	} else {
		image_data = NULL;
	}
}

int keyboard_get_mode()
{
	return kb_mode;
}

int nls_button_enabled()
{
	return (get_search_string_len() == 0 && get_wiki_count() > 1);
}


void keyboard_paint()
{
	pre_key = NULL;
	if(kb_mode == KEYBOARD_CHAR || kb_mode == KEYBOARD_PASSWORD_CHAR) {
		image_data = &keyboard_abc_image;
	} else if(kb_mode == KEYBOARD_NUM || kb_mode == KEYBOARD_PASSWORD_NUM) {
		image_data = &keyboard_123_image;
//      } else if(kb_mode == KEYBOARD_CLEAR_HISTORY) {
//           image_data = &clear_history_image;
	} else {
		return;
	}

	guilib_fb_lock();
	guilib_blit_image(image_data, 0, guilib_framebuffer_height() - image_data->height);
	if (nls_button_enabled())
		guilib_blit_image(&nls_image, 200, guilib_framebuffer_height() - image_data->height - 40);
	guilib_fb_unlock();
}

unsigned int keyboard_height()
{
	if (!image_data)
		return 0;
	else
		return image_data->height;
}

/**
 * Coordinates are screen absolute ones
 */
#define KEY_GAP1 1
#define KEY_GAP2 0
#define KEY_GAP3 0
#define KEY_GAP4 1
struct keyboard_key * keyboard_get_data(int x, int y)
{
	unsigned int i;

	if (kb_mode == KEYBOARD_CHAR) {
		for (i = 0; i < ARRAY_SIZE(qwerty_char); ++i) {
			if (qwerty_char[i].left_x + KEY_GAP1 <= x && qwerty_char[i].right_x - KEY_GAP2 >= x
			    && qwerty_char[i].left_y + KEY_GAP3 <= y && qwerty_char[i].right_y - KEY_GAP4 >= y
			    && (nls_button_enabled() || qwerty_char[i].key != WL_KEY_NLS)) {
				DP(DBG_KEYBOARD, ("O Entered '%c'\n", qwerty_char[i].key));
				return &qwerty_char[i];
			}
		}
	}
	else if (kb_mode == KEYBOARD_NUM) {
		for (i = 0; i < ARRAY_SIZE(qwerty_num); ++i) {
			if (qwerty_num[i].left_x + KEY_GAP1 <= x && qwerty_num[i].right_x - KEY_GAP2 >= x
			    && qwerty_num[i].left_y + KEY_GAP3 <= y && qwerty_num[i].right_y - KEY_GAP4 >= y
			    && (nls_button_enabled() || qwerty_char[i].key != WL_KEY_NLS)) {
				DP(DBG_KEYBOARD, ("O Entered '%c'\n", qwerty_num[i].key));
				return &qwerty_num[i];
			}
		}
	}
	else if (kb_mode == KEYBOARD_CLEAR_HISTORY) {
		for (i = 0; i < ARRAY_SIZE(clear_history); ++i) {
			if (clear_history[i].left_x + KEY_GAP1 <= x && clear_history[i].right_x - KEY_GAP2 >= x
			    && clear_history[i].left_y + KEY_GAP3 <= y && clear_history[i].right_y - KEY_GAP4 >= y) {
				return &clear_history[i];
			}
		}
	}
	else if (kb_mode == KEYBOARD_PASSWORD_CHAR) {
		for (i = 0; i < ARRAY_SIZE(password_char); ++i) {
			if (password_char[i].left_x + KEY_GAP1 <= x && password_char[i].right_x - KEY_GAP2 >= x
			    && password_char[i].left_y + KEY_GAP3 <= y && password_char[i].right_y - KEY_GAP4 >= y) {
				return &password_char[i];
			}
		}
	}
	else if (kb_mode == KEYBOARD_PASSWORD_NUM) {
		for (i = 0; i < ARRAY_SIZE(password_num); ++i) {
			if (password_num[i].left_x + KEY_GAP1 <= x && password_num[i].right_x - KEY_GAP2 >= x
			    && password_num[i].left_y + KEY_GAP3 <= y && password_num[i].right_y - KEY_GAP4 >= y) {
				return &password_num[i];
			}
		}
	}
	else if (kb_mode == KEYBOARD_RESTRICTED) {
		for (i = 0; i < ARRAY_SIZE(restriction); ++i) {
			if (restriction[i].left_x + KEY_GAP1 <= x && restriction[i].right_x - KEY_GAP2 >= x
			    && restriction[i].left_y + KEY_GAP3 <= y && restriction[i].right_y - KEY_GAP4 >= y) {
				return &restriction[i];
			}
		}
	}
	else if (kb_mode == KEYBOARD_FILTER_ON_OFF) {
		for (i = 0; i < ARRAY_SIZE(filter_on_off); ++i) {
			if (filter_on_off[i].left_x + KEY_GAP1 <= x && filter_on_off[i].right_x - KEY_GAP2 >= x
			    && filter_on_off[i].left_y + KEY_GAP3 <= y && filter_on_off[i].right_y - KEY_GAP4 >= y) {
				return &filter_on_off[i];
			}
		}
	}
	else if (kb_mode == KEYBOARD_FILTER_OPTION) {
		for (i = 0; i < ARRAY_SIZE(filter_option); ++i) {
			if (filter_option[i].left_x + KEY_GAP1 <= x && filter_option[i].right_x - KEY_GAP2 >= x
			    && filter_option[i].left_y + KEY_GAP3 <= y && filter_option[i].right_y - KEY_GAP4 >= y) {
				return &filter_option[i];
			}
		}
	}

	return NULL;
}

void draw_key_bubble(int start_x, int start_y, int end_x, int end_y, int key)
{
	int i, j;
	int bubble_start_x, bubble_start_y, bubble_end_x, bubble_end_y;
	int bubble_stem_start_x, bubble_stem_start_y, bubble_stem_end_x, bubble_stem_end_y;
	int bubble_stem_left_diff, bubble_stem_right_diff;
	char s[2];
	int width;

	guilib_fb_lock();
	bubble_start_x = start_x - (KEY_BUBBLE_TOTAL_WIDTH - (end_x - start_x + 1)) / 2;
	if (bubble_start_x < 0)
		bubble_start_x = 0;
	else if (bubble_start_x + KEY_BUBBLE_TOTAL_WIDTH > LCD_BUF_WIDTH_PIXELS)
		bubble_start_x = LCD_BUF_WIDTH_PIXELS - KEY_BUBBLE_TOTAL_WIDTH;
	bubble_start_y = start_y + KEY_BUBBLE_STEM_UNDER_KEYPAD_PIXELS + KEY_BUBBLE_STEM_HEIGHT - KEY_BUBBLE_TOTAL_HEIGHT;
	bubble_end_x = bubble_start_x + KEY_BUBBLE_TOTAL_WIDTH - 1;
	bubble_end_y = bubble_start_y + KEY_BUBBLE_TOTAL_HEIGHT - KEY_BUBBLE_STEM_HEIGHT - 1;

	bubble_stem_start_x = bubble_start_x + (KEY_BUBBLE_TOTAL_WIDTH - KEY_BUBBLE_STEM_WIDTH) / 2;
	if (bubble_stem_start_x < 0)
		bubble_stem_start_x = 0;
	else if (bubble_stem_start_x + KEY_BUBBLE_STEM_WIDTH > LCD_BUF_WIDTH_PIXELS)
		bubble_stem_start_x = LCD_BUF_WIDTH_PIXELS - KEY_BUBBLE_STEM_WIDTH;
	bubble_stem_end_x = bubble_stem_start_x + KEY_BUBBLE_STEM_WIDTH - 1;
	bubble_stem_start_y = bubble_end_y + 1;
	bubble_stem_end_y = bubble_start_y + KEY_BUBBLE_TOTAL_HEIGHT - 1;

	bubble_stem_left_diff = bubble_stem_start_x - bubble_start_x;
	bubble_stem_right_diff = bubble_end_x - bubble_stem_end_x;

	key_bubble_save_x_start_byte = bubble_start_x / 8;
	key_bubble_save_y_start = bubble_start_y;
	key_bubble_save_width_bytes = KEY_BUBBLE_TOTAL_WIDTH_BYTES;
	if (key_bubble_save_x_start_byte + key_bubble_save_width_bytes > LCD_BUF_WIDTH_BYTES)
		key_bubble_save_width_bytes = LCD_BUF_WIDTH_BYTES - key_bubble_save_x_start_byte;
	for (i = 0; i < KEY_BUBBLE_TOTAL_HEIGHT; i++)
	{
		memcpy(&key_bubble_save[i * KEY_BUBBLE_TOTAL_WIDTH_BYTES],
			&framebuffer[(key_bubble_save_y_start + i) * LCD_BUF_WIDTH_BYTES + key_bubble_save_x_start_byte], key_bubble_save_width_bytes);
	}

	for (i = 0; i < KEY_BUBBLE_TOTAL_HEIGHT - KEY_BUBBLE_STEM_HEIGHT; i++)
	{
		if (i == 0 || i == 1)
		{
			for (j = 2 - i; j < KEY_BUBBLE_TOTAL_WIDTH - 2 + i; j++)
				lcd_set_framebuffer_pixel(bubble_start_x + j, bubble_start_y + i);
		}
		else if (i == 2)
		{
			lcd_set_framebuffer_pixel(bubble_start_x + 2 - i, bubble_start_y + i);
			lcd_set_framebuffer_pixel(bubble_start_x + 3 - i, bubble_start_y + i);
			lcd_set_framebuffer_pixel(bubble_start_x + 4 - i, bubble_start_y + i);
			lcd_set_framebuffer_pixel(bubble_start_x + KEY_BUBBLE_TOTAL_WIDTH - 3 + i, bubble_start_y + i);
			lcd_set_framebuffer_pixel(bubble_start_x + KEY_BUBBLE_TOTAL_WIDTH - 4 + i, bubble_start_y + i);
			lcd_set_framebuffer_pixel(bubble_start_x + KEY_BUBBLE_TOTAL_WIDTH - 5 + i, bubble_start_y + i);
			for (j = 5 - i; j < KEY_BUBBLE_TOTAL_WIDTH - 5 + i; j++)
				lcd_clear_framebuffer_pixel(bubble_start_x + j, bubble_start_y + i);
		}
		else if (KEY_BUBBLE_TOTAL_HEIGHT - KEY_BUBBLE_STEM_HEIGHT - 3 <= i && i <= KEY_BUBBLE_TOTAL_HEIGHT - KEY_BUBBLE_STEM_HEIGHT - 1 )
		{
			int left_diff, right_diff;

			left_diff = bubble_stem_left_diff / (KEY_BUBBLE_TOTAL_HEIGHT - KEY_BUBBLE_STEM_HEIGHT - i);
			if (bubble_stem_left_diff && ! left_diff)
				left_diff = 1;
			bubble_stem_left_diff -= left_diff;
			right_diff = bubble_stem_right_diff / (KEY_BUBBLE_TOTAL_HEIGHT - KEY_BUBBLE_STEM_HEIGHT - i);
			if (bubble_stem_right_diff && ! right_diff)
				right_diff = 1;
			bubble_stem_right_diff -= right_diff;
			lcd_set_framebuffer_pixel(bubble_start_x + left_diff, bubble_start_y + i);
			lcd_set_framebuffer_pixel(bubble_start_x + left_diff + 1, bubble_start_y + i);
			lcd_set_framebuffer_pixel(bubble_start_x + KEY_BUBBLE_TOTAL_WIDTH - right_diff - 1, bubble_start_y + i);
			lcd_set_framebuffer_pixel(bubble_start_x + KEY_BUBBLE_TOTAL_WIDTH - right_diff - 2, bubble_start_y + i);
			for (j = left_diff + 2; j < KEY_BUBBLE_TOTAL_WIDTH - right_diff - 2; j++)
				lcd_clear_framebuffer_pixel(bubble_start_x + j, bubble_start_y + i);
		}
		else
		{
			lcd_set_framebuffer_pixel(bubble_start_x, bubble_start_y + i);
			lcd_set_framebuffer_pixel(bubble_start_x + 1, bubble_start_y + i);
			lcd_set_framebuffer_pixel(bubble_start_x + KEY_BUBBLE_TOTAL_WIDTH - 1, bubble_start_y + i);
			lcd_set_framebuffer_pixel(bubble_start_x + KEY_BUBBLE_TOTAL_WIDTH - 2, bubble_start_y + i);
			for (j = 2; j < KEY_BUBBLE_TOTAL_WIDTH - 2; j++)
				lcd_clear_framebuffer_pixel(bubble_start_x + j, bubble_start_y + i);
		}
	}

	for (i = 0; i < KEY_BUBBLE_STEM_HEIGHT; i++) // drawn from the bottom of the stem
	{
		if (i == 0) // bottom
		{
			for (j = 3; j < KEY_BUBBLE_STEM_WIDTH - 3; j++)
				lcd_set_framebuffer_pixel(bubble_stem_start_x + j, bubble_stem_start_y + (KEY_BUBBLE_STEM_HEIGHT - 1 - i));
		}
		else if (1 <= i && i <= 2)
		{
			lcd_set_framebuffer_pixel(bubble_stem_start_x + 3 - i, bubble_stem_start_y + (KEY_BUBBLE_STEM_HEIGHT - 1 - i));
			lcd_set_framebuffer_pixel(bubble_stem_start_x + KEY_BUBBLE_STEM_WIDTH - 4 + i, bubble_stem_start_y + (KEY_BUBBLE_STEM_HEIGHT - 1 - i));
			for (j = 4 - i; j < KEY_BUBBLE_STEM_WIDTH - 4 + i; j++)
				lcd_clear_framebuffer_pixel(bubble_stem_start_x + j, bubble_stem_start_y + (KEY_BUBBLE_STEM_HEIGHT - 1 - i));
		}
		else
		{
			lcd_set_framebuffer_pixel(bubble_stem_start_x, bubble_stem_start_y + (KEY_BUBBLE_STEM_HEIGHT - 1 - i));
			lcd_set_framebuffer_pixel(bubble_stem_start_x + KEY_BUBBLE_STEM_WIDTH - 1, bubble_stem_start_y + (KEY_BUBBLE_STEM_HEIGHT - 1 - i));
			for (j = 1; j < KEY_BUBBLE_STEM_WIDTH - 1; j++)
				lcd_clear_framebuffer_pixel(bubble_stem_start_x + j, bubble_stem_start_y + (KEY_BUBBLE_STEM_HEIGHT - 1 - i));
		}
	}

	s[0] = toupper(key);
	s[1] = '\0';
	width = get_external_str_pixel_width(s, TITLE_FONT_IDX);
	render_string(TITLE_FONT_IDX, bubble_start_x + (KEY_BUBBLE_TOTAL_WIDTH - width) / 2, bubble_start_y + 1, s, 1, 0);
	guilib_fb_unlock();
}

void restore_key_bubble(void)
{
	int i;

	guilib_fb_lock();
	for (i = 0; i < KEY_BUBBLE_TOTAL_HEIGHT; i++)
		memcpy(&framebuffer[(key_bubble_save_y_start + i) * LCD_BUF_WIDTH_BYTES + key_bubble_save_x_start_byte],
			&key_bubble_save[i * KEY_BUBBLE_TOTAL_WIDTH_BYTES], key_bubble_save_width_bytes);
	guilib_fb_unlock();
}

void keyboard_process_key_invert(struct keyboard_key *key)
{
	int start_x, start_y, end_x, end_y;

	start_x = key->left_x + 2;
	start_y = key->left_y + 2;
	end_x = key->right_x - 2;
	end_y = key->right_y - 2;
	if (strchr("qaz1*@", key->key))
		start_x++;
	if (strchr("p0", key->key) || key->key == WL_KEY_BACKSPACE)
		end_x--;
	if (strchr("asdfghjklzxcvbnm*$%#()-+=<@?!& ,.'", key->key) || key->key == WL_KEY_BACKSPACE)
		start_y++;
	if (key->key == ' ' || key->key == WL_KEY_BACKSPACE || key->key == WL_KEY_NLS || isupper(key->key))
	{
		guilib_invert_area(start_x,start_y,end_x,end_y);
		guilib_invert_area(start_x,start_y,start_x,start_y);
		guilib_invert_area(start_x,end_y,start_x,end_y);
		guilib_invert_area(end_x,start_y,end_x,start_y);
		guilib_invert_area(end_x,end_y,end_x,end_y);
	}
	else
	{
		draw_key_bubble(start_x, start_y, end_x, end_y, key->key);
	}
	keyboard_key_invert_dalay = 0;
}

int keyboard_key_inverted(void)
{
	if (pre_key)
		return 1;
	else
		return 0;
}

void keyboard_key_invert(struct keyboard_key *key)
{
	guilib_fb_lock();
	if (key && key->key == INTERNAL_NUMBER)
		pre_key = NULL;
	else
	{
		if (pre_key)
		{
			keyboard_key_reset_invert(KEYBOARD_RESET_INVERT_NOW, 0);
		}
		if (key)
		{
			keyboard_process_key_invert(key);
			pre_key = key;
		}
	}
	guilib_fb_unlock();
	keyboard_key_invert_dalay = 0;
}

int keyboard_key_reset_invert(int bFlag, unsigned long ev_time)
{
	static unsigned long start_time;
	int rc = 0;

	if (pre_key)
	{
		if (bFlag == KEYBOARD_RESET_INVERT_DELAY)
		{
			start_time = ev_time;
			keyboard_key_invert_dalay = 1;
			rc = 1;
		}
		else if (bFlag == KEYBOARD_RESET_INVERT_CHECK && keyboard_key_invert_dalay)
		{
			if (time_diff(get_time_ticks(), start_time) > seconds_to_ticks(KEY_BUBBLE_STAY_TIME))
				bFlag = KEYBOARD_RESET_INVERT_NOW; // reset invert immediately
			else
				rc = 1;
		}

		if (bFlag == KEYBOARD_RESET_INVERT_NOW)
		{
			guilib_fb_lock();
			if (pre_key->key == ' ' || pre_key->key == WL_KEY_BACKSPACE || pre_key->key == WL_KEY_NLS || isupper(pre_key->key))
				keyboard_process_key_invert(pre_key);
			else
				restore_key_bubble();
			pre_key = NULL;
			if (kb_mode == KEYBOARD_CHAR || kb_mode == KEYBOARD_NUM)
				search_to_be_reloaded(SEARCH_TO_BE_RELOADED_CHECK, 0);
			guilib_fb_unlock();
		}
	}
	return rc;
}

int keyboard_adjacent_keys(struct keyboard_key *key1, struct keyboard_key *key2)
{
	if (((key1->left_x == key2->left_x || key1->right_x == key2->right_x) && // same column
		(key1->left_y - key2->right_y == 1 || key2->left_y - key1->right_y == 1)) || // adjacent row
		((key1->left_y == key2->left_y || key1->right_y == key2->right_y) && // same row
		(key1->left_x - key2->right_x == 1 || key2->left_x - key1->right_x == 1))) // adjancent column
	{
		return 1;
	}
	else
	{
		return 0;
	}
}
