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

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

#define Uses_TApplication
#define Uses_TGroup
#define Uses_TEditor
#define Uses_TFileEditor
#define Uses_TEvent
#define Uses_opstream
#define Uses_ipstream
#define Uses_MsgBox
#define Uses_TKeys
#define Uses_TWindow
#define Uses_TStreamableClass
#define Uses_TScreen
#define Uses_TProgram
#include <tv.h>

#include <string.h>
#include <dos.h>
#include <ctype.h>
#include "message.h"
#include "pp.h"
#include "tagger.h"
#include "break.h"

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

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

#if !defined( __FSTREAM_H )
#include <fstream.h>
#endif  // __FSTREAM_H

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

#if !defined( __STDIO_H )
#include <stdio.h>
#endif  // __STDIO_H

void processEmacsHeader(const char *aFileName);
void createEmacsHeader(const char *FileName);

void traceF7(void);
void resetTrace(void);

extern char curfilename[MAXPATH];

const bcSaveAll = 1608;
const bcReSelectEditor = 1609;
const bcSelectWindow = 1610;

extern int winused[100];
extern TFileEditor *Editor[100];
extern unsigned long crcEditor[100];
extern char registered;
extern int runProcedure;
extern char emacsHeader;
extern int mainNumber;
extern int wantToTrace;

extern char drive[MAXDRIVE];
extern char dir[MAXDIR];
extern char fname[MAXFILE];
extern char ext[MAXEXT];

extern Boolean Tagger;
extern TTaggerWindow *TaggerWindow;

extern TCollection *tagList;

void reminder(int chance);
extern "C" unsigned long int crc32file(char *name);

char TopicSelected[50];
int getKeyWordContext(char str[]);
extern char errorMessage[128];
int errorDisplayed;
char errorFichier[256];
int errorLineNumber;
char aproxKeyWord[256];

char drive3[MAXDRIVE];
char dir3[MAXDIR];
char fname3[MAXFILE];
char ext3[MAXEXT];

extern char saveBefore;
extern char SysPath[MAXPATH];
extern char desktopFile[256];

extern int curTag;

inline ushort min( ushort u1, ushort u2 )
{
    return u1 < u2 ? u1 : u2;
}

#pragma warn -ias
const char * const TFileEditor::name = "TFileEditor";
#pragma warn .ias

TFileEditor::TFileEditor( const TRect& bounds,
			  TScrollBar *aHScrollBar,
			  TScrollBar *aVScrollBar,
			  TIndicator *aIndicator,
			  const char *aFileName,
			  int num
			) :
	TEditor( bounds, aHScrollBar, aVScrollBar, aIndicator, 0 )
{
    number = num;
	if( aFileName == 0 )
		{
		fileName[0] = EOS;
		Editor[number] = this;
        }
	else
	{
	strcpy( fileName, aFileName );
	fexpand( fileName );
	if( isValid )
		{
		isValid = loadFile();
		errorDisplayed = False;
		Editor[number] = this;
        strcpy(curfilename,fileName);
		}
	}
}

void TFileEditor::doneBuffer()
{
	delete buffer;
}

void TFileEditor::helpOnTopic()
{
int i=0;
int j=0;
char c[2]="X";
strcpy(TopicSelected,"");
ushort p = curPtr;
while (1)
	{
	i = bufChar(prevChar(p));
	if (!((i >= '@') && (i <= 'z') || (i >= '0') && (i <= '9'))) break;
	if (p == 0) break;
	p = prevChar(p);
	}

while (1)
	{
	j++;
	i = bufChar(p);
	if (!((i >= '@') && (i <= 'z') || (i >= '0') && (i <= '9'))) break;
	c[0] = bufChar(p);
	strcat(TopicSelected,c);
	p = nextChar(p);
	if (curPtr < gapLen)
		if (p == bufSize) break;
	else
		if (p+gapLen == bufSize) break;
	if (j == 49) break;
	}
strcpy(TopicSelected,strupr(TopicSelected));
aproxKeyWord[0] = 0;
int k = getKeyWordContext(TopicSelected);
TEvent t;
if (k != 1000)
	{
    t.what = evBroadcast;
    t.message.command = 1015;
    t.message.infoInt = k;
    putEvent(t);
    }
else
	{
    t.what = evBroadcast;
    t.message.command = 1015;
    if (toupper(TopicSelected[0]) < 'A' || toupper(TopicSelected[0]) > 'Y')
	    t.message.infoInt = 346;
    else
    	{
	    strcpy(aproxKeyWord, TopicSelected);
	    t.message.infoInt = 500 + TopicSelected[0]-'A';
        }
    putEvent(t);
    }
}


void TFileEditor::handleEvent( TEvent& event )
{
	unsigned long crc;
	char tmpname[255];
    FILE *out;
	TEditor::handleEvent(event);
	uchar selectMode = 0;
	switch( event.what )
	{
	case evCommand:
		switch( event.message.command )
		{
		case cmTopicSearch:
			 if (!registered) reminder(5);
			 helpOnTopic();
			 return;
		case cmSave:
			if (!registered) reminder(6);
			save();
			break;
		case cmSaveAs:
			saveAs();
			break;

		case 2001:

			char answer[81]="";
			if (inputBox("Add Tag", "Name your tag :", answer, 80) == cmCancel) break;
			fnsplit(fileName, drive, dir, fname, ext);
			char str[15];
			strcpy(str,fname);
			strcat(str,ext);

			char str2[256+1];

			sprintf(str2,"%s %d : %s", str, curPos.y+1, answer);
			tagList->insert( newStr( str2 ) );
			if (Tagger) TaggerWindow->update();
		break;

        case 2008:

            if (tagList->getCount() > 0)
            	{
                if (!Tagger)
                	{
					event.what = evCommand;
					event.message.command = 2000;
                    putEvent(event);
                    break;
                    }
                curTag = TaggerWindow->tagListBox->focused;
			    if (curTag < tagList->getCount()-1)
				    curTag++;
                else
            	    curTag=0;
                TaggerWindow->tagListBox->focusItem(curTag);
                }

        break;


		case cmCompile:

			event.what = evBroadcast;
			event.message.command = 1540;

			if (strcmp(&desktopFile[strlen(desktopFile)-10], "DESKTOP.PP"))
				{
				fnsplit(desktopFile,drive3,dir3,fname3,ext3);
				strcpy(errorFichier,fname3);
				strcat(errorFichier,".PPS");
				event.message.infoPtr = errorFichier;
				}
			else
				event.message.infoPtr = fileName;

			putEvent(event);
			break;

		case cmRunPPE:
        case cmTraceF7:

            if (event.message.command == cmTraceF7)
            	wantToTrace = 1;

			event.what = evBroadcast;
			event.message.command = 1543;

			if (strcmp(&desktopFile[strlen(desktopFile)-10], "DESKTOP.PP"))
				{
				fnsplit(desktopFile,drive3,dir3,fname3,ext3);
				strcpy(errorFichier,fname3);
				strcat(errorFichier,".PPS");
				event.message.infoPtr = errorFichier;
				}
			else
				event.message.infoPtr = fileName;

			putEvent(event);
			break;

		default:
			return;
		}
		break;
	case evBroadcast:
		switch(event.message.command)
		{
		case bcReSelectEditor:
			event.message.command = bcSelectWindow;
			event.message.infoInt = number;
			return;

		case 1530:
			if (strstr(fileName, errorFichier) == NULL) return;
			update(ufLine);
			int makeFocused = event.message.infoInt;
			errorDisplayed = True;
			int trackedLine;
			int errorLine;
			int trackPos;
            if (errorLineNumber > limit.y) break;

            if (errorLineNumber >= delta.y && errorLineNumber < delta.y + size.y - 1)
            	{
                trackedLine = delta.y;
                trackPos = errorLineNumber - delta.y - 1;
                errorLine = size.y-1;
                }
            else
            	{
                trackPos = (int)(size.y/2);
                trackedLine = errorLineNumber - trackPos - 1;
                errorLine = size.y-1;
                if (limit.y < trackedLine + size.y + 1)
                	{
                    trackPos += trackedLine + size.y - limit.y;
                    trackedLine -= trackedLine + size.y - limit.y;
                    }
                if (trackedLine < 0)
                	{
                    trackPos += trackedLine;
                    trackedLine = 0;
                    }
                if (trackPos == errorLine && trackPos != 0)
                	errorLine = 0;
                }

			scrollTo(0,trackedLine);
			setCurPtr(lineStart(lineMove(curPtr, (errorLineNumber - curPos.y-1))), selectMode);
			TDrawBuffer b;
		    ushort color = getColor(0x0201);
			char line[maxLineLength];
			curLine(curPtr/*+FP_OFF(buffer)*/,line);
			line[80] = 0;
			b.moveStr( 0, line , (color & 0xFF00) >> 8);
			writeBuf(0, trackPos, size.x, 1, b);
			if (makeFocused)
			{
			b.moveStr( 0, errorMessage , 0x4F);
			writeBuf(0, errorLine, size.x, 1, b);
			}
			event.what = evBroadcast;
			if (makeFocused) event.message.command = 1531; else event.message.command = 1532;
			event.message.infoPtr = fileName;
			putEvent(event);
			break;

		case 1540:
			if (strstr(fileName, errorFichier) == NULL) return;

			if ( this == clipboard || !strcmp(fileName,"Message") || !strcmp(fileName,"Tags"))
				return;

			if (!registered) reminder(3);
			if (saveBefore)
				for (int a=1; a < 100; a++)
					if (Editor[a] != NULL) Editor[a]->save();

				strcpy(tmpname,SysPath);
				strcat(tmpname,"__TEMP__.$$$");

			out = fopen(tmpname, "wb");

			fwrite( buffer, curPtr, 1, out );
			fwrite( buffer+curPtr+gapLen, bufLen-curPtr, 1, out );

			fclose(out);


			clearEvent(event);
			event.what = evBroadcast;
			event.message.command = 1001;
			strcpy(curfilename,fileName);
			event.message.infoInt = number;
			mainNumber = number;
			return;

		case 1543:
			if (strstr(fileName, errorFichier) == NULL) return;
			if ( this == clipboard || !strcmp(fileName,"Message") || !strcmp(fileName,"Tags"))
			return;

			if (runProcedure) goto suite;

				strcpy(tmpname,SysPath);
				strcat(tmpname,"__TEMP__.$$$");

			out = fopen(tmpname, "wb");

			fwrite( buffer, curPtr, 1, out );
			fwrite( buffer+curPtr+gapLen, bufLen-curPtr, 1, out );

			fclose(out);

			crc = crc32file(tmpname);
			unlink(tmpname);

			if (crcEditor[number] != crc)
				{
                event.what = evCommand;
				runProcedure = True;
				event.message.command = cmCompile;
				putEvent(event);
				return;
				}

			suite:
			runProcedure = False;
			if (!registered) reminder(3);

			clearEvent(event);
			event.what = evBroadcast;
			event.message.command = 1700;
			strcpy(curfilename,fileName);

			return;

		default:
			 return;
		}
	case evKeyDown:
		switch(event.message.command)
		{

		default:
			 return;
		}
	default:
		return;
	}
	clearEvent(event);
}

void TFileEditor::initBuffer()
{
	buffer = new char[bufSize];
}

Boolean TFileEditor::loadFile()
{
	if (emacsHeader)
		createEmacsHeader(fileName);

	ifstream f( fileName, ios::in | ios::binary );
    if( !f )
	{
        setBufLen( 0 );
	return True;
	}
	else
        {
	long fSize = filelength( f.rdbuf()->fd() );
	if( fSize > 0xFFE0L || setBufSize(ushort(fSize)) == False )
            {
            editorDialog( edOutOfMemory );
	    return False;
	    }
        else
	    {
	    if ( fSize > INT_MAX )
            {
               f.read( &buffer[bufSize - ushort(fSize)], INT_MAX );
	       f.read( &buffer[bufSize - ushort(fSize) + INT_MAX],
                                ushort(fSize - INT_MAX) );

		}
			else
	       f.read( &buffer[bufSize - ushort(fSize)], ushort(fSize) );
		if( !f )
                {
                editorDialog( edReadError, fileName );
		return False;
                }
            else
                {
                setBufLen(ushort(fSize));
		return True;
                }
	    }
	}
}

Boolean TFileEditor::save()
{
	Boolean sav;

	if( *fileName == EOS )
	return saveAs();
	else
		{
		sav = saveFile();
		if (emacsHeader)
			processEmacsHeader(fileName);
		}
	return sav;
}

Boolean TFileEditor::saveAs()
{
    char oldName[MAXPATH];
    int a;
    strcpy(oldName,fileName);
    Boolean res = False;

this->makeFirst();

askname:
    if( editorDialog( edSaveAs, fileName ) != cmCancel )
	{
	fexpand( fileName );
	if (access(fileName, 0) == 0)
		{
		a = messageBox(mfWarning | mfYesButton | mfNoButton | mfCancelButton, "\003%s already exists. Overwrite ?", fileName);
		if (a == cmNo) goto askname;
		if (a == cmCancel)
			{
			strcpy(fileName,oldName);
			return res;
			}
		}
	message( owner, evBroadcast, cmUpdateTitle, 0 );
	res = saveFile();
	if (emacsHeader)
		processEmacsHeader(fileName);
	if( isClipboard() == True )
		*fileName = EOS;
	}
    return res;
}

static void writeBlock( ofstream& f, char *buf, unsigned len )
{
    while( len > 0 )
        {
	int l = len < INT_MAX ? len : INT_MAX;
		f.write( buf, l );
        buf += l;
	len -= l;
        }
}

Boolean TFileEditor::saveFile()
{
    char drive[MAXDRIVE];
	char dir[MAXDIR];
    char file[MAXFILE];
	char ext[MAXEXT];

	if( (editorFlags & efBackupFiles) != 0 )
		{
		fnsplit( fileName, drive, dir, file, ext );
		char backupName[MAXPATH];
		fnmerge( backupName, drive, dir, file, backupExt );
		unlink( backupName );
		rename( fileName, backupName );
		}
	ofstream f( fileName, ios::out | ios::binary );

	if( !f )
		{
		editorDialog( edCreateError, fileName );
	return False;
		}
	else
	{
		writeBlock( f, buffer, curPtr );
		writeBlock( f, buffer+curPtr+gapLen, bufLen-curPtr );

		if( !f )
			{
			editorDialog( edWriteError, fileName );
			return False;
			}
		else
			{
			modified = False;
			update(ufUpdate);
			}
		}
	return True;
}

Boolean TFileEditor::setBufSize( ushort newSize )
{
	if( newSize > 0xF000 )
		newSize = 0xFFE0;
    else
        newSize = (newSize + 0x0FFF) & 0xF000;
    if( newSize != bufSize )
        {
        char *temp = buffer;
        if( (buffer = new char[newSize]) == 0 )
            {
			delete temp;
            return False;
            }
		ushort n = bufLen - curPtr + delCount;
        memcpy( buffer, temp, min( newSize, bufSize ) );
        memmove( &buffer[newSize - n], &temp[bufSize - n], n );
        delete temp;
        bufSize = newSize;
	gapLen = bufSize - bufLen;
        }
    return True;
}

void TFileEditor::shutDown()
{
    setCmdState(cmSave, False);
    setCmdState(cmSaveAs, False);
    TEditor::shutDown();
}

void TFileEditor::updateCommands()
{
    TEditor::updateCommands();
	setCmdState(cmSave, True);
    setCmdState(cmSaveAs, True);
}

Boolean TFileEditor::valid( ushort command )
{
    if( command == cmValid )
	return isValid;
    else
        {
        if( modified == True )
            {
            int d;
            if( *fileName == EOS )
                d = edSaveUntitled;
            else
		d = edSaveModify;

            switch( editorDialog( d, fileName ) )
                {
				case cmYes:
		    return save();
                case cmNo:
					modified = False;
                    return True;
		case cmCancel:
                    return False;
		}
	    }
        }
    return True;
}

void TFileEditor::write( opstream& os )
{
    TEditor::write( os );
    os << number;
	os.writeString( fileName );
    os << selStart << selEnd << curPtr;
    os.writeWord((crcEditor[number] & 0xFFFF0000L) >> 16);
	os.writeWord(crcEditor[number] & 0xFFFF);
}

void *TFileEditor::read( ipstream& is )
{
	TEditor::read( is );
	is >> number;
	is.readString( fileName, sizeof( fileName ) );
	if( isValid )
	{
	isValid = loadFile();
	errorDisplayed = False;
	Editor[number] = this;
	strcpy(curfilename,fileName);
	ushort sStart, sEnd, curs;
	is >> sStart >> sEnd >> curs;
	if( isValid && sEnd <= bufLen )
	    {
	    setSelect( sStart, sEnd, Boolean(curs == sStart) );
	    trackCursor( True );
		}
	}
    crcEditor[number] = is.readWord();
	crcEditor[number] = (crcEditor[number] << 16);
	crcEditor[number] += is.readWord();
    Editor[number] = this;
    return this;
}

TStreamable *TFileEditor::build()
{
    return new TFileEditor( streamableInit );
}

TStreamableClass RFileEditor( TFileEditor::name,
			      TFileEditor::build,
			      __DELTA(TFileEditor)
                          );

TFileEditor::TFileEditor( StreamableInit ) : TEditor( streamableInit )
{
}

