/*********************************************************************
 * HexCompare.c
 *
 * Written by Lane Roathe and Jeff Smith
 * Copyright 1995, Ideas From the Deep
 *********************************************************************
 * Ideas:
 *
 * A. Auto spacing of buttons for width of window...
 * B. Add way of changing the offset of either window so differences
 *		can still be found if one word inserted, or something?
 *********************************************************************/

#include "HexEdit.h"

// variables
short gCompSizeMode=0,gCompTypeMode=0;

extern short CompareFlag;
extern WindowPtr CompWind1,CompWind2;
extern unsigned char	gSearchBuffer[256];


//Actual compare guts here

void PerformTextDifferenceCompare(EditWindowPtr dWin, EditWindowPtr dWin2)
{
	short		ch,ch2,matchIdx;
	long		addr,addr2,matchAddr;

	MySetCursor(C_Watch);

	// Search in Direction gSearchDir
	// for text gSearchBuffer

	if (gSearchDir == 0) {
		addr = dWin->endSel;
		addr2 = dWin2->endSel;
	}
	else {
		addr = dWin->startSel - 1;
		addr2 = dWin2->startSel - 1;
		if (addr < 0)
			return;
		if (addr2 < 0)
			return;
	}
	
	//1 = byte, 2 = words, 4 = longs, ect...
	gSearchBuffer[0]=gCompSizeMode+1;
	if(gCompSizeMode==CM_Long) gSearchBuffer[0] += 1;


	matchIdx = 0;
	while (1) {
		ch = GetByte(dWin, addr);
		ch2 = GetByte(dWin2, addr2);
		if (ch != ch2) {				//change this if you want compare for similarities
			if (matchIdx == 0) matchAddr = addr;
			++matchIdx;
			if (matchIdx >= gSearchBuffer[0])
				goto Success;
			++addr;
			++addr2;
			if (addr == dWin->fileSize) {
				matchIdx = 0;
				addr = matchAddr;
			}
			else
				continue;
		}
		else {
			if (matchIdx) {
				matchIdx = 0;
				addr = matchAddr;
			}
		}
		if (gSearchDir == 0) {
			++addr;
			++addr2;
			if (addr2 == dWin2->fileSize)
				goto Failure;
			if (addr == dWin->fileSize)
				goto Failure;
		}
		else {
			--addr;
			--addr2;
			if (addr < 0)
				goto Failure;
			if (addr2 < 0)
				goto Failure;
		}
	}

Failure:
	SysBeep(1);
	MySetCursor(C_Arrow);
	return;

Success:
	SelectWindow((WindowPtr) dWin);
	dWin->startSel = matchAddr;
	dWin->endSel = dWin->startSel + gCompSizeMode + 1;
	if(gCompSizeMode==CM_Long) dWin->endSel += 1;
	ScrollToSelection(dWin, dWin->startSel, true, true);

	SelectWindow((WindowPtr) dWin2);
	dWin2->startSel = matchAddr;
	dWin2->endSel = dWin2->startSel + gCompSizeMode + 1;
	if(gCompSizeMode==CM_Long) dWin2->endSel += 1;
	ScrollToSelection(dWin2, dWin2->startSel, true, true);

	MySetCursor(C_Arrow);
}

//Actual compare guts here too

void PerformTextMatchCompare(EditWindowPtr dWin, EditWindowPtr dWin2)
{
	short		ch,ch2,matchIdx;
	long		addr,addr2,matchAddr;

	MySetCursor(C_Watch);

	// Search in Direction gSearchDir
	// for text gSearchBuffer

	if (gSearchDir == 0) {
		addr = dWin->endSel;
		addr2 = dWin2->endSel;
	}
	else {
		addr = dWin->startSel - 1;
		addr2 = dWin2->startSel - 1;
		if (addr < 0)
			return;
		if (addr2 < 0)
			return;
	}
	
	//1 = byte, 2 = words, 4 = longs, ect...
	gSearchBuffer[0]=gCompSizeMode+1;
	if(gCompSizeMode==CM_Long) gSearchBuffer[0] += 1;

	matchIdx = 0;
	while (1) {
		ch = GetByte(dWin, addr);
		ch2 = GetByte(dWin2, addr2);
		if (ch == ch2) {
			if (matchIdx == 0) matchAddr = addr;
			++matchIdx;
			if (matchIdx >= gSearchBuffer[0])
				goto Success;
			++addr;
			++addr2;
			if (addr == dWin->fileSize) {
				matchIdx = 0;
				addr = matchAddr;
			}
			else
				continue;
		}
		else {
			if (matchIdx) {
				matchIdx = 0;
				addr = matchAddr;
			}
		}
		if (gSearchDir == 0) {
			++addr;
			++addr2;
			if (addr2 == dWin2->fileSize)
				goto Failure;
			if (addr == dWin->fileSize)
				goto Failure;
		}
		else {
			--addr;
			--addr2;
			if (addr < 0)
				goto Failure;
			if (addr2 < 0)
				goto Failure;
		}
	}

Failure:
	SysBeep(1);
	MySetCursor(C_Arrow);
	return;

Success:
	SelectWindow((WindowPtr) dWin);
	dWin->startSel = matchAddr;
	dWin->endSel = dWin->startSel + gCompSizeMode + 1;
	if(gCompSizeMode==CM_Long) dWin->endSel += 1;
	ScrollToSelection(dWin, dWin->startSel, true, true);

	SelectWindow((WindowPtr) dWin2);
	dWin2->startSel = matchAddr;
	dWin2->endSel = dWin2->startSel + gCompSizeMode + 1;
	if(gCompSizeMode==CM_Long) dWin2->endSel += 1;
	ScrollToSelection(dWin2, dWin2->startSel, true, true);

	MySetCursor(C_Arrow);
}


// main handler for the compare of the contents of two windows

void DoCompare(void)
{
	GrafPtr		oldPort;
	DialogPtr	pDlg;
	Handle 		iHandle;
	short 		iType;
	Rect 		iRect;
	EventRecord	theEvent;
	WindowPeek	frontWindow;

	//open the first window
here:
	CompareFlag=1;
	iType = AskEditWindow();
	if(iType==-1) return;		//if Cancel, exit
	//open the second window
	CompareFlag=2;
	iType = AskEditWindow();
	if(iType==-1) {				//if Cancel, go back to first dialog chose
		frontWindow = (WindowPeek) FrontWindow();	//kill first dialog
		CloseEditWindow((WindowPtr) frontWindow);
		goto here;
	}
	//put up dialog and let user interact

	GetPort(&oldPort);
	//make dialog
	pDlg = GetNewDialog (131, 0L, (WindowPtr)-1);
	MoveWindow(pDlg,22,gMaxHeight-64+8,true);				//move dialog to 14,400?
//	SizeWindow(pDlg,CompWind1->portRect.right-16,48,true);		//size dialog to 14,400?
	ShowWindow(pDlg);
	SetPort(pDlg);
	//ring around Ok
	GetDItem (pDlg, 1, &iType, &iHandle, &iRect );
	PenSize( 3,3 );
	InsetRect( &iRect, -4,-4);
	FrameRoundRect( &iRect, 16,16 );
	//show the contents of the windows
	DrawPage((EditWindowPtr)CompWind1);
	UpdateOnscreen(CompWind1);
	DrawPage((EditWindowPtr)CompWind2);
	UpdateOnscreen(CompWind2);
			
	//handle event processing
	do {
		WaitNextEvent(everyEvent,&theEvent,0L,NULL);
		iType=0;
		if (IsDialogEvent(&theEvent)) {
			DialogSelect(&theEvent, &pDlg, &iType);
		
			if(iType==1) {			//handle find forward here
				gSearchDir = 0; 	//set flag: forward
				if(gCompTypeMode == CM_Match)
					PerformTextMatchCompare((EditWindowPtr)CompWind1,(EditWindowPtr)CompWind2);
				else
					PerformTextDifferenceCompare((EditWindowPtr)CompWind1,(EditWindowPtr)CompWind2);
				SelectWindow((WindowPtr) pDlg);
			}
			if(iType==3) {			//handle find backward here
				gSearchDir = 1; 	//set flag: backward
				if(gCompTypeMode == CM_Match)
					PerformTextMatchCompare((EditWindowPtr)CompWind1,(EditWindowPtr)CompWind2);
				else
					PerformTextDifferenceCompare((EditWindowPtr)CompWind1,(EditWindowPtr)CompWind2);
				SelectWindow((WindowPtr) pDlg);
			}
		}
	} while ( (iType != 2) && (iType != 4) );	//2 = Done, 4 = Drop out and edit
			
	//close all 3 windows, unless we want to leave them to edit...
	DisposDialog(pDlg);
	SetPort(oldPort);
	if(iType == 2) {
		frontWindow = (WindowPeek) FrontWindow();
		CloseEditWindow((WindowPtr) frontWindow);
		frontWindow = (WindowPeek) FrontWindow();
		CloseEditWindow((WindowPtr) frontWindow);
	}
	//restore flag
	CompareFlag=false;
}

// handler for the options dialog for compare.

void DoComparePref(void)
{
	GrafPtr		oldPort;
	DialogPtr	pDlg;
	Handle 		iHandle;
	short 		iType,radio1,radio2;
	Rect 		iRect;

	GetPort(&oldPort);
	//make dialog
	pDlg = GetNewDialog (132, 0L, (WindowPtr)-1);
	SetPort(pDlg);

	radio1 = gCompSizeMode = gPrefs.compSize;
	radio2 = gCompTypeMode = gPrefs.compType;

	//init radio buttons to current settings...
	SetControl(pDlg, CP_Bytes, gCompSizeMode == CM_Byte);
	SetControl(pDlg, CP_Words, gCompSizeMode == CM_Word);
	SetControl(pDlg, CP_Longs, gCompSizeMode == CM_Long);

	SetControl(pDlg, CP_Different, gCompTypeMode == CM_Different);
	SetControl(pDlg, CP_Match, gCompTypeMode == CM_Match);
	
	//ring around Ok
	GetDItem (pDlg, CP_Done, &iType, &iHandle, &iRect );
	PenSize( 3,3 );
	InsetRect( &iRect, -4,-4);
	FrameRoundRect( &iRect, 16,16 );
			
	//handle event processing
	do {
		ModalDialog(NULL, &iType);
		switch (iType) {
		//handle each radio button...
		case CP_Bytes:
			radio1 = CM_Byte;
			SetControl(pDlg, CP_Bytes, 1);
			SetControl(pDlg, CP_Words, 0);
			SetControl(pDlg, CP_Longs, 0);
			break;
		case CP_Words:
			radio1 = CM_Word;
			SetControl(pDlg, CP_Bytes, 0);
			SetControl(pDlg, CP_Words, 1);
			SetControl(pDlg, CP_Longs, 0);
			break;
		case CP_Longs:
			radio1 = CM_Long;
			SetControl(pDlg, CP_Bytes, 0);
			SetControl(pDlg, CP_Words, 0);
			SetControl(pDlg, CP_Longs, 1);
			break;
			
		case CP_Different:
			radio2 = CM_Different;
			SetControl(pDlg, CP_Different, 1);
			SetControl(pDlg, CP_Match, 0);
			break;
		case CP_Match:
			radio2 = CM_Match;
			SetControl(pDlg, CP_Different, 0);
			SetControl(pDlg, CP_Match, 1);
			break;
		}
	} while ( (iType != CP_Done) && (iType != CP_Cancel) );
	
	if(iType==CP_Done) {
		//change flags based on which one is selected
		gCompSizeMode = radio1;
		gCompTypeMode = radio2;
		//change vars in Prefs...
		gPrefs.compSize = radio1;
		gPrefs.compType = radio2;
	}		
	//close window
	DisposDialog(pDlg);
	SetPort(oldPort);
}
