//visual text comparer 
//by den po - jdp@bk.ru

#define MEMSIZE 4096 * 60
#include "../lib/io.h"
#include "../lib/strings.h"
#include "../lib/obj/console.h"
IO io1, io2;

#define MAX_PATH 260

#include "32user32.h"
#include "objects.h"
#include "diff_gui.h"

#define mincmpstrings 2
#define maxcmpstrings 10
#define maxcmpoffset  100

char window_title[] = "Kolibri Diff tool (Visual Text Comparer)";

char* srcfilename;
char* dstfilename;
dword srcfile;
dword dstfile;
dword srcfilesize;
dword dstfilesize;
TCollection srcfilelines; //file lines
TCollection dstfilelines;
TCollection srcfilenums; //lines numbers
TCollection dstfilenums;

struct TSimpleCollection:TSortedCollection
{
  TSimpleCollection();
  int  Compare( int Key1, Key2 );
};
int TSimpleCollection::Compare( int Key1, Key2 ){
  return Key1-Key2;
}
TSimpleCollection::TSimpleCollection():TSortedCollection(1000,1000);
{
  comparemethod=#Compare;
}

TSimpleCollection diffs;   //list of differing strings
dword srcfilelinks;     //pointer to the previos line with the same first symbol
dword dstfilelinks;     //

getstrings(dword srcfile,srcfilesize,srcfilelines){
  ECX=srcfilesize;
  ESI=srcfile;
  WHILE(ECX){
	$push ECX,ESI
	EAX=srcfilelines; 
	EAX.TCollection.Insert(ESI);
	$pop ESI,ECX
	WHILE(ECX){
	  $cld $lodsb ECX--;
	  if(AL==0x0D)||(AL==0x0A)DSBYTE[ESI-1]=0;
	  if(AL==0x0D)&&(DSBYTE[ESI]==0x0A){$lodsb;ECX--;}
	  if(AL==0x0D)||(AL==0x0A)BREAK;
	}
  }
}

#include "if.h"

bool getparam() 
{
	int i, param_len = strlen(#param);
	if (param[0]=='"') {
		for (i=1; i<param_len; i++) if (param[i]=='"') param[i]=NULL;
		srcfilename = #param + 1;
		dstfilename = #param + strlen(#param) + 3;
		return true;
	}
	notify("'Wrong params specified. Use next format:\nAPPPATH \"PARAM1\" \"PARAM2\"' -E");
	return false;
}

main(){
	if (param[0]) getparam();
	if (!srcfilename) || (!dstfilename) gui(); else console();
}

console() {
	int i;
	int p;
	int bs,bd,bsc,bdc,bsp,bdp;
	int cache[256];
	char s1;
	int s2;

	srcfile = io1.read(srcfilename);
	dstfile = io2.read(dstfilename);

	srcfilesize = io1.FILES_SIZE;
	dstfilesize = io2.FILES_SIZE;

	if (!srcfile) die("'First file not found' -E"); 
	if (!dstfile) die("'Second file not found' -E");
	
	srcfilelines.TCollection(srcfilesize/40,8192);
	dstfilelines.TCollection(dstfilesize/40,8192);
	//fill line pointers
	getstrings(srcfile,srcfilesize,#srcfilelines);
	getstrings(dstfile,dstfilesize,#dstfilelines);
	srcfilenums.TCollection(srcfilelines.Count,1000);
	dstfilenums.TCollection(dstfilelines.Count,1000);

	srcfilelinks=malloc(srcfilelines.Count*4);
	dstfilelinks=malloc(dstfilelines.Count*4);

	//fill links on the next strings with the same first symbols
	FillMemory(#cache,sizeof(cache),-1); i=srcfilelines.Count;
	WHILE(i){
	  i--;
	  EBX=srcfilelines.At(i); EBX=DSBYTE[EBX];
	  DSDWORD[i<<2+srcfilelinks]=cache[EBX*4];
	  cache[EBX*4]=i;
	}
	FillMemory(#cache,sizeof(cache),-1); i=dstfilelines.Count;
	WHILE(i){
	  i--;
	  EBX=dstfilelines.At(i); EBX=DSBYTE[EBX];
	  DSDWORD[i<<2+dstfilelinks]=cache[EBX*4];
	  cache[EBX*4]=i;
	}

	diffs.TSimpleCollection();

	while( bsp < srcfilelines.Count ) || ( bdp < dstfilelines.Count )
	{
	  ////////////////////////////////////////////////////////
	  bsc=0;
	  p=dstfilelines.At(bdp);//current dst position
	  s1=DSBYTE[p];
	  bs=bsp+1;//found src line, bsc - number of matched
	  while( bs != -1 )//no next line starting with the same symbols
		 &&(bs-bsp<=maxcmpoffset)//check for 100 lines depth
		 &&(bs<srcfilelines.Count)
	  {
		s2=srcfilelines.At(bs);
		if(!strcmp(p,s2))
		{//line found
		  bsc=1;
		  WHILE(bsc<maxcmpstrings)//counting number of matching lines
			 &&(bdp+bsc<dstfilelines.Count)
			 &&(bs+bsc<srcfilelines.Count)
			 &&(!strcmp(dstfilelines.At(bdp+bsc),srcfilelines.At(bs+bsc)))bsc++;
		  BREAK;
		}
		if(DSBYTE[s2]==s1)bs=DSDWORD[bs<<2+srcfilelinks];else bs++;
	  }
	  bdc=0;
	  p=srcfilelines.At(bsp);//current src position
	  s1=DSBYTE[p];
	  bd=bdp+1;//found dst line, bsc - number of matched
	  while( bd != -1 )//no next line starting with the same symbols
		 &&(bd-bdp<=maxcmpoffset)//check for 100 lines depth
		 &&(bd<dstfilelines.Count)
	  {
		s2=dstfilelines.At(bd);
		if(!strcmp(p,s2))
		{
		  bdc=1;
		  WHILE(bdc<maxcmpstrings)//counting number of matching lines
			 &&(bsp+bdc<srcfilelines.Count)
			 &&(bd+bdc<dstfilelines.Count)
			 &&(!strcmp(srcfilelines.At(bsp+bdc),dstfilelines.At(bd+bdc)))bdc++;
		  BREAK;
		}
		if(DSBYTE[s2]==s1)bd=DSDWORD[bd<<2+dstfilelinks];else bd++;
	  }

	  if(bsc<bdc)
	   if(bd-bdp<bdc)
	   ||(bdc>=mincmpstrings)
	  {
		WHILE(bdp<bd){
		  diffs.Insert(srcfilenums.Count);
		  srcfilenums.Insert(-1); dstfilenums.Insert(bdp); bdp++;
		}
		continue;
	  }

	  if(strcmp(srcfilelines.At(bsp),dstfilelines.At(bdp)))diffs.Insert(srcfilenums.Count);
	  //lines are equal
	  srcfilenums.Insert(bsp); bsp++;
	  dstfilenums.Insert(bdp); bdp++;
	  CONTINUE;
	}

	free(srcfilelinks);
	free(dstfilelinks);

	if(!diffs.Count)
		notify("'Nothing to compare' -E"); 
	else ifinit();

	diffs.DeleteAll();        delete diffs;
	dstfilenums.DeleteAll();  delete dstfilenums;
	srcfilenums.DeleteAll();  delete srcfilenums;
	dstfilelines.DeleteAll(); delete dstfilelines;
	srcfilelines.DeleteAll(); delete srcfilelines;
	free(dstfile);
	free(srcfile);
}