#include "ELF.h"
#include <dbg.h>

/* Здесь мы будем хранить выданную нам "базу" ивентов */

static UINT32  			sel_item;          // номер текущего пункта
static UINT32			entries_num;		// кол-во пунктов меню
static UINT32 			status , code_type;
static LINE 		    *item;
static WCHAR  			ini_file[256];
static UIX_ICON_T		icon_null;
ldrElf       	 		elf;	// Структура эльфа
UIS_DIALOG_T            dialog; // Диалог

/* Название приложения. Длина строки именно такая и никакая иначе */
const char app_name[APP_NAME_LEN] = "IniTool"; 

/* Обработчики событий для каждого state-а */

/* Обработчики событий для HW_STATE_ANY (используется в любом state) */
static EVENT_HANDLER_ENTRY_T any_state_handlers[] =
{  
    /* GUI-приложения должы ловить этот ивент */
    { EV_REVOKE_TOKEN,     APP_HandleUITokenRevoked },
    { STATE_HANDLERS_END,        			   NULL },
};

/* Таблица соответствий обработчиков, состояний и функций входа-выхода из состояния.
   Порядок состояний такой же, как в enum-e */
static const STATE_HANDLERS_ENTRY_T state_handling_table[] =
{
    { HW_STATE_ANY,               // State
      NULL,                       // Обработчик входа в state
      NULL,                       // Обработчик выхода из state
      any_state_handlers          // Список обработчиков событий
    },
    
    { HW_STATE_MAIN,
      MainStateEnter,
      StateExit,
      NULL
    },
	
	{ HW_STATE_EDIT,
      EditStateEnter,
      StateExit,
      NULL
    },
};



/* Это entry для всех эльфов, должна быть объявлена именно так */
/* uri - путь к эльфу (аналогично argv[0]) */
/* params - параметры эльфа (аналогично остальным argv) */
ldrElf * _start( WCHAR * uri, WCHAR * params )
{
    UINT32 			i, reserve = ldrRequestEventBase();
	
	elf.evbase = reserve++;
	
	// инитим таблицы ивентов
	for ( i = 0; i < sizeof(state_handling_table)/sizeof(STATE_HANDLERS_ENTRY_T); i++ )
		reserve = ldrInitEventHandlersTbl( (EVENT_HANDLER_ENTRY_T *)state_handling_table[i].htable, reserve );
		
	/* Регистрируем приложение */
    status = APP_Register( &elf.evbase,            // На какое событие должно запускаться приложение
                           1,                       // Кол-во событий, на которое должно запускаться приложение
                           state_handling_table,    // Таблица состояний
                           HW_STATE_MAX,            // Количество состояний
                           (void*)startApp );   // Функция, вызываемая при запуске
						   
	param2uri(params, ini_file);
	
    ReadMenu( ini_file );
    
	ldrSendEvent( elf.evbase );	// Запустить немедленно
    
    return &elf; 
}

void APP_MyHandleEvent( EVENT_STACK_T *ev_st, APPLICATION_T *app, APP_ID_T app_id, REG_ID_T reg_id )
{
	UIX_HandleEvent( ev_st, app );
	APP_HandleEvent( ev_st, app, app_id, reg_id );
}

/* Функция вызываемая при старте приложения */
UINT32 startApp( EVENT_STACK_T *ev_st,  REG_ID_T reg_id,  UINT32 param2 )//void *reg_hdl )
{
    APP_HELLOWORLD_T     *app = NULL;
    /* Проверяем, а вдруг уже запущено? */
    if( AFW_InquireRoutingStackByRegId( reg_id ) == RESULT_OK )
    return RESULT_FAIL; // Нам не нужен второй экземпляр
	
    /* Инициализация для фоновых приложений */
    app = (APP_HELLOWORLD_T*)APP_InitAppData( (void *)APP_MyHandleEvent, // Обработчик для приложений с GUI
                                              sizeof(APP_HELLOWORLD_T), // Размер структуры приложения
                                              reg_id,
                                              0, 1,
                                              1,
                                              1, 1, 0 );

    status = APP_Start( ev_st,
                        &app->apt,
                        HW_STATE_MAIN, // Начальное состояние
                        state_handling_table,
                        destroyApp,
                        app_name,
                        0 );
						
				
	return RESULT_OK;
}


/* Функция выхода из приложения */
UINT32 destroyApp( EVENT_STACK_T *ev_st,  APPLICATION_T *app )
{
	APPLICATION_T           *papp = (APPLICATION_T*) app;
	
	if (item != NULL) mfree(item);

    /* Завершаем работу приложения */
    status = APP_Exit( ev_st, app, 0 );

    /* Выгружаем эльф */
   ldrUnloadElf();

    return status;
}


/* Обработчик входа в main state */
UINT32 MainStateEnter( EVENT_STACK_T *ev_st,  APPLICATION_T *app,  ENTER_STATE_TYPE_T type )
{
    APPLICATION_T           *papp = (APPLICATION_T*) app;
	UIX_CALL_BACK_F 		*CallBackArr[2];
	
	if(type!=ENTER_STATE_ENTER) return RESULT_OK;
	
	CallBackArr[0] = (void *)HandleSelect;
	CallBackArr[1] = (void *)HandleExit;
	CallBackArr[2] = (void *)HandleNavigate;

	app->dialog = UIX_CreateListDialog(app, entries_num, MainEntryReq, CallBackArr);
	
	UIX_DialogAddAction(app->dialog, "Add new line", ( void *)HandleAddLine, 0 );
	UIX_DialogAddAction(app->dialog, "Delete line", ( void *)HandleDeleteLine, 0 );
	UIX_DialogAddAction(app->dialog, "About...", ( void *)HandleAbout, 0 );
	UIX_DialogAddAction(app->dialog, "Exit", ( void *)HandleExit, 0 );

	
	UIX_DialogSetTitle(app->dialog, app_name, icon_null);
	UIX_Render(app->dialog);
	
    return RESULT_OK;
}

UINT32 EditStateEnter( EVENT_STACK_T *ev_st,  APPLICATION_T *app,  ENTER_STATE_TYPE_T type )
{
    APPLICATION_T           *papp = (APPLICATION_T*) app;
											
	app->dialog = UIX_CreateInputDialog(app, 256, item[sel_item].str, INP_USE_DIGITAL | INP_USE_ENGLISH | INP_USE_OTHERS, ( void *)EditDone );
		
	UIX_DialogSetTitle(app->dialog, "Edit", icon_null);
	UIX_Render(app->dialog);
	
    return RESULT_OK;
}

UINT32 StateExit( EVENT_STACK_T *ev_st,  APPLICATION_T *app,  EXIT_STATE_TYPE_T type )
{
	status = APP_UtilUIXDialogDelete( &app->dialog );

	return status;
}


UINT32 HandleNavigate( EVENT_STACK_T * ev_st, APPLICATION_T * app, UINT32 idx )
{
	sel_item = idx;

	return RESULT_OK;
}

UINT32 HandleSelect( EVENT_STACK_T * ev_st, APPLICATION_T * app, UINT32 param )
{	
	APP_UtilChangeState( HW_STATE_EDIT, ev_st, app );
	
	return RESULT_OK;
}

UINT32 HandleAddLine( EVENT_STACK_T * ev_st, APPLICATION_T * app, UINT32 param )
{	
	char 		    	enter[] = {0x0D, 0x0A};
	FILE		 		file=0;
    UINT32 				written, i=0;

		DL_FsDeleteFile(ini_file, 0);
		
		file = DL_FsOpenFile(ini_file, FILE_WRITE_MODE, 0);
		
		// запись в файл
		for(i=0; i < entries_num; i++)
		{
			if( i == sel_item )
			DL_FsWriteFile("=\n", 2, 1, file, &written );
			
			if( item[i].str[0] != '\n' )
			DL_FsWriteFile(item[i].str, strlen(item[i].str), 1, file, &written);
			if( i != entries_num - 1 )
			DL_FsWriteFile(enter, 2, 1, file, &written );
			
		}
		

	DL_FsCloseFile(file);

	ReadMenu(ini_file);
    APP_UtilChangeState( HW_STATE_MAIN, ev_st, app );
	
	return RESULT_OK;
}

UINT32 HandleDeleteLine( EVENT_STACK_T * ev_st, APPLICATION_T * app, UINT32 param )
{	
	char 		    	enter[] = {0x0D, 0x0A};
	FILE		 		file=0;
    UINT32 				written, i=0;

		DL_FsDeleteFile(ini_file, 0);
		
		file = DL_FsOpenFile(ini_file, FILE_WRITE_MODE, 0);
	
		// запись в файл

		for(i=0; i < entries_num; i++ )
		{
			if( i != sel_item ) {
			
				if(item[i].str[0] != '\n')
				DL_FsWriteFile(item[i].str, strlen(item[i].str), 1, file, &written);
				if(i != entries_num - 1)
				DL_FsWriteFile(enter, 2, 1, file, &written );
			}
		}
		

	DL_FsCloseFile(file);

	ReadMenu(ini_file);
    APP_UtilChangeState( HW_STATE_MAIN, ev_st, app ); 
	return RESULT_OK;
}

UINT32 HandleAbout( EVENT_STACK_T * ev_st, APPLICATION_T * app, UINT32 param )
{	
	UIX_MessageBox( app, "iniTool v1.5 by R.U.S.H.A.N.\n"
						 "http://forum.motofan.ru\n",
						 "About...",
						 icon_null
						 );
	return RESULT_OK;
}

UINT32 HandleExit( EVENT_STACK_T * ev_st, APPLICATION_T * app, UINT32 param )
{	
	APP_ConsumeEv( ev_st, app );
	
	destroyApp(ev_st, app);
	
	return RESULT_OK;
}

UINT32 EditDone( EVENT_STACK_T * ev_st, APPLICATION_T * app, UINT32 l  )
{

	if ( l == (UINT32)(-1) || l == 0 ) {
		// Cancel
		return APP_UtilChangeState( HW_STATE_MAIN, ev_st, app );
	}
	
	UIX_InputGetData(app->dialog, item[sel_item].str, 0);
	
	Save(ev_st, app); 

    return RESULT_OK;

}

static void * MainEntryReq( APPLICATION_T * app, UINT32 idx )
{
	UIX_LIST_ENTRY_T    *e;
	
	// динамически создаем
	e = malloc( sizeof(UIX_LIST_ENTRY_T) );
	memclr(e, sizeof(UIX_LIST_ENTRY_T));
	
	e->name = item[idx].str;
	
	return e;
}
//Сохранить
UINT32 Save( EVENT_STACK_T *ev_st,  APPLICATION_T *app )
{
	char 		    	enter[] = {0x0D, 0x0A};
	FILE		 		file=0;
    UINT32 				written, i=0;

		DL_FsDeleteFile(ini_file, 0);
		
		file = DL_FsOpenFile(ini_file, FILE_WRITE_MODE, 0);
		
		
		// запись в файл

		for(i=0; i < entries_num; i++)
		{
			if(item[i].str[0] != '\n')
			DL_FsWriteFile(item[i].str, strlen(item[i].str), 1, file, &written);
			if(i != entries_num - 1)
			DL_FsWriteFile(enter, 2, 1, file, &written );
		}
		

	DL_FsCloseFile(file);

	ReadMenu(ini_file);
    APP_UtilChangeState( HW_STATE_MAIN, ev_st, app ); 
	
	return RESULT_OK;
}


UINT32 ReadMenu(WCHAR *ini_uri)
{

	UINT32          readen;
	FILE 		    f;
	UINT32 			fSize=0;	
	int 			i = 0, j = 0, numStr=0;
	char   			*buffer=NULL;
	
		if (DL_FsFFileExist(ini_uri)) // проверяем есть ли файл
		{
		
			f = DL_FsOpenFile(ini_uri, FILE_READ_MODE, 0);
			
			fSize = DL_FsGetFileSize(f);

			buffer = (char*)malloc( fSize );

		DL_FsReadFile( buffer,  1,  fSize+2, f, &readen );
		if (readen == 0) return RESULT_FAIL;
		buffer[readen] = 0;
	
        DL_FsCloseFile(f);
				
			
        entries_num = 0;
		
        for (i=0; i < fSize+2; i++)
        if (buffer[i] == 0x0A) entries_num++; 
			
        if (item != NULL)
        {
            mfree(item);
            item = NULL;
        }
        item = (LINE*)malloc( entries_num*sizeof(LINE) );

        item[numStr].str[0]= 0;
		
      		for (i=0; i < fSize+2 && j+1 < 256 && numStr <= entries_num; i++)
			{
				if (buffer[i] == 0x0A) 
				{
                     j=0;
					 numStr++; 

                     item[numStr].str[0]= 0;
				}
				else
				{
                    if( buffer[i] != 0x0D) 
					{
						  item[numStr].str[j] = buffer[i];
						  item[numStr].str[j+1] = 0;
						 j++;
					}
				}
			}

            entries_num++;

		mfree(buffer);	
	}
		return status;
}


UINT32 util_asc2ul( const WCHAR * s, UINT32 count )
{
	WCHAR    b;
	UINT32   res = 0;
	
	while ( count > 0 ) {
		b = *s++;
		if ( b >= '0' && b <= '9' ) {
			b -= '0';
		} else if ( b >= 'A' && b <= 'F' ) {
			b = b - 'A' + 10;
		} else if ( b >= 'a' && b <= 'f' ) {
			b = b - 'a' + 10;
		} else
		b = 0;
		res = res << 4;
		res |= b;
		count--;
	}
	
	return res;
}

UINT32 param2uri(WCHAR *param, WCHAR *uri)
{
	_u64                    tmp;
	DL_FS_MID_T             fid;

	tmp.R0 = util_asc2ul(param+0, 8);
	tmp.R1 = util_asc2ul(param+8, 8);

	if (tmp.R0==0 && tmp.R1==0) {
		u_strcpy(uri, param);
	} else {
		memcpy(&fid, &tmp, sizeof(UINT64));
		DL_FsGetURIFromID( &fid, uri );
	}
	return RESULT_OK;
}