/*
	Using pseudo enumerations to implement category and sub-category
	attribute values.
	
	Dialog displays two choice elements.
	The first contains choices for the main category.
	The second contains choices for the sub-category.
	
	The choices available for the sub-category are dependant on the
	selected main category.
	
	
	This script assumes the existance of two ordinary string attributes,
	the names of which are defined below.
	
	Enumeration values are defined in a string array.
	
	Tony Goodman.
*/

/******************************************************************************
	Attribute Names
	
	These must exist - no check is made.
******************************************************************************/
const string ATTR_MAIN_CATEGORY = "Category"
const string ATTR_SUB_CATEGORY  = "Sub Category"


/******************************************************************************
	Indices into string array of enumeration values
	
	By defining these here we can change the number of categories without
	changing the rest of the code.
******************************************************************************/
const int NUM_MAIN_CATEGORIES  = 3
const int NUM_SUB_CATEGORIES   = 10
const int NUM_ENTRIES          = NUM_MAIN_CATEGORIES + (NUM_MAIN_CATEGORIES * NUM_SUB_CATEGORIES)  
const int MAIN_CATEGORY_OFFSET = NUM_SUB_CATEGORIES + 1


/******************************************************************************
	"Enumeration" Values
	
	First entry on the left is the main category. This is followed by the
	sub category values.
	
	It is important that there are exactly the same number entries in this
	array for each category.
	
	Empty strings are ignored - this allows us to have a varying number of
	sub categories for each category.
******************************************************************************/
const string CATEGORIES[NUM_ENTRIES] = { 
"Cheeses", "Brie",    "Cheddar", "Stilton", "Camembert", "Edam", "", "", "", "", "",
"Meats",   "Chicken", "Lamb",    "Beef",    "Pork",      "",     "", "", "", "", "",
"Fruits",  "Orange",  "Banana",  "Apple",   "Mango",     "",     "", "", "", "", ""
}


/******************************************************************************
	Dialog elements
******************************************************************************/
DB  dbMain			= null
DBE dbeLabel        = null
DBE dbeMainCategory = null
DBE dbeSubCategory	= null


/******************************************************************************
	Global variables
******************************************************************************/
Module currModule = null
Object currObject = null


/******************************************************************************
	isMainCategory
	
	returns true if the index to the string array is a main category.
	
	e.g. if the number of sub-categories is 10, then main categories
	are at indices 0, 11, 22, 33 ...
******************************************************************************/
bool isMainCategory(int i)
{
	return(i % MAIN_CATEGORY_OFFSET == 0)
}


/******************************************************************************
	getMainChoiceIndex
	
	given the choice selected in the dialog, returns the index to the string
	array.
******************************************************************************/
int getMainChoiceIndex(string mainCategory)
{
	int i = 0
	
	for (i = 0; i < NUM_MAIN_CATEGORIES; i++)
	{
		if (CATEGORIES[i * MAIN_CATEGORY_OFFSET] == mainCategory)
		{
			return(i)
		}
	}
	
	return(-1)
}


/******************************************************************************
	getSubChoiceIndex
	
	given the choices selected in the dialog, returns the index to the string
	array for the selected sub category.
******************************************************************************/
int getSubChoiceIndex(int mainSelection, string subCategory)
{
	int i          = 0
	int startIndex = 0
	int endIndex   = 0
	
	
	// convert into index to string array
	startIndex = mainSelection + 1
	endIndex   = startIndex + NUM_SUB_CATEGORIES
	
	// find matching sub category
	for (i = startIndex; i < endIndex; i++)
	{
		if (CATEGORIES[i] == subCategory)
		{
			return(i - startIndex)
		}
	}
	
	return(-1)
}


/******************************************************************************
	printCategories
******************************************************************************/
void printCategories()
{
	int i = 0
	
	for (i = 0; i < NUM_ENTRIES; i++)
	{
		if (isMainCategory(i))
		{
			print("\nCategory: " CATEGORIES[i] "\n")
		}
		else
		{
			print("\t" CATEGORIES[i] "\t")
		}
	}
}


/******************************************************************************
	initSubCategoryChoices
	
	initialise the choices displayed in the sub category DBE.
	If the current object has a value for this attribute then this value
	is selected, but only if it is a legal choice based on the current main
	category.
******************************************************************************/
void initSubCategoryChoices()
{
	int i              = 0
	int mainSelection  = 0
	string subCategory = ""
	
	empty(dbeSubCategory)
	
	mainSelection = get(dbeMainCategory)
	
	// if there is no selection in the main category DBE
	// then leave this DBE empty.
	if (mainSelection < 0)
	{
		return
	}
	
	// convert into index to string array
	mainSelection = mainSelection * MAIN_CATEGORY_OFFSET
	
	// extract string values from array
	for (i = mainSelection + 1; i < mainSelection + NUM_SUB_CATEGORIES; i++)
	{
		if (CATEGORIES[i] == "")
		{
			break
		}
		
		insert(dbeSubCategory, noElems(dbeSubCategory), CATEGORIES[i])
	}
	
	// check current object for current atribute value
	subCategory = currObject.ATTR_SUB_CATEGORY ""
	
	set(dbeSubCategory, getSubChoiceIndex(mainSelection, subCategory))
}


/******************************************************************************
	initMainCategoryChoices
	
	initialise the choices displayed in the main category DBE.
	If the current object has a value for this attribute then this value
	is selected.
	
	The sub category DBE is also updated.
******************************************************************************/
void initMainCategoryChoices()
{
	int i = 0
	string mainCategory = ""
	
	
	// populate main category DBE
	for (i = 0; i < NUM_ENTRIES; i += MAIN_CATEGORY_OFFSET)
	{
		insert(dbeMainCategory, noElems(dbeMainCategory), CATEGORIES[i])
	}
	
	// check current object for current atribute value
	mainCategory = currObject.ATTR_MAIN_CATEGORY ""
	
	// convert mainCategory to choice index and set selection in DBE
	set(dbeMainCategory, getMainChoiceIndex(mainCategory))
	
	// initialise sub category choices based on setting of main category
	initSubCategoryChoices()
}


/******************************************************************************
	doEditCategory
	
	Callback when user clicks OK button.
******************************************************************************/
void doEditCategory(DB db)
{
	int mainSelection = 0
	int subSelection  = 0
	
	
	// get user selections from dialog elements
	mainSelection = get(dbeMainCategory)
	subSelection  = get(dbeSubCategory)
	
	
	// enforce selection of main category
	if (mainSelection < 0)
	{
		infoBox("Please select main category")
		return
	}
	
	// enforce selection of sub category
	if (subSelection < 0)
	{
		infoBox("Please select sub category")
		return
	}
	
	// adjust selections for for real index into string array
	mainSelection = mainSelection * MAIN_CATEGORY_OFFSET
	subSelection  = mainSelection + subSelection + 1
	
	// set object attributes
	currObject.ATTR_MAIN_CATEGORY = CATEGORIES[mainSelection]
	currObject.ATTR_SUB_CATEGORY  = CATEGORIES[subSelection]
	
	// refresh display to show changes
	refresh(currModule)
	
	// we are finished
	hide(dbMain)
	destroy(dbMain)
	dbMain = null
}


/******************************************************************************
	doSelectMainCategory
	
	Callback when user makes a selection in the main category DBE.
	Call function to update the choices available for sub category.
******************************************************************************/
void doSelectMainCategory(DBE dbe)
{
	initSubCategoryChoices()
}


/******************************************************************************
	MAIN
******************************************************************************/
currModule = current Module

if (null currModule)
{
	infoBox("This utility can only be run from a module")
	halt
}

currObject = current Object

if (null currObject)
{
	infoBox("No current object.")
	halt
}

dbMain = create(currModule, "Edit Category")

dbeLabel = label(dbMain, 
                 "Description of edit category utility.\n")

dbeMainCategory  = choice(dbMain, "Main Category: ", dummy, 0)
dbeSubCategory = choice(dbMain, "Sub Category: ", dummy, 0)

apply(dbMain, "OK", doEditCategory)

set(dbeMainCategory, doSelectMainCategory)

realize(dbMain)

initMainCategoryChoices()

show(dbMain)