/*
 *    Module skiplist.cpp
 *      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 "mguipp.h"

#include "mguitest.hpp"

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

class SkippedList : public CmCallback {
    CmShell *shell;
public:
    SkippedList();
    void closeDialog(void) { delete shell; delete this; };
};

class CaSkippedSList:public CmClippedSList
{
    long skip_pos;
      public:
    void onActivate(LIST_ACT * la);
    void onItemSeek(SBL_ITEM_SEEK * is);
    void onNewBlock(SBL_NEW_BLOCK * nb);
      CaSkippedSList(CmContainer * par, MTFont font, int nrow, int ncol, int xc)
    : CmClippedSList(par, 0,
	(NEWBLOCK_CB)&CaSkippedSList::onNewBlock,
	(ITEMSEEK_CB)&CaSkippedSList::onItemSeek,
	font, nrow, ncol, xc)
    {
	setCallback(this, (LISTACT_CB)&CaSkippedSList::onActivate);
	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((int) *str))
		return -1L;
	    ll += *str - 'a';
	    str++;
	}
    }
    return (*str == '\0' ? ll : -1L);
}

/**************************************************
*	This function 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
**************************************************/
void CaSkippedSList::
onNewBlock(SBL_NEW_BLOCK * pnb)
{
    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 function is called by the Object when
*	it needs to know the position (if any) of
*	an item in the list.
**************************************************/
void CaSkippedSList::
onItemSeek(SBL_ITEM_SEEK * pis)
{
    long ll;
    char *str = pis->sub_item;

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

/***************************************************************
 *	This function shows the activated item text, position
 *	and user data
 ***************************************************************/
void CaSkippedSList::
onActivate(LIST_ACT * la)
{
    char str[128];

    sprintf(str, "Activated row # %ld\n<%s>\nuser data: %ld",
	    la->pos, la->item, (long) la->u_data);
    MMessageDialog("", str, "Ok", 0);
}

SkippedList::SkippedList(void)
{
    CmForm *form0, *form;
    CaSkippedSList *skiplist;
    CmSFile *sfile;
    CmPushButton *pb;
    char str[512];

    shell = new CmShell("Skip List", SF_MODAL);

    form0 = new CmColForm(shell);
    form = new CmRowForm(form0);

    sprintf(str, "%ld elements", MAX_POS);
    new CmLabel(form, str, FIXED_MEDIUM);

    skiplist = new CaSkippedSList(form, FIXED_MEDIUM, 12, 16, 30);
    skiplist->setColor(cadetblue, black);
    skiplist->setHead("Value  Position  Offset");
    skiplist->setHeadColor(darkblue, white);

    sfile = new CmSFile(form0, __FILE__, FIXED_MEDIUM, 15, 60);
    sfile->setColor(darkblue, white);

    pb = new CmPushButton(shell, "Close", TIMES_MEDIUM);
    pb->setCallback(this, (VOID_CB)&SkippedList::closeDialog);

    shell->realize();
}

/*****************************************************
 *	This callback opens the dialog when the
 *	corresponding menu item is selected
 *****************************************************/
void
SkippedListDemo(void)
{
	new SkippedList;
}
