/* draw.c
 *
 * Copyright (C) 2010 dking <dking024@gmail.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public Licens e as
 * published by the Free Software Foundation; either version 2 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
//v1.1

/******************************************************************************
 * draw.cpp
 * basic program to draw some graphic
 ******************************************************************************/
#include "port.h"
#include <string.h>
#include <stdio.h>
#include "ds2_malloc.h"
#include "ds2_cpu.h"
#include "bdf_font.h"
#include "gui.h"
#include "bitmap.h"
#include "draw.h"

/******************************************************************************
 * macro definition
 ******************************************************************************/
#define progress_sx (screen_width2 - SCREEN_WIDTH / 3)  // Center -160/-80
#define progress_ex (screen_width2 + SCREEN_WIDTH / 3)  // Center +160/+80
#define progress_sy (screen_height2 + 3)                // Center +3
#define progress_ey (screen_height2 + 13)               // Center +13
#define yesno_sx    (screen_width2 - SCREEN_WIDTH / 3)  // Center -160/-80
#define yesno_ex    (screen_width2 + SCREEN_WIDTH / 3)  // Center +160/+80
#define yesno_sy    (screen_height2 + 3)                // Center +3
#define yesno_ey    (screen_height2 + 13)               // Center +13
#define progress_color COLOR16(15,15,15)

//#define progress_wait (0.5 * 1000 * 1000)
#define progress_wait (OS_TICKS_PER_SEC/2)				//0.5S

#define FONTS_HEIGHT    14

#define SCREEN_PITCH	256

#define VRAM_POS(screen, x, y)  ((unsigned short*)screen + (x + (y) * SCREEN_PITCH))

#define BOOTLOGO "SYSTEM/GUI/boot.bmp"
#define GUI_SOURCE_PATH "SYSTEM/GUI"
#define GUI_PIC_BUFSIZE 1024*512

char gui_picture[GUI_PIC_BUFSIZE];

struct gui_iconlist gui_icon_list[]= {
    //file system
    /* 00 */ {"zipfile", 16, 16, NULL},
    /* 01 */ {"directory", 16, 16, NULL},
    /* 02 */ {"sfcfile", 16, 16, NULL},

	//title
	/* 03 */ {"stitle", 256, 33, NULL},
	//main menu
	/* 04 */ {"savo", 52, 52, NULL},
	/* 05 */ {"ssaveo", 52, 52, NULL},
	/* 06 */ {"stoolo", 52, 52, NULL},
	/* 07 */ {"scheato", 52, 52, NULL},
	/* 08 */ {"sother", 52, 52, NULL},
	/* 09 */ {"sexito", 52, 52, NULL},
	/* 10 */ {"smsel", 79, 15, NULL},
	/* 11 */ {"smnsel", 79, 15, NULL},

	/* 12 */ {"snavo", 52, 52, NULL},
	/* 13 */ {"snsaveo", 52, 52, NULL},
	/* 14 */ {"sntoolo", 52, 52, NULL},
	/* 15 */ {"sncheato", 52, 52, NULL},
	/* 16 */ {"snother", 52, 52, NULL},
	/* 17 */ {"snexito", 52, 52, NULL},

	/* 18 */ {"sunnof", 16, 16, NULL},
	/* 19 */ {"smaini", 85, 38, NULL},
	/* 20 */ {"snmaini", 85, 38, NULL},
	/* 21 */ {"smaybgo", 256, 192, NULL},

	/* 22 */ {"sticon", 29, 13, NULL},
	/* 23 */ {"ssubbg", 256, 192, NULL},

	/* 24 */ {"subsela", 245, 22, NULL},
	/* 25 */ {"sfullo", 12, 12, NULL},
	/* 26 */ {"snfullo", 12, 12, NULL},
	/* 27 */ {"semptyo", 12, 12, NULL},
	/* 28 */ {"snemptyo", 12, 12, NULL},
	/* 29 */ {"fdoto", 16, 16, NULL},
	/* 30 */ {"backo", 19, 13, NULL},
	/* 31 */ {"nbacko", 19, 13, NULL},
	/* 32 */ {"chtfile", 16, 15, NULL},
	/* 33 */ {"smsgfr", 193, 111, NULL},
	/* 34 */ {"sbutto", 76, 16, NULL}
                        };


/*
*	Drawing string aroud center
*/
void print_string_center(void* screen_addr, u32 sy, u32 color, u32 bg_color, char *str)
{
	int width = 0;//fbm_getwidth(str);
	u32 sx = (SCREEN_WIDTH - width) / 2;

	PRINT_STRING_BG(screen_addr, str, color, bg_color, sx, sy);
}

/*
*	Drawing string with shadow around center
*/
void print_string_shadow_center(void* screen_addr, u32 sy, u32 color, char *str)
{
	int width = 0;//fbm_getwidth(str);
	u32 sx = (SCREEN_WIDTH - width) / 2;

	PRINT_STRING_SHADOW(screen_addr, str, color, sx, sy);
}

/*
*	Drawing horizontal line
*/
void drawhline(void* screen_addr, u32 sx, u32 ex, u32 y, u32 color)
{
	u32 x;
	u32 width  = (ex - sx) + 1;
	u16 *dst = VRAM_POS(screen_addr, sx, y);

	for (x = 0; x < width; x++)
		*dst++ = (u16)color;
}

/*
*	Drawing vertical line
*/
void drawvline(void* screen_addr, u32 x, u32 sy, u32 ey, u32 color)
{
	int y;
	int height = (ey - sy) + 1;
	u16 *dst = VRAM_POS(screen_addr, x, sy);

	for (y = 0; y < height; y++)
	{
		*dst = (u16)color;
		dst += SCREEN_PITCH;
	}
}

/*
*	Drawing rectangle
*/
void drawbox(void* screen_addr, u32 sx, u32 sy, u32 ex, u32 ey, u32 color)
{
	drawhline(screen_addr, sx, ex - 1, sy, color);
	drawvline(screen_addr, ex, sy, ey - 1, color);
	drawhline(screen_addr, sx + 1, ex, ey, color);
	drawvline(screen_addr, sx, sy + 1, ey, color);
}

/*
*	Filling a rectangle
*/
void drawboxfill(void* screen_addr, u32 sx, u32 sy, u32 ex, u32 ey, u32 color)
{
	u32 x, y;
	u32 width  = (ex - sx) + 1;
	u32 height = (ey - sy) + 1;
	u16 *dst = VRAM_POS(screen_addr, sx, sy);

	for (y = 0; y < height; y++)
	{
		for (x = 0; x < width; x++)
		{
			dst[x + y * SCREEN_PITCH] = (u16)color;
		}
	}
}

/*
*	Drawing a selection item
- active    0 not fill
-           1 fill with gray
-           2 fill with color
-           3 fill with color and most brithness
- color     0 Red
-           1 Green
-           2 Blue
------------------------------------------------------*/
void draw_selitem(void* screen_addr, u32 x, u32 y, u32 color, u32 active)
{
    u32 size;
    u32 color0, color1, color2, color3;

    size= 10;

    switch(active)
    {
        case 1:
            color0 = COLOR16(12, 12, 12);
            color1 = COLOR16(2, 2, 2);
            color2 = COLOR16(7, 7, 7);
            color3 = COLOR16(22, 22, 22);
          break;
        case 2:
            switch(color)
            {
                case 0: //Red
                    color0 = COLOR16(12, 12, 12);
                    color1 = COLOR16(8, 0, 0);
                    color2 = COLOR16(16, 0, 0);
                    color3 = COLOR16(24, 0, 0);
                  break;
                case 1: //Green
                    color0 = COLOR16(12, 12, 12);
                    color1 = COLOR16(0, 8, 0);
                    color2 = COLOR16(0, 16, 0);
                    color3 = COLOR16(0, 24, 0);
                  break;
                case 2: //Blue
                    color0 = COLOR16(12, 12, 12);
                    color1 = COLOR16(0, 0, 8);
                    color2 = COLOR16(0, 0, 16);
                    color3 = COLOR16(0, 0, 24);
                  break;
                default:
                    color0 = COLOR16(12, 12, 12);
                    color1 = COLOR16(0, 8, 0);
                    color2 = COLOR16(0, 16, 0);
                    color3 = COLOR16(0, 24, 0);
                  break;
            }
          break;
        case 3:
            switch(color)
            {
                case 0: //Red
                    color0 = COLOR16(31, 31, 31);
                    color1 = COLOR16(16, 0, 0);
                    color2 = COLOR16(22, 0, 0);
                    color3 = COLOR16(31, 0, 0);
                  break;
                case 1: //Green
                    color0 = COLOR16(31, 31, 31);
                    color1 = COLOR16(0, 16, 0);
                    color2 = COLOR16(0, 22, 0);
                    color3 = COLOR16(0, 31, 0);
                  break;
                case 2: //Blue
                    color0 = COLOR16(31, 31, 31);
                    color1 = COLOR16(0, 0, 16);
                    color2 = COLOR16(0, 0, 22);
                    color3 = COLOR16(0, 0, 31);
                  break;
                default:
                    color0 = COLOR16(31, 31, 31);
                    color1 = COLOR16(0, 16, 0);
                    color2 = COLOR16(0, 22, 0);
                    color3 = COLOR16(0, 31, 0);
                  break;
            }
          break;
        default:
            color0= COLOR16(18, 18, 18);
            color1= color2= color3= COLOR16(18, 18, 18);
          break;
    }

    drawbox(screen_addr, x, y, x+size-1, y+size-1, color0);

    if(active >0)
    {
        drawbox(screen_addr, x+1, y+1, x+size-2, y+size-2, color1);
        drawbox(screen_addr, x+2, y+2, x+size-3, y+size-3, color2);
        drawboxfill(screen_addr, x+3, y+3, x+size-4, y+size-4, color3);
    }
}

/*
*	Drawing message box
*	Note if color_fg is transparent, screen_bg can't be transparent
*/
void draw_message(void* screen_addr, u16 *screen_bg, u32 sx, u32 sy, u32 ex, u32 ey,
        u32 color_fg)
{
    if(!(color_fg & 0x8000))
    {
//        drawbox(screen_addr, sx, sy, ex, ey, COLOR16(12, 12, 12));
//        drawboxfill(screen_addr, sx+1, sy+1, ex-1, ey-1, color_fg);
		show_icon(screen_addr, &ICON_MSG, (NDS_SCREEN_WIDTH - ICON_MSG.x) / 2, (NDS_SCREEN_HEIGHT - ICON_MSG.y) / 2);
    }
    else
    {
        u16 *screenp, *screenp1;
        u32 width, height, i, k;
        u32 tmp, tmp1, tmp2;
        u32 r, g, b;

        width= ex-sx;
        height= ey-sy;
        r= ((color_fg >> 10) & 0x1F) * 6/7;
        g= ((color_fg >> 5) & 0x1F) * 6/7;
        b= (color_fg & 0x1F) * 6/7;
        for(k= 0; k < height; k++)
        {
            screenp = VRAM_POS(screen_addr, sx, sy+k);
            screenp1 = screen_bg + sx + (sy + k) * SCREEN_PITCH;
            for(i= 0; i < width; i++)
            {
                tmp = *screenp1++;
                tmp1 = ((tmp >> 10) & 0x1F) *1/7 + r;
                tmp2 = (tmp1 > 31) ? 31 : tmp1;
                tmp1 = ((tmp >> 5) & 0x1F) *1/7 + g;
                tmp2 = (tmp2 << 5) | ((tmp1 > 31) ? 31 : tmp1);
                tmp1 = (tmp & 0x1F) *1/7 + b;
                tmp2 = (tmp2 << 5) | ((tmp1 > 31) ? 31 : tmp1);
                *screenp++ = tmp2;
            }
        }
    }
}

/*
*	Drawing string horizontal center aligned
*/
void draw_string_vcenter(void* screen_addr, u32 sx, u32 sy, u32 width, u32 color_fg, char *string)
{
    u32 x, num, i, m;
    u16 *screenp;
    u16 unicode[256];

    num= 0;
    while(*string)
    {
        string= utf8decode(string, unicode+num);
        num++;
    }

    if(num== 0) return;

    screenp = (unsigned short*)screen_addr + sx + sy*SCREEN_WIDTH;
    i= 0;
    while(i < num)
    {
        m= BDF_cut_unicode(&unicode[i], num-i, width, 1);
        x= (width - BDF_cut_unicode(&unicode[i], m, 0, 3)) / 2;
        while(m--)
        {
            x += BDF_render16_ucs(screenp+x, SCREEN_WIDTH, 0, COLOR_TRANS, 
                color_fg, unicode[i++]);
        }
        if (i < num && (unicode[i] == 0x0D || unicode[i] == 0x0A))
            i++;
	else {
            while (i < num && (unicode[i] == ' ')) i++;
        }
        screenp += FONTS_HEIGHT * SCREEN_WIDTH;
    }
}

/*------------------------------------------------------
	Drawing a scroll string
------------------------------------------------------*/
//limited
// < 256 Unicodes
// width < 256+128
//#define MAX_SCROLL_STRING   8

/*------------------------------------------------------
- scroll_val    < 0     scroll toward left
-               > 0     scroll toward right
------------------------------------------------------*/
struct scroll_string_info{
    u16     *screenp;
    u32     sx;
    u32     sy;
    u32     width;
    u32     height;
    u16     *unicode;
    u32     color_bg;
    u32     color_fg;
    u16     *buff_fonts;
    u32     buff_width;
    u16     *buff_bg;
    s32     pos_pixel;
    u32     str_start;
    u32     str_end;
    u32     str_len;
};

static struct scroll_string_info    scroll_strinfo[MAX_SCROLL_STRING];
static u32  scroll_string_num= 0;

/*
 * Initialises a text scroller to display a certain string.
 * Input assertions: sx + width < NDS_SCREEN_WIDTH &&
 *   sy + [text height] < NDS_SCREEN_HEIGHT && string != NULL &&
 *   screen_addr != NULL.
 * Input: 'screen_addr', the address of the upper-left corner of the screen.
 *        'sx' and 'sy', the X and Y coordinates of the upper-left corner of
 *          the text.
 *        'width', the width of the scroller's viewport.
 *        'color_bg', the RGB15 color of the background around the text, or
 *          COLOR_TRANS for transparency.
 *        'color_fg', the RGB15 color of the text.
 *        'string', the text to be scrolled, encoded as UTF-8.
 * Output: the scroller's handle, to be used to scroll the text in
 *   draw_hscroll.
 */
u32 hscroll_init(void* screen_addr, u32 sx, u32 sy, u32 width, 
        u32 color_bg, u32 color_fg, char *string)
{
    u32 index, x, textWidth, num, len, i;
    u16 *unicode, *screenp;

    // 1. Which scroller should we use for this request?
    for(i= 0; i < MAX_SCROLL_STRING; i++)
    {
        if(scroll_strinfo[i].screenp == NULL)
            break;
    }

    if(i >= MAX_SCROLL_STRING)
        return -1;

    index= i;

    // 2. Convert to Unicode while calculating the width of the text.
    unicode= (u16*)malloc(strlen(string)*sizeof(u16));
    if(unicode == NULL)
    {
        scroll_strinfo[index].str_len = 0;
        return -3;
    }

    num= 0;
    textWidth = 0;
    while(*string)
    {
        string= utf8decode(string, unicode+num);
        if(unicode[num] != 0x0D && unicode[num] != 0x0A) {
            textWidth += BDF_width16_ucs(unicode[num]);
            num++;
        }
    }
    if (textWidth < width)
        textWidth = width;

    // 3. Allocate a rectangle of pixels for drawing the entire text into.
    screenp= (u16*)malloc(textWidth*FONTS_HEIGHT*sizeof(u16));
    if(screenp == NULL)
    {
        scroll_strinfo[index].str_len = 0;
        free((void*)unicode);
        return -2;
    }

    if(color_bg == COLOR_TRANS)
        memset(screenp, 0, textWidth*FONTS_HEIGHT*sizeof(u16));

    scroll_string_num += 1;
    scroll_strinfo[index].screenp = (unsigned short*)screen_addr;
    scroll_strinfo[index].sx= sx;
    scroll_strinfo[index].sy= sy;
    scroll_strinfo[index].color_bg= color_bg;
    scroll_strinfo[index].color_fg= color_fg;
    scroll_strinfo[index].width= width;
    scroll_strinfo[index].height= FONTS_HEIGHT;
    scroll_strinfo[index].unicode= unicode;
    scroll_strinfo[index].buff_fonts= screenp;
    scroll_strinfo[index].buff_bg= 0;
    scroll_strinfo[index].buff_width= textWidth;
    scroll_strinfo[index].pos_pixel= 0;
    scroll_strinfo[index].str_start= 0;
    scroll_strinfo[index].str_end= len-1;

    scroll_strinfo[index].str_len= num;
    if(num == 0)
        return index; // (1. Which scroller?)

    // 4. Render text into the allocation.
    i= 0;
    x= 0;
    while(i < num)
    {
        x += BDF_render16_ucs(screenp + x, textWidth, 0, color_bg, color_fg, unicode[i++]);
    }

    return index; // (1. Which scroller?)
}

u32 draw_hscroll_init(void* screen_addr, u32 sx, u32 sy, u32 width, 
        u32 color_bg, u32 color_fg, char *string)
{
	u32 ret = hscroll_init(screen_addr, sx, sy, width, color_bg, color_fg, string);

	draw_hscroll(ret, 0 /* stay on the left */);

	return ret;
}

/*
 * Scrolls an initialised scroller's text.
 * A scroller is never allowed to go past the beginning of the text when
 * scrolling to the left, or to go past the end when scrolling to the right.
 * Input assertions: index was returned by a previous call to
 *   draw_hscroll_init and not used in a call to draw_hscroll_over.
 * Input: 'index', the scroller's handle.
 *        'scroll_val', the number of pixels to scroll. The sign affects the
 *          direction. If scroll_val > 0, the scroller's viewport is moved to
 *          the left; if < 0, the scroller's viewport is moved to the right.
 * Output: the number of pixels still available to scroll in the direction
 *   specified by the sign of 'scroll_val'.
 *
 * Example: (assume each letter is 1 pixel; this won't be true in reality)
 *           [some lengthy text shown in ]         |
 * val -5 -> |    [lengthy text shown in a scr]xxxxx -> to right, returns 5
 * val -5 -> |         [hy text shown in a scroller] -> to right, returns 0
 * val  3 -> xxxxxxx[ngthy text shown in a scrol]  | -> to left,  returns 7
 * val  3 -> xxxx[ lengthy text shown in a sc]     | -> to left,  returns 4
 */
u32 draw_hscroll(u32 index, s32 scroll_val)
{
    u32 color_bg, color_fg, i, width, height;
    s32 xoff;

    if(index >= MAX_SCROLL_STRING) return -1;
    if(scroll_strinfo[index].screenp == NULL) return -2;
    if(scroll_strinfo[index].str_len == 0) return 0;
    
    width= scroll_strinfo[index].width;
    height= scroll_strinfo[index].height;
    color_bg= scroll_strinfo[index].color_bg;
    color_fg= scroll_strinfo[index].color_fg;

    // 1. Shift the scroller.
    scroll_strinfo[index].pos_pixel -= scroll_val;
    if (scroll_strinfo[index].pos_pixel < 0) // Reached the beginning
        scroll_strinfo[index].pos_pixel = 0;
    else if (scroll_strinfo[index].pos_pixel > scroll_strinfo[index].buff_width - width) // Reached the end
        scroll_strinfo[index].pos_pixel = scroll_strinfo[index].buff_width - width;

    // 2. Draw the scroller's text at its new position.
    u32 x, sx, sy, pixel;
    u16 *screenp, *screenp1;

    sx= scroll_strinfo[index].sx;
    sy= scroll_strinfo[index].sy;

    if(color_bg == COLOR_TRANS)
    {
        for(i= 0; i < height; i++)
        {
            screenp= scroll_strinfo[index].screenp + sx + (sy + i) * SCREEN_WIDTH;
            screenp1= scroll_strinfo[index].buff_fonts + scroll_strinfo[index].pos_pixel + i*scroll_strinfo[index].buff_width;
            for(x= 0; x < width; x++)
            {
                pixel= *screenp1++;
				if(pixel) *screenp = pixel;
				screenp ++;
            }
        }
    }
    else
    {
        for(i= 0; i < height; i++)
        {
            screenp= scroll_strinfo[index].screenp + sx + (sy + i) * SCREEN_WIDTH;
            screenp1= scroll_strinfo[index].buff_fonts + scroll_strinfo[index].pos_pixel + i*scroll_strinfo[index].buff_width;
            for(x= 0; x < width; x++)
                *screenp++ = *screenp1++;
        }
    }

    // 3. Return how many more pixels we can scroll in the same direction.
    if(scroll_val > 0)
        // Scrolling to the left: Return the number of pixels we can still go
        // to the left.
        return scroll_strinfo[index].pos_pixel;
    else
        // Scrolling to the right: Return the number of pixels we can still go
        // to the right.
        return scroll_strinfo[index].buff_width - scroll_strinfo[index].pos_pixel - width;
}

void draw_hscroll_over(u32 index)
{
    if(scroll_strinfo[index].screenp== NULL)
        return;

    if(index < MAX_SCROLL_STRING && scroll_string_num > 0)
    {
        if(scroll_strinfo[index].unicode)
        {
            free((void*)scroll_strinfo[index].unicode);
            scroll_strinfo[index].unicode= NULL;
        }
        if(scroll_strinfo[index].buff_fonts)
        {
            free((void*)scroll_strinfo[index].buff_fonts);
            scroll_strinfo[index].buff_fonts= NULL;
        }
        scroll_strinfo[index].screenp= NULL;
        scroll_strinfo[index].str_len= 0;
    
        scroll_string_num -=1;
    }
}

/*
*	Drawing dialog
*/
void draw_dialog(void* screen_addr, u32 sx, u32 sy, u32 ex, u32 ey)
{
	drawboxfill(screen_addr, sx + 5, sy + 5, ex + 5, ey + 5, COLOR_DIALOG_SHADOW);

	drawhline(screen_addr, sx, ex - 1, sy, COLOR_FRAME);
	drawvline(screen_addr, ex, sy, ey - 1, COLOR_FRAME);
	drawhline(screen_addr, sx + 1, ex, ey, COLOR_FRAME);
	drawvline(screen_addr, sx, sy + 1, ey, COLOR_FRAME);

	sx++;
	ex--;
	sy++;
	ey--;

	drawhline(screen_addr, sx, ex - 1, sy, COLOR_FRAME);
	drawvline(screen_addr, ex, sy, ey - 1, COLOR_FRAME);
	drawhline(screen_addr, sx + 1, ex, ey, COLOR_FRAME);
	drawvline(screen_addr, sx, sy + 1, ey, COLOR_FRAME);

	sx++;
	ex--;
	sy++;
	ey--;

	drawboxfill(screen_addr, sx, sy, ex, ey, COLOR_DIALOG);
}

/*
*	Draw yes or no dialog
*/
u32 draw_yesno_dialog(enum SCREEN_ID screen, u32 sy, char *yes, char *no)
{
    u16 unicode[8];
    u32 len, width, box_width, i;
    char *string;
	void* screen_addr;

    len= 0;
    string= yes;
    while(*string)
    {
        string= utf8decode(string, &unicode[len]);
        if(unicode[len] != 0x0D && unicode[len] != 0x0A)
        {
            if(len < 8) len++;
            else break;
        }
    }
    width= BDF_cut_unicode(unicode, len, 0, 3);
    
    len= 0;
    string= no;
    while(*string)
    {
        string= utf8decode(string, &unicode[len]);
        if(unicode[len] != 0x0D && unicode[len] != 0x0A)
        {
            if(len < 8) len++;
            else    break;
        }
    }
    i= BDF_cut_unicode(unicode, len, 0, 3);

    if(width < i)   width= i;
    box_width= 64;
    if(box_width < (width +6)) box_width = width +6;

	if(screen & UP_MASK)
		screen_addr = up_screen_addr;
	else
		screen_addr = down_screen_addr;

	sy = (NDS_SCREEN_HEIGHT + ICON_MSG.y) / 2 - 8 - ICON_BUTTON.y;

	u32 left_sx = NDS_SCREEN_WIDTH / 2 - 8 - ICON_BUTTON.x,
	    right_sx = NDS_SCREEN_WIDTH / 2 + 8;

	show_icon((unsigned short*)screen_addr, &ICON_BUTTON, left_sx, sy);
    draw_string_vcenter((unsigned short*)screen_addr, left_sx + 2, sy, ICON_BUTTON.x - 4, COLOR_WHITE, yes);

	show_icon((unsigned short*)screen_addr, &ICON_BUTTON, right_sx, sy);
    draw_string_vcenter((unsigned short*)screen_addr, right_sx + 2, sy, ICON_BUTTON.x - 4, COLOR_WHITE, no);

	ds2_flipScreen(screen, 2);

    gui_action_type gui_action = CURSOR_NONE;
    while((gui_action != CURSOR_SELECT)  && (gui_action != CURSOR_BACK))
    {
        gui_action = get_gui_input();
	if (gui_action == CURSOR_TOUCH)
	{
		struct key_buf inputdata;
		ds2_getrawInput(&inputdata);
		// Turn it into a SELECT (A) or BACK (B) if the button is touched.
		if (inputdata.y >= sy && inputdata.y < sy + ICON_BUTTON.y)
		{
			if (inputdata.x >= left_sx && inputdata.x < left_sx + ICON_BUTTON.x)
				gui_action = CURSOR_SELECT;
			else if (inputdata.x >= right_sx && inputdata.x < right_sx + ICON_BUTTON.x)
				gui_action = CURSOR_BACK;
		}
	}
	mdelay(16);
    }

    if (gui_action == CURSOR_SELECT)
        return 1;
    else
        return 0;
}

/*
*	Draw hotkey dialog
*	Returns DS keys pressed, as in ds2io.h.
*/
u32 draw_hotkey_dialog(enum SCREEN_ID screen, u32 sy, char *clear, char *cancel)
{
    u16 unicode[8];
    u32 len, width, box_width, i;
    char *string;
	void* screen_addr;

    len= 0;
    string= clear;
    while(*string)
    {
        string= utf8decode(string, &unicode[len]);
        if(unicode[len] != 0x0D && unicode[len] != 0x0A)
        {
            if(len < 8) len++;
            else break;
        }
    }
    width= BDF_cut_unicode(unicode, len, 0, 3);
    
    len= 0;
    string= cancel;
    while(*string)
    {
        string= utf8decode(string, &unicode[len]);
        if(unicode[len] != 0x0D && unicode[len] != 0x0A)
        {
            if(len < 8) len++;
            else    break;
        }
    }
    i= BDF_cut_unicode(unicode, len, 0, 3);

    if(width < i)   width= i;
    box_width= 64;
    if(box_width < (width +6)) box_width = width +6;

	if(screen & UP_MASK)
		screen_addr = up_screen_addr;
	else
		screen_addr = down_screen_addr;

    i= SCREEN_WIDTH/2 - box_width - 2;
	show_icon((unsigned short*)screen_addr, &ICON_BUTTON, 49, 128);
    draw_string_vcenter((unsigned short*)screen_addr, 51, 130, 73, COLOR_WHITE, clear);

    i= SCREEN_WIDTH/2 + 3;
	show_icon((unsigned short*)screen_addr, &ICON_BUTTON, 136, 128);
    draw_string_vcenter((unsigned short*)screen_addr, 138, 130, 73, COLOR_WHITE, cancel);

	ds2_flipScreen(screen, 2);

	// This function has been started by a key press. Wait for it to end.
	struct key_buf inputdata;
	do {
		mdelay(1);
		ds2_getrawInput(&inputdata);
	} while (inputdata.key != 0);

	// While there are no keys pressed, wait for keys.
	do {
		mdelay(1);
		ds2_getrawInput(&inputdata);
	} while (inputdata.key == 0);

	// Now, while there are keys pressed, keep a tally of keys that have
	// been pressed. (IGNORE TOUCH AND LID! Otherwise, closing the lid or
	// touching to get to the menu will do stuff the user doesn't expect.)
	u32 TotalKeys = 0;

	do {
		TotalKeys |= inputdata.key & ~(KEY_TOUCH | KEY_LID);
		// If there's a touch on either button, turn it into a
		// clear (A) or cancel (B) request.
		if (inputdata.key & KEY_TOUCH)
		{
			if (inputdata.y >= 128 && inputdata.y < 128 + ICON_BUTTON.y)
			{
				if (inputdata.x >= 49 && inputdata.x < 49 + ICON_BUTTON.x)
					return KEY_A;
				else if (inputdata.x >= 136 && inputdata.x < 136 + ICON_BUTTON.x)
					return KEY_B;
			}
		}
		mdelay(1);
		ds2_getrawInput(&inputdata);
	} while (inputdata.key != 0 || TotalKeys == 0);

	return TotalKeys;
}

/*
*	Drawing scroll bar
*/
#define SCROLLBAR_COLOR1 COLOR16( 0, 2, 8)
#define SCROLLBAR_COLOR2 COLOR16(15,15,15)

void scrollbar(void* screen_addr, u32 sx, u32 sy, u32 ex, u32 ey, u32 all, u32 view, u32 now)
{
	u32 scrollbar_sy;
	u32 scrollbar_ey;
	u32 len;

	len = ey - sy - 2;

	if ((all != 0) && (all > now))
		scrollbar_sy = (u32)((float)len * (float)now / (float)all) +sy + 1;
	else
		scrollbar_sy = sy + 1;

	if ((all > (now + view)) && (all != 0))
		scrollbar_ey = (u32)((float)len * (float)(now + view) / (float)all ) + sy + 1;
	else
		scrollbar_ey = len + sy + 1;

	drawbox(screen_addr, sx, sy, ex, ey, COLOR_BLACK);
	drawboxfill(screen_addr, sx + 1, sy + 1, ex - 1, ey - 1, SCROLLBAR_COLOR1);
	drawboxfill(screen_addr, sx + 1, scrollbar_sy, ex - 1, scrollbar_ey, SCROLLBAR_COLOR2);
}

#if 0
static struct background back_ground = {{0}, {0}};

int show_background(void *screen, char *bgname)
{
    int ret;

    if(strcasecmp(bgname, back_ground.bgname))
    {
        char *buff, *src;
        int x, y;        
        unsigned short *dst;
		unsigned int type;

        buff= (char*)malloc(256*192*4);

        ret= BMP_read(bgname, buff, 256, 192, &type);
        if(ret != BMP_OK)
        {
            free((int)buff);
            return(-1);
        }

        src = buff;

		if(type ==2)		//2 bytes per pixel
		{
			unsigned short *pt;
			pt = (unsigned short*)buff;
//			memcpy((char*)back_ground.bgbuffer, buff, 256*192*2);
			dst=(unsigned short*)back_ground.bgbuffer;
	        for(y= 0; y< 192; y++)
	        {
    	        for(x= 0; x< 256; x++)
    	        {
    	            *dst++= RGB16_15(pt);
    	            pt += 1;
    	        }
    	    }
		}
		else if(type ==3)	//3 bytes per pixel
		{
			dst=(unsigned short*)back_ground.bgbuffer;
	        for(y= 0; y< 192; y++)
	        {
    	        for(x= 0; x< 256; x++)
    	        {
    	            *dst++= RGB24_15(buff);
    	            buff += 3;
    	        }
    	    }
		}
		else
		{
            free((int)buff);
            return(-1);
		}

        free((int)src);
        strcpy(back_ground.bgname, bgname);
    }

    memcpy((char*)screen, back_ground.bgbuffer, 256*192*2);

    return 0;    
}
#endif

/*
*	change GUI icon
*/
int gui_change_icon(u32 language_id)
{
    char path[128];
    char fpath[8];
    u32  i, item;
    int err, ret; 
    char *buff, *src;
    u32 x, y;
    char *icondst;
	unsigned int type;

    item= sizeof(gui_icon_list)/16;
    buff= (char*)malloc(256*192*4);
    if(buff == NULL)
        return -1;

    ret= 0;
    icondst= gui_picture;

    sprintf(fpath, "%d.bmp", language_id);
    for(i= 0; i< item; i++)
    {
        sprintf(path, "%s/%s/%s%s", main_path, GUI_SOURCE_PATH, gui_icon_list[i].iconname, fpath);

	    src= buff; 
        err= BMP_read(path, src, gui_icon_list[i].x, gui_icon_list[i].y, &type);
        if(err != BMP_OK)
        {
            sprintf(path, "%s/%s/%s%s", main_path, GUI_SOURCE_PATH, gui_icon_list[i].iconname, ".bmp");
            err= BMP_read(path, src, gui_icon_list[i].x, gui_icon_list[i].y, &type);
        }

		if(type < 2)	//< 1 byte per pixels, not surpport now
		{
            if(!ret) ret = -(i+1);
            gui_icon_list[i].iconbuff= NULL;
			continue;
		}

        if(err == BMP_OK)
        {
            unsigned short *dst;

            if(icondst >= gui_picture + GUI_PIC_BUFSIZE -1)
            {
                ret = 1;
                break;
            }

			if(type == 2)
			{
				unsigned short *pt;
				pt = (unsigned short*)src;
//				memcpy((char*)icondst, src, 256*192*2);
				dst = (unsigned short*)icondst;
    	        for(y= 0; y< gui_icon_list[i].y; y++)
    	        {
    	            for(x= 0; x < gui_icon_list[i].x; x++)
    	            {
    	                *dst++ = RGB16_15(pt);
    	                pt += 1;
    	            }
	            }
			}

			if(type == 3)
			{
				dst = (unsigned short*)icondst;
    	        for(y= 0; y< gui_icon_list[i].y; y++)
    	        {
    	            for(x= 0; x < gui_icon_list[i].x; x++)
    	            {
    	                *dst++ = RGB24_15(src);
    	                src += 3;
    	            }
	            }
            }

            gui_icon_list[i].iconbuff= icondst;
            icondst += gui_icon_list[i].x*gui_icon_list[i].y*2;
        }
        else
        {
            if(!ret) ret = -(i+1);
            gui_icon_list[i].iconbuff= NULL;
        }
    }

    free((void*)buff);
//printf("icon_buf: %08x\n", icondst - gui_picture );
    return ret;
}

/*************************************************************/
int icon_init(u32 language_id)
{
    u32  i;
    int ret;

//Initial draw_scroll_string function
    scroll_string_num = 0;
    for(i= 0; i < MAX_SCROLL_STRING; i++)
    {
        scroll_strinfo[i].unicode= NULL;
        scroll_strinfo[i].buff_fonts= NULL;
        scroll_strinfo[i].screenp = NULL;
        scroll_strinfo[i].str_len = 0;
    }

    ret= gui_change_icon(language_id);

//#define GUI_INIT_DEBUG
#if 0
    item= sizeof(gui_icon_list)/12;
    buff= (char*)malloc(256*192*4);
    src= buff;
    ret= 0;
    icondst= gui_picture;

    for(i= 0; i< item; i++)
    {
        sprintf(path, "%s\\%s", GUI_SOURCE_PATH, gui_icon_list[i].iconname);
        
        err= BMP_read(path, buff, gui_icon_list[i].x, gui_icon_list[i].y);
        if(err == BMP_OK)
        {
            unsigned short *dst;
            
            if(icondst >= gui_picture + GUI_PIC_BUFSIZE -1)
            {
                ret = 1;
#ifdef GUI_INIT_DEBUG
                printf("GUI Initial overflow\n");
#endif
                break;
            }

            for(y= 0; y< gui_icon_list[i].y; y++)
            {
                dst= (unsigned short*)(icondst + (gui_icon_list[i].y - y -1)*gui_icon_list[i].x*2);
                for(x= 0; x < gui_icon_list[i].x; x++)
                {
                    *dst++ = RGB24_15(buff);
                    buff += 4;
                }
            }                
            
            gui_icon_list[i].iconname= icondst;
            icondst += gui_icon_list[i].x*gui_icon_list[i].y*2;
        }
        else
        if(!ret)
        {
            ret = -(i+1);
            gui_icon_list[i].iconname= NULL;
#ifdef GUI_INIT_DEBUG
            printf("GUI Initial: %s not open\n", path);
#endif
        }
    }

#ifdef GUI_INIT_DEBUG
    printf("GUI buff %d\n", icondst - gui_picture);
#endif

    free((int)src);
#endif

    return ret;
}

/*************************************************************/
void show_icon(void* screen, struct gui_iconlist* icon, u32 x, u32 y)
{
    u32 i, k;
    unsigned short *src, *dst;

    src= (unsigned short*)icon->iconbuff;
    dst = (unsigned short*)screen + y*NDS_SCREEN_WIDTH + x;
	if(NULL == src) return;	//The icon may initialized failure

	if (icon->x == NDS_SCREEN_WIDTH && icon->y == NDS_SCREEN_HEIGHT && x == 0 && y == 0)
	{
		// Don't support transparency for a background.
		memcpy(dst, src, NDS_SCREEN_WIDTH * NDS_SCREEN_HEIGHT * sizeof(u16));
	}
	else
	{
		for(i= 0; i < icon->y; i++)
		{
			for(k= 0; k < icon->x; k++)
			{
				if(0x03E0 != *src) dst[k]= *src;
				src++;
			}

			dst += NDS_SCREEN_WIDTH;
		}
	}
}

/*************************************************************/
void show_Vscrollbar(char *screen, u32 x, u32 y, u32 part, u32 total)
{
//    show_icon((u16*)screen, ICON_VSCROL_UPAROW, x+235, y+55);
//    show_icon((u16*)screen, ICON_VSCROL_DWAROW, x+235, y+167);
//    show_icon((u16*)screen, ICON_VSCROL_SLIDER, x+239, y+64);
//    if(total <= 1)
//        show_icon((u16*)screen, ICON_VSCROL_BAR, x+236, y+64);
//    else
//        show_icon((u16*)screen, ICON_VSCROL_BAR, x+236, y+64+(part*90)/(total-1));
}

/*
*	display a log
*/
void show_log(void* screen_addr)
{
    char tmp_path[MAX_PATH];
	char *buff;
	int x, y;        
	unsigned short *dst;
	unsigned int type;
	int ret;

    sprintf(tmp_path, "%s/%s", main_path, BOOTLOGO);
	buff= (char*)malloc(256*192*4);

	ret= BMP_read(tmp_path, buff, 256, 192, &type);
	if(ret != BMP_OK)
	{
		free((void*)buff);
		return;
	}

	if(type ==2)		//2 bytes per pixel
	{
		unsigned short *pt;
		pt = (unsigned short*)buff;
		dst=(unsigned short*)screen_addr;
		for(y= 0; y< 192; y++)
		{
			for(x= 0; x< 256; x++)
			{
				*dst++= RGB16_15(pt);
				pt += 1;
			}
		}
	}
	else if(type ==3)	//3 bytes per pixel
	{
		unsigned char *pt;
		pt = (unsigned char*)buff;
		dst=(unsigned short*)screen_addr;
		for(y= 0; y< 192; y++)
		{
			for(x= 0; x< 256; x++)
			{
				*dst++= RGB24_15(pt);
				pt += 3;
			}
		}
	}

	free((void*)buff);
}

/*************************************************************/
void err_msg(enum SCREEN_ID screen, char *msg)
{
	// A wild console appeared!
	ConsoleInit(RGB15(31, 31, 31), RGB15(0, 0, 0), UP_SCREEN, 2);
	printf(msg);
}

/*
*	Copy screen
*/
void copy_screen(void* to, void *from, u32 x, u32 y, u32 w, u32 h)
{
	u32 yy;
	unsigned short *src, *dst;

	//not check argument
	src = (unsigned short*)from;
	dst = (unsigned short*)to;

	src += y*256+x;
	dst += y*256+x;
    for(yy= 0; yy < h; yy++)
    {
		memcpy((void*)dst, (void*)src, w*2);
		src += 256;
		dst += 256;
    }
}

/*
*
*/
void blit_to_screen(void* screen_addr, u16 *src, u32 w, u32 h, u32 dest_x, u32 dest_y)
{
    u32 x, y;
    u16 *dst;
    u16 *screenp;

    if(w > NDS_SCREEN_WIDTH) w= NDS_SCREEN_WIDTH;
    if(h > NDS_SCREEN_HEIGHT) h= NDS_SCREEN_HEIGHT;
    if(dest_x == -1)    //align center
        dest_x= (NDS_SCREEN_WIDTH - w)/2;
    if(dest_y == -1)
        dest_y= (NDS_SCREEN_HEIGHT - h)/2;

    screenp= (unsigned short*)screen_addr -16*256 -8;
    for(y= 0; y < h; y++)
    {
        dst= screenp + (y+dest_y)*256 + dest_x;
        for(x= 0; x < w; x++)
            *dst++ = *src++;
    }
}