/*
 *	Module skiplist.c
 *	Demonstrates the usage of the SkipList Object.
 *	It is a LIST Object that keeps in memory only the
 *	items visible in its window. The number of items in the list
 *	is not known by the Object. New items are queried to the application
 *	via a callback as the user requests to skip in the list.
 */
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "mgui.h"

#define MAX_POS	26L*26L*26L*26L*26L*26L

extern void CloseDialogCB(MOBJECT p, void *s, void *v);
extern void ScrolledListCB(MOBJECT p, LIST_ACT *la, void *v);

extern MTColor black, white, cadetblue, seagreen, darkblue;
extern int LowRes;

static long skip_pos = MAX_POS - 10;

/******************************************************************
 *	This function converts a long integer to a string of six
 *	lower case alphabetic characters
 *******************************************************************/
static char *PosToString(long ll)
{
	static char buff[8];
	int i;

	i = 6;
	buff[i] = '\0';
	for (; i-- > 0; ll/= 26)
		buff[i] = 'a' + (char)(ll%26);

	return buff;
}

/***********************************************************
 *	This function converts a string of six
 *	lower case alphabetic characters to a long integer
 **********************************************************/
static long StringToPos(char *str)
{
	long ll;
	int i;

	ll = 0L;
	for (i=0; i<6; i++) {
		ll *= 26;
		if (*str) {
			if (!islower(*str))
                        	return -1L;
			ll += *str - 'a';
			str++;
		}
	}
	return (*str == '\0' ? ll : -1L);
}

/**************************************************
*	This callback is called by the Object when
*	it needs new data to draw rows in the
*	virtual list. The application must 'sprintf'
*	row data in the linked list passed by the
*	Object
**************************************************/
static void SkipBlockCB(MOBJECT obj, SBL_NEW_BLOCK *pnb, void *data)
{
	int i;
	long off=skip_pos;
	XL_ITEM *pd = pnb->pi;
	long max = MAX_POS;

	switch (pnb->from) {
		case SEEK_SET:
/*
 * Items must be supplied begining at an offset from the top
 */
			off = pnb->offset;
			break;
		case SEEK_CUR:
/*
 * Items must be supplied begining at an offset from the current position
 */
			off += pnb->offset;
			if (off < 0L) {
				pnb->nr = -1;
				return;
			}
			else if (off >= max) {
				pnb->nr = -1;
				return;
			}
			break;
		case SEEK_END:
/*
 * Items must be supplied begining at an offset from the end
 */
			off = pnb->offset + max;
			break;
		default:
			off = 0L;
	}
	for (i=0; i < pnb->nr && pd && off < max; i++) {
		sprintf(pd->data, "%s %09ld %09ld", PosToString(off), off, pnb->offset);
		pd->u_data = (void *)off++;
		pd = pd->next;
	}
	pnb->nr = i;
/*
 * skip_pos stores the last supplied item
 */
	skip_pos = off-1;
}

/**************************************************
*	This callback is called by the Object when
*	it needs to know the position (if any) of
*	an item in the list.
**************************************************/
static void SkipSearchCB(MOBJECT obj, SBL_ITEM_SEEK *pis, void *data)
{
	long ll;
	char *str = pis->sub_item;

        if ((ll = StringToPos(str)) != -1L) {
		pis->pos = 0;
		skip_pos = ll;
	}
}

/*****************************************************
 *	This callback opens the dialog when the
 *	corresponding menu item is selected
 *****************************************************/
void SkipListCB(MOBJECT o, void *o_data, void *a_data)
{
	MOBJECT pbutton, shell, form0, form, sfile, slist;
	char str[512];

	shell = MCreateShell("Skip List", SF_MODAL);

	form0 = MCreateColForm(shell);

	form = MCreateRowForm(form0);

	sprintf(str, "%ld elements", MAX_POS);
	MCreateLabel(form, str, DEFAULT_FONT);

	slist = MCreateSListSkip(form, SkipBlockCB, 0L, SkipSearchCB, 0L, DEFAULT_FONT, 12, 16, 30);
	MObjectSetColor(slist, cadetblue, black);
	MSBLSetHead(slist, "Value  Position  Offset");
	MSBLHeadSetColor(slist, darkblue, white);
	MSListSetCallback(slist, ScrolledListCB, 0L);

	sfile = MCreateSFile(form0, __FILE__,
			(LowRes ? SMALL_FONT : DEFAULT_FONT),
			15, 56);
	MObjectSetColor(sfile, darkblue, white);

	pbutton = MCreatePButton(shell, "Close", TIMES_L_FONT);
	MPButtonSetCallback(pbutton, CloseDialogCB, 0L);

	MShellRealize(shell);
}

