/*------------------------------------------------------------*/
/*                                                            */
/*    Turbo Vision -  Version 1.0                             */
/*                                                            */
/*                                                            */
/*    Copyright (c) 1991 by Borland International             */
/*    All Rights Reserved.                                    */
/*                                                            */
/*------------------------------------------------------------*/

/*------------------------------------------------------------*/
/*                                                            */
/* Modified by Francis Gastellu for PowerPPL
/*                                                            */
/*------------------------------------------------------------*/

#define Uses_TStreamableClass
#define Uses_TPoint
#define Uses_TStreamable
#define Uses_ipstream
#define Uses_opstream
#define Uses_fpstream
#define Uses_TRect
#define Uses_TScrollBar
#define Uses_TScroller
#define Uses_TDrawBuffer
#define Uses_TEvent
#define Uses_TWindow
#define Uses_TKeys
#define Uses_TPalette
#define Uses_MsgBox
#include <tv.h>
#include "pphlp.h"
#include <alloc.h>

#if !defined( __HELP_H )
#include "Help.h"
#endif  // __HELP_H

#if !defined( __UTIL_H )
#include "Util.h"
#endif  // __UTIL_H

#if !defined( __STRING_H )
#include <string.h>
#endif  // __STRING_H

#if !defined( __LIMITS_H )
#include <limits.h>
#endif  // __LIMITS_H

#if !defined( __STAT_H )
#include <sys\stat.h>
#endif  // __STAT_H

#if !defined( __CTYPE_H )
#include <ctype.h>
#endif  // __CTYPE_H

#if !defined( __IO_H )
#include <io.h>
#endif  // __IO_H

#ifdef IDE
#define HISTSIZE 100
#else
#define HISTSIZE 1000
#endif

#pragma warn -dsz

TPoint HelpDeltaHistory[HISTSIZE];
int HelpHistory[HISTSIZE][2];
int HelpHistoryPtr = 0;
int speedSearchShifting=0;


void addToHistory(int topicnr, int selRef, TPoint olddelta);
int getLastTopic();
int getLastSelRef();
TPoint getLastDelta();
int initCursor;
int synchroCursor;
extern char aproxKeyWord[256];

// THelpViewer

THelpViewer::THelpViewer( const TRect& bounds, TScrollBar* aHScrollBar,
    TScrollBar* aVScrollBar, THelpFile *aHelpFile, ushort context, int selRef )
    : TScroller( bounds, aHScrollBar, aVScrollBar )
{
    options = (options | ofSelectable);
    growMode = gfGrowHiX | gfGrowHiY;
    hFile = aHelpFile;
    topic = aHelpFile->getTopic(context);
    topic->setWidth(size.x);
    topicMap = NULL;
    setLimit(78, topic->numLines());
    refreshMap();
//    selected = selRef;
    selected = 0;
	initCursor=1;
    synchroCursor=0;
    showCursor();
    speedSearch[0]=0;
    speedSearchShifting = 0;
    if (aproxKeyWord[0] != 0)
    	{
        initCursor = 0;
    	strcpy(speedSearch, aproxKeyWord);
        execSpeedSearch();
        speedSearch[0]=0;
		aproxKeyWord[0]=0;
        }
#pragma warn -par
}
#pragma warn .par

THelpViewer::~THelpViewer()
{
      delete hFile;
      delete topic;
      if (topicMap != NULL)
      	delete topicMap;
}


void addToHistory(int topicnr, int selRef, TPoint oldDelta)
{
HelpHistory[HelpHistoryPtr][0] = topicnr;
HelpHistory[HelpHistoryPtr][1] = selRef;
HelpDeltaHistory[HelpHistoryPtr] = oldDelta;

if (HelpHistoryPtr == HISTSIZE - 1)
	{
	for(int a=1; a < HISTSIZE-1;a++)
		{
		HelpHistory[a-1][0] = HelpHistory[a][0];
		HelpHistory[a-1][1] = HelpHistory[a][1];
        HelpDeltaHistory[a-1] = HelpDeltaHistory[a];
		}
	}
else
	HelpHistoryPtr++;
}

int getLastTopic()
{
if (HelpHistoryPtr == 0) return 0;
HelpHistoryPtr--;
return HelpHistory[HelpHistoryPtr-1][0];
}

int getLastSelRef()
{
if (HelpHistoryPtr == 0) return 1;
return HelpHistory[HelpHistoryPtr][1];
}

TPoint getLastDelta()
{
TPoint nul={0,0};
if (HelpHistoryPtr == 0) return nul;
return HelpDeltaHistory[HelpHistoryPtr];
}

void THelpViewer::changeBounds( TRect& bounds )
{
    TScroller::changeBounds(bounds);
    topic->setWidth(size.x);
    setLimit(limit.x, topic->numLines());
}

void THelpViewer::draw()
{
    TDrawBuffer b;
    char line[256];
    char buffer[256];
    char *bufPtr;
    int i, j, l;
    int keyCount;
    ushort normal, keyword, selKeyword, c;
    TPoint keyPoint;
    uchar keyLength;
    int keyRef;

    normal = getColor(1);
    keyword = getColor(2);
    selKeyword = getColor(3);
    keyCount = 0;
    keyPoint.x = 0;
    keyPoint.y = 0;
    if (initCursor)
    	{
		cursor.x = 0;
        cursor.y = 0;
        setCursor(cursor.x, cursor.y);
        initCursor=0;
        }
    topic->setWidth(size.x);
    if (topic->getNumCrossRefs() > 0)
        {
	do
	    {
	    topic->getCrossRef(keyCount, keyPoint, keyLength, keyRef);
	    ++keyCount;
	    } while ( (keyCount < topic->getNumCrossRefs()) && (keyPoint.y <= delta.y));
	}
    for (i = 1; i <= size.y; ++i)
        {
	b.moveChar(0, ' ', normal, size.x);
	strcpy(line, topic->getLine(i + delta.y));
	if (strlen(line) > delta.x)
	    {
            bufPtr = line + delta.x;
            strncpy(buffer, bufPtr, size.x);
            buffer[size.x] = 0;
            b.moveStr(0, buffer, normal);
	    }
        else
	    b.moveStr(0, "", normal);
	while (i + delta.y == keyPoint.y)
	    {
            l = keyLength;
            if (keyPoint.x < delta.x )
                {
                l -= (delta.x - keyPoint.x);
				keyPoint.x = delta.x;
                }
            if (keyCount == selected)
            	{
				c = selKeyword;
				if (synchroCursor)
			    	{
					cursor.x = keyPoint.x - delta.x + speedSearchShifting;
			        cursor.y = keyPoint.y - delta.y-1;
			        setCursor(cursor.x, cursor.y);
			        synchroCursor=0;
			        }
                }
		    else
				c = keyword;
	    for(j = 0; j < l; ++j)
                b.putAttribute((keyPoint.x - delta.x + j),c);
	    if (keyCount < topic->getNumCrossRefs())
		{
		topic->getCrossRef(keyCount, keyPoint, keyLength, keyRef);
		++keyCount;
		}
	    else
		keyPoint.y = 0;
	    }
        writeLine(0, i-1, size.x, 1, b);
	}
}

TPalette& THelpViewer::getPalette() const
{
    static TPalette palette(cHelpViewer, sizeof( cHelpViewer)-1);
    return palette;
}

void THelpViewer::makeSelectVisible( int selected, TPoint& keyPoint,
	 uchar& keyLength, int& keyRef )
{
    TPoint d;

    topic->getCrossRef(selected, keyPoint, keyLength, keyRef);
    d = delta;
    if (keyPoint.x < d.x)
        d.x = keyPoint.x;
    if (keyPoint.x > d.x + size.x)
        d.x = keyPoint.x - size.x;
    if (keyPoint.y <= d.y)
	d.y = keyPoint.y -1;
    if (keyPoint.y > d.y + size.y)
	d.y = keyPoint.y - size.y;
    if ((d.x != delta.x) || (d.y != delta.y))
     delta=d;
//	 scrollTo(d.x, d.y);
}

void THelpViewer::switchToTopic( int keyRef, int selRef, int deltaX, int deltaY)
{
    if (topic != 0)
	delete topic;
    topic = hFile->getTopic(keyRef);
    topic->setWidth(size.x);
    scrollTo(0, 0);
    setLimit(limit.x, topic->numLines());
    refreshMap();
    selected = selRef;
    initCursor = 1;
    delta.x = deltaX;
    delta.y = deltaY;
    drawView();
    speedSearch[0]=0;
    speedSearchShifting = 0;
}

void THelpViewer::refreshMap()
{
TPoint keyPoint;
uchar keyLength;
int keyRef;
int keyCount;
char buf[256];
//int a;

if (topicMap != 0)
	free(topicMap);
topicMap = (char *)calloc((limit.x+1) * (limit.y+1),1);

keyCount = 0;

while (1)
	{
	++keyCount;
	if (keyCount > topic->getNumCrossRefs())
		break;
	topic->getCrossRef(keyCount-1, keyPoint, keyLength, keyRef);
//    for (a=0;a<keyLength;a++)
	strcpy(buf,topic->getLine(keyPoint.y));
   	strncpy(&topicMap[keyPoint.y*limit.x+keyPoint.x], &buf[keyPoint.x], keyLength);
	}

}

void THelpViewer::handleEvent( TEvent& event )
{

    TPoint keyPoint, mouse;
    uchar keyLength;
    int keyRef;
    int keyCount;
    int forceRedraw=0;
    int l;
    int bx, by;


    TScroller::handleEvent(event);
    switch (event.what)
	{

	case evKeyDown:
	    switch (event.keyDown.keyCode)
		{
        case kbDown:
		    speedSearch[0]=0;
		    speedSearchShifting = 0;
            if (cursor.y+1 > size.y-1)
            	{
            	if (delta.y < limit.y-1)
					{
					delta.y++;
                    forceRedraw=1;
                    }
                }
            else
	        	cursor.y++;
            setCursor(cursor.x, cursor.y);
            if (checkCursTopic() || forceRedraw)
				drawView();
            break;

        case kbUp:
		    speedSearch[0]=0;
		    speedSearchShifting = 0;
            if (cursor.y-1 < 0)
            	{
            	if (delta.y > 0)
					{
					delta.y--;
                    forceRedraw=1;
                    }
                }
            else
	        	cursor.y--;
            setCursor(cursor.x, cursor.y);
            if (checkCursTopic() || forceRedraw)
				drawView();
            break;

        case kbRight:
		    speedSearch[0]=0;
		    speedSearchShifting = 0;
            if (cursor.x+1 > size.x-1)
            	{
            	if (delta.x < limit.x-1)
					{
					delta.x++;
                    forceRedraw=1;
                    }
                }
            else
	        	cursor.x++;
            setCursor(cursor.x, cursor.y);
            if (checkCursTopic() || forceRedraw)
				drawView();
            break;

        case kbLeft:
		    speedSearch[0]=0;
		    speedSearchShifting = 0;
            if (cursor.x-1 < 0)
            	{
            	if (delta.x > 0)
					{
					delta.x--;
                    forceRedraw=1;
                    }
                }
            else
	        	cursor.x--;
            setCursor(cursor.x, cursor.y);
            if (checkCursTopic() || forceRedraw)
				drawView();
            break;

        case kbPgUp:
		    speedSearch[0]=0;
		    speedSearchShifting = 0;
        	if (delta.y == 0 && cursor.y != 0)
            	{
            	cursor.y = 0;
	            setCursor(cursor.x, cursor.y);
                }
            else if (delta.y - size.y >= 0)
            		{
    	         	delta.y -= size.y;
                    forceRedraw=1;
                    }
	    	     else
                 	{
            		delta.y = 0;
                    forceRedraw=1;
                    }
            if (checkCursTopic() || forceRedraw)
				drawView();
            break;

        case kbPgDn:
		    speedSearch[0]=0;
		    speedSearchShifting = 0;
        	if (delta.y + size.y < limit.y-1)
            	{
                delta.y += size.y;
                forceRedraw=1;
                }
            else
            	{
                if (delta.y == limit.y-1)
                	{
                    cursor.y = size.y - 1;
                    setCursor(cursor.x, cursor.y);
                    }
                else
                	{
            	    delta.y = limit.y-1;
                    forceRedraw=1;
                    }
                }
            if (delta.y < 0)
				{
				delta.y = 0;
                forceRedraw=1;
                }
            if (checkCursTopic() || forceRedraw)
				drawView();
            break;

        case kbHome:
		    speedSearch[0]=0;
		    speedSearchShifting = 0;
            cursor.x = 0;
            setCursor(cursor.x, cursor.y);
            checkCursTopic();
            drawView();
            break;

        case kbEnd:
		    speedSearch[0]=0;
		    speedSearchShifting = 0;
			cursor.x = strlen(topic->getLine(cursor.y + delta.y + 1));
            setCursor(cursor.x, cursor.y);
            checkCursTopic();
            drawView();
            break;



		case kbTab:

		    speedSearch[0]=0;
		    speedSearchShifting = 0;
        	synchroCursor=1;

            if (selected != 0)
            	{
		        ++selected;
		        if (selected > topic->getNumCrossRefs())
			    selected = 1;
		        if ( topic->getNumCrossRefs() != 0 )
			    makeSelectVisible(selected-1,keyPoint,keyLength,keyRef);
		        drawView();
		        break;
                }

            bx=cursor.x;
            by=cursor.y;
            for (;by<limit.y;by++)
            	{
                l = strlen(topic->getLine(by + delta.y + 1));
                for (;bx<l;bx++)
                    if (topicMap[(by+1+delta.y)*limit.x+bx+delta.x])
						{
                        cursor.x = bx;
                        cursor.y = by;
                        setCursor(cursor.x, cursor.y);
		            	checkCursTopic();
						makeSelectVisible(selected-1,keyPoint,keyLength,keyRef);
        		    	drawView();
						return;
                        }
                bx=0;
                }
	        selected = 1;
			if (topic->getNumCrossRefs() != 0)
				makeSelectVisible(selected-1,keyPoint,keyLength,keyRef);
            drawView();
            break;

		case kbShiftTab:

		    speedSearch[0]=0;
		    speedSearchShifting = 0;
        	synchroCursor=1;

            if (selected != 0)
            	{
		        --selected;
		        if (selected == 0)
			    selected = topic->getNumCrossRefs();
		        if ( topic->getNumCrossRefs() != 0 )
			    makeSelectVisible(selected-1,keyPoint,keyLength,keyRef);
		        drawView();
		        break;
                }

            bx=cursor.x;
            by=cursor.y;
            for (;by>0;by--)
            	{
                for (;bx>0;bx--)
                    if (topicMap[(by+1+delta.y)*limit.x+bx+delta.x])
						{
                        cursor.x = bx;
                        cursor.y = by;
                        setCursor(cursor.x, cursor.y);
		            	checkCursTopic();
						makeSelectVisible(selected-1,keyPoint,keyLength,keyRef);
        		    	drawView();
						return;
                        }
                if (by>1)
	                bx = strlen(topic->getLine(by + delta.y));
                }
	        selected = topic->getNumCrossRefs();
			if (topic->getNumCrossRefs() != 0)
				makeSelectVisible(selected-1,keyPoint,keyLength,keyRef);
            drawView();
            break;

		case kbEnter:
		    if (selected > 0 && selected <= topic->getNumCrossRefs())
			{
			topic->getCrossRef(selected-1, keyPoint, keyLength, keyRef);
			addToHistory(keyRef, selected, delta);
			switchToTopic(keyRef, 0, 0, 0);
			}
		    drawView();
		    break;
		case kbEsc:
		    event.what = evCommand;
		    event.message.command = cmClose;
		    putEvent(event);
		    drawView();
		    break;
		case kbAltF1:
        	synchroCursor=1;
		    if (HelpHistoryPtr == 0) break;
		    int a = getLastTopic();
		    int b = getLastSelRef();
            TPoint where = getLastDelta();
		    switchToTopic(a,b,where.x, where.y);
		    drawView();
		    break;
		case kbShiftF1:
		    addToHistory(hcHelpIndex,selected, delta);
		    switchToTopic(hcHelpIndex,1,0,0);
		default:
        	if (event.keyDown.charScan.charCode > 31)
				{
                char x[2] = "X";
                x[0] = event.keyDown.charScan.charCode;
                if (strlen(speedSearch) < 250);
                strcat(speedSearch, x);
                execSpeedSearch();
				}
		    return;
		}
	    clearEvent(event);
	    break;

	case evMouseDown:
	    mouse = makeLocal(event.mouse.where);
	    mouse.x += delta.x;
	    mouse.y += delta.y;
	    keyCount = 0;

	    do
	    {
		++keyCount;
		if (keyCount > topic->getNumCrossRefs())
		    return;
		topic->getCrossRef(keyCount-1, keyPoint, keyLength, keyRef);
	    } while (!((keyPoint.y == mouse.y+1) && (mouse.x >= keyPoint.x) &&
		  (mouse.x < keyPoint.x + keyLength)));
	    selected = keyCount;
	    drawView();
	    if (event.mouse.doubleClick)
		addToHistory(keyRef, selected, delta);
		switchToTopic(keyRef, 1, 0, 0);
	    clearEvent(event);
	    break;

	case evCommand:
	    if ((event.message.command == cmClose) && (owner->state && sfModal != 0))
		{
		endModal(cmClose);
		clearEvent(event);
		}
            break;
        }
}

int THelpViewer::checkCursTopic()
{
TPoint keyPoint, where;
uchar keyLength;
int keyRef;
int keyCount;
int previousTopic=selected;

where.x = cursor.x + delta.x;
where.y = cursor.y + delta.y;

if (!topicMap[(where.y+1)*limit.x+where.x])
	{
    selected = 0;
	return (selected != previousTopic);
    }

keyCount = 0;

do
{
	++keyCount;
	if (keyCount > topic->getNumCrossRefs())
		{
	    selected = 0;
		return (selected != previousTopic);
	    }
	topic->getCrossRef(keyCount-1, keyPoint, keyLength, keyRef);
} while (!((keyPoint.y == where.y+1) && (where.x >= keyPoint.x) && (where.x < keyPoint.x + keyLength)));

selected = keyCount;
return (selected != previousTopic);
}

void THelpViewer::execSpeedSearch()
{
TPoint keyPoint;
uchar keyLength;
int keyRef;
int keyCount;
char buf[256];
int best[2]={0,0};
int a,n;

keyCount = 0;

while (1)
	{
	++keyCount;
	if (keyCount > topic->getNumCrossRefs())
		break;
	topic->getCrossRef(keyCount-1, keyPoint, keyLength, keyRef);
//    for (a=0;a<keyLength;a++)
	strcpy(buf,topic->getLine(keyPoint.y));
    strncpy(buf, &buf[keyPoint.x], keyLength);
    buf[keyLength] = 0;
    n=0;
    for (a=0;a<strlen(speedSearch);a++)
    	if ((toupper(speedSearch[a]) == toupper(buf[a])) || (speedSearch[a] == ' ' && buf[a] == 'ÿ'))
        	n++;
        else
        	break;
    if (n > best[0])
    	{
    	best[1] = keyCount;
        best[0] = n;
        }
	}
if (best[1] != 0)
	{
    selected = best[1];
    makeSelectVisible(selected-1,keyPoint,keyLength,keyRef);
    synchroCursor=1;
    speedSearchShifting = best[0];
    drawView();
    }
}

// THelpWindow

THelpWindow::THelpWindow( TRect& bounds, THelpFile *hFile, ushort context ):
       TWindow( bounds, "Help", wnNoNumber ),
       TWindowInit( &THelpWindow::initFrame)
{
    TPoint nul={0,0};
    int selRef = 1;
    if (context == 0xFFFF)
	{
	addToHistory(1,1, nul);
	context = getLastTopic();
	selRef = getLastSelRef();
	}
    else
	addToHistory(context,1, nul);
    bounds.grow(-2,-1);
    options = (options | ofCentered);
    insert(new THelpViewer (bounds,
      standardScrollBar(sbHorizontal | sbHandleKeyboard),
      standardScrollBar(sbVertical | sbHandleKeyboard), hFile, context, selRef));
}

TPalette& THelpWindow::getPalette() const
{
    static TPalette palette(cHelpWindow, sizeof( cHelpWindow)-1);
    return palette;
}

#pragma warn .dsz

