#include <PalmOS.h>
#include "PanelList.h"

#pragma warn_a5_access on

//private defines

#define PANEL_LIST_INVALID_CATEGORY	0xFFFFFFFF
#define TOSTR(x)		#x
#define STRINGIFY(x)	TOSTR(x)
#define assert(x) 		assert_str(x,__FILE__ ":" STRINGIFY(__LINE__) " - assert failed \"" #x "\"")


//////// private methods

static void assert_str(Boolean cond,char* str){
	
	if(!cond){
		
		ErrAlertCustom(0,str,NULL,NULL);
		SysReset();
	}
}

static void* malloc(UInt32 x){
	
	void* ret = MemChunkNew(0,x,0x1200);
	
	assert_str(ret != NULL,"Out of memory");
					//all allocs are small, so they will not fail. If they do  - you have bigger problems.
					// however to make sure we corrupt no data, this is here to save the day, should
					// you try to run this with only 200 bytes of dynamic ram free :-)
	
	
	return ret;
}

static void free(void* x){
	
	MemPtrFree(x);
}

static void* realloc(void* x,UInt32 newSize){
	
	void* ret;
	
	
	ret = malloc(newSize);
	
	if(ret){
		
		MemMove(ret,x,newSize);
		free(x);
		
	}
	
	return ret;
}

static UInt32 prvPanelListFindCatIdx(PanelList* list,char* category){
	
	UInt32 i;
	
	for(i = 0; i < list->numCategories; i++){
		
		if(StrCaselessCompare(list->categories[i]->name,category) == 0) return i;
	}
	
	return PANEL_LIST_INVALID_CATEGORY;
}

static void prvPanelListAddItemToAList(void*** items,UInt32* numItems,void* item){
	
	*items = realloc(*items,(*numItems+1)*sizeof(void*));
	
	(*items)[(*numItems)++] = item;
}

static void prvPanelListAllocItem(void*** items,UInt32* numItems,UInt32 itemSz){
	
	prvPanelListAddItemToAList(items,numItems,malloc(itemSz));
}

static Int16 prvPanelListCategoryComparF(void * a, void * b, Int32){
	
	Category* c1 = *(Category**)a;
	Category* c2 = *(Category**)b;
	
	return StrCaselessCompare(c1->name,c2->name);
}

static Int16 prvPanelListPanelComparF(void * a, void * b, Int32){
	
	Panel* p1 = *(Panel**)a;
	Panel* p2 = *(Panel**)b;
	
	return StrCaselessCompare(p1->name,p2->name);
}

static UInt32 prvPanelListGetCategoryIdx(PanelList* list, Category* cat){
	
	UInt32 i;
	
	for(i = 0; i < list->numCategories; i++){
		
		if(list->categories[i] == cat) return i;
	}
	
	assert_str(0,"Panel not in any category!");
	return 0;
}


//////// public methods




PanelList* PanelListCreate(PanelListItemKillProc itemKillF){
	
	PanelList* list;
	Panel** panels;
	Category** categories;
	
	list = malloc(sizeof(PanelList));
	panels = malloc(1);
	categories = malloc(1);
	
	list->numPanels = 0;
	list->numCategories = 0;
	list->panels = panels;
	list->categories = categories;
	list->itemKillF = itemKillF;
	
	return list;
}

void PanelListDestroy(PanelList* list){
	
	UInt32 i;
	
	for(i = 0; i < list->numPanels; i++){
		
		free(list->panels[i]);
	}
	
	for(i = 0; i< list->numCategories; i++){
		
		free(list->categories[i]->panels);
		free(list->categories[i]);
	}
	
	free(list->panels);
	free(list->categories);
	free(list);
}

void PanelListAddPanel(PanelList* list,UInt32 crid,BitmapPtr icon,BitmapPtr iconS,char* name,char* category){
	
	UInt32 catIdx;
	Panel* panel;
	Category* cat;
	
	if(!category) category = "Other";
	
	assert(name != NULL);
	assert(StrLen(name) < 32);
	assert(StrLen(category) < 32);
	
	prvPanelListAllocItem((void***)&list->panels,&list->numPanels,sizeof(Panel));
	
	panel = list->panels[list->numPanels-1];
	
	catIdx = prvPanelListFindCatIdx(list,category);
	if(catIdx == PANEL_LIST_INVALID_CATEGORY){
		
		prvPanelListAllocItem((void***)&list->categories,&list->numCategories,sizeof(Category));
		
		cat = list->categories[list->numCategories-1];
		
		StrCopy(cat->name,category);
		cat->num = 0;
		cat->panels = malloc(1);
		
	}
	else{
		
		cat = list->categories[catIdx];
	}
	
	prvPanelListAddItemToAList((void***)&cat->panels,&cat->num,panel);
	
	StrCopy(panel->name,name);
	panel->crid = crid;
	panel->icon = icon;
	panel->iconS = iconS;
	panel->category = cat;
}

void PanelListSort(PanelList* list){
	
	UInt32 i;
	
	
	//sort category list
	
	SysQSort(list->categories,list->numCategories,sizeof(void*),&prvPanelListCategoryComparF,NULL);
	
	
	//sort items in each category
	
	for(i = 0; i < list->numCategories; i++){
		
		SysQSort(list->categories[i]->panels,list->categories[i]->num,sizeof(void*),
														&prvPanelListPanelComparF,NULL);
	}
}

UInt32 PanelListGetNumPanels(PanelList* list){
	
	return list->numPanels;
}


UInt32 PanelListGetNumCategories(PanelList* list){
	
	return list->numCategories;
}

void PanelListGetNthPanel(PanelList* list,UInt32 n,UInt32* crid,BitmapPtr* icon,BitmapPtr* iconS,char* name,UInt32* categoryIdx){
	
	Panel* p;
	
	assert(n < list->numPanels);
	
	p = list->panels[n];
	
	if(crid) *crid = p->crid;
	if(icon) *icon = p->icon;
	if(iconS) *iconS = p->icon;
	if(name) StrCopy(name,p->name);
	if(categoryIdx) *categoryIdx = prvPanelListGetCategoryIdx(list,p->category);
}

void PanelListGetNthCategory(PanelList* list,UInt32 n,char* name,UInt32* numPanels){
	
	Category* c;
	
	assert(n < list->numCategories);
	
	c = list->categories[n];
	
	if(name) StrCopy(name,c->name);
	if(numPanels) *numPanels = c->num;
}

void PanelListGetNthPanelInCategory(PanelList* list,UInt32 catIdx,UInt32 n,UInt32* crid,BitmapPtr* icon,BitmapPtr* iconS,char* name){
	
	Category* c;
	Panel* p;
	
	assert(catIdx < list->numCategories);
	
	c = list->categories[catIdx];
	
	assert(n < c->num);
	
	p = (Panel*)c->panels[n];
	
	if(crid) *crid = p->crid;
	if(icon) *icon = p->icon;
	if(iconS) *iconS = p->icon;
	if(name) StrCopy(name,p->name);
}