#pragma option lst
#pragma option dbg
#jumptomain NONE

#include "32rtm.hmm"
#include "objects.hmm"
#include "mytypes.hmm"

#define version "v1.0beta4"
:char strbar=" ~F5~ vert/horiz  ~N~ togglenums  ~+~/~-~ next/prev  ~ESC~ exit            │ VC "version;

#define accuratesearch

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

char* srcfilename;
char* dstfilename;
dword srcfile;
dword dstfile;
dword srcfilesize;
dword dstfilesize;
TCollection srcfilelines;//строки файла
TCollection dstfilelines;
TCollection srcfilenums; //номера строк
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;      //список различающихся строчек
dword srcfilelinks;     //указатель на предыдущую строку с тем же первым символом
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.hmm"

char hellostring="
┌────────────────────┬─────────
│visual text comparer│"version"
│by den po            jdp@bk.ru
├─────────────────     ────────
"z;

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

  printf(#hellostring);
  parsecmdline();
  IF(argc<2)printf("│give me filenames\n",argc);
  else
  {
    srcfilename=argv[4];
    printf( "│loadin' %-19.19s ", srcfilename );
    srcfile=-1;
    EBX=CreateFile(srcfilename,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,0,0);
    IF(EBX!=-1)
    {
      srcfilesize=GetFileSize(EBX,0);
      srcfile=GlobalAlloc(GMEM_FIXED,srcfilesize+1);
      IF(!ReadFile(EBX,srcfile,srcfilesize,ESP,0))
      {
        GlobalFree(srcfile);
        srcfile=-1;
      }ELSE
      {
        DSBYTE[srcfile+srcfilesize]=0;
      }
      CloseHandle(EBX);
    }
    IF(srcfile==-1)printf(":(\n");else
    {
      printf("ok\n");

      dstfilename=argv[8];
      dstfile=-1;
      printf( "│loadin' %-19.19s ", dstfilename );
      EBX=CreateFile(dstfilename,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,0,0);
      IF(EBX!=-1)
      {
        dstfilesize=GetFileSize(EBX,0);
        dstfile=GlobalAlloc(GMEM_FIXED,dstfilesize+1);
        IF(!ReadFile(EBX,dstfile,dstfilesize,ESP,0))
        {
          GlobalFree(dstfile);
          dstfile=-1;
        }ELSE
        {
          DSBYTE[dstfile+dstfilesize]=0;
        }
        CloseHandle(EBX);
      }
      IF(dstfile==-1)printf(":(\n");else
      {
        printf("ok\n");
        printf("│parsing                     ");
        srcfilelines.TCollection(srcfilesize/40,8192);
        dstfilelines.TCollection(dstfilesize/40,8192);
        //заполняем указатели на строчки
        getstrings(srcfile,srcfilesize,#srcfilelines);
        getstrings(dstfile,dstfilesize,#dstfilelines);
        srcfilenums.TCollection(srcfilelines.Count,1000);
        dstfilenums.TCollection(dstfilelines.Count,1000);

        srcfilelinks=GlobalAlloc(GMEM_FIXED,srcfilelines.Count*4);
        dstfilelinks=GlobalAlloc(GMEM_FIXED,dstfilelines.Count*4);

        //заполняем ссылки на следующие строки с теми же первыми символами
        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;
        }
        printf("ok\n");

        diffs.TSimpleCollection();

        /////////////////////////////////////////////////////////////
        printf( "│sorting     0%%[░░░░░░░░░░]100%%\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" );
        k = 0;//процессбар :)
        bsp = 0;
        bdp = 0;
        while( bsp < srcfilelines.Count ) || ( bdp < dstfilelines.Count )
        {
          MAINLOOP:
          //draw status
//          j = srcfilelines.Count+dstfilelines.Count;
//          i = bsp+bdp*10/j;
j=srcfilelines.Count;
IF(j<dstfilelines.Count)j=dstfilelines.Count;
i=dstfilenums.Count+1*9/j;
//printf("%d %d\n",dstfilenums.Count,dstfilelines.Count);
          WHILE( k <= i ){ printf( "▓" ); k++; }
          ////////////////////////////////////////////////////////
          //если dst строчки иссякли
          IF(bdp>=dstfilelines.Count)
          {
            diffs.Insert(srcfilenums.Count);
            srcfilenums.Insert(bsp); dstfilenums.Insert(-1); bsp++;
            continue;
          }
          //если src строчки иссякли
          IF(bsp>=srcfilelines.Count)
          {
            diffs.Insert(dstfilenums.Count);
            srcfilenums.Insert(-1); dstfilenums.Insert(bdp); bdp++;
            continue;
          }
          ////////////////////////////////////////////////////////
#ifndef accuratesearch
          IF(!_lstrcmp(srcfilelines.At(bsp),dstfilelines.At(bdp)))
          { //равные строчки
            @addpair:
            srcfilenums.Insert(bsp); bsp++;
            dstfilenums.Insert(bdp); bdp++;
            continue;//goto MAINLOOP;
          }
#endif
          ////////////////////////////////////////////////////////
          bsc=0;
          p=dstfilelines.At(bdp);//строчка текущей позиции dst
          s1=DSBYTE[p];
          bs=bsp+1;//найденная строчка src  bsc - число совпадающих
          while( bs != -1 )//нет следующей строки начинающейся тем же символом
             &&(bs-bsp<=maxcmpoffset)//проверка на глубину 100 строк
             &&(bs<srcfilelines.Count)
          {
            s2=srcfilelines.At(bs);
            IF(!_lstrcmp(p,s2))
            {//строка найдена
              bsc=1;
              WHILE(bsc<maxcmpstrings)//считаем число совпадающих строк
                 &&(bdp+bsc<dstfilelines.Count)
                 &&(bs+bsc<srcfilelines.Count)
                 &&(!_lstrcmp(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);//строчка текущей позиции src
          s1=DSBYTE[p];
          bd=bdp+1;//найденная строчка dst  bdc - число совпадающих
          while( bd != -1 )//нет следующей строки начинающейся тем же символом
             &&(bd-bdp<=maxcmpoffset)//проверка на глубину 100 строк
             &&(bd<dstfilelines.Count)
          {
            s2=dstfilelines.At(bd);
            IF(!_lstrcmp(p,s2))
            {
              bdc=1;
              WHILE(bdc<maxcmpstrings)//считаем число совпадающих строк
                 &&(bsp+bdc<srcfilelines.Count)
                 &&(bd+bdc<dstfilelines.Count)
                 &&(!_lstrcmp(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(bdc<bsc)
           IF(bs-bsp<bsc)
           ||(bsc>=mincmpstrings)
          {
            WHILE(bsp<bs){
              diffs.Insert(srcfilenums.Count);
              srcfilenums.Insert(bsp); dstfilenums.Insert(-1); bsp++;
            }
            CONTINUE;
          }
#ifdef accuratesearch
          IF(_lstrcmp(srcfilelines.At(bsp),dstfilelines.At(bdp)))diffs.Insert(srcfilenums.Count);
          //равные строчки
          srcfilenums.Insert(bsp); bsp++;
          dstfilenums.Insert(bdp); bdp++;
          CONTINUE;//goto MAINLOOP;
#else
          diffs.Insert(srcfilenums.Count);
          goto addpair;
#endif
        }

        GlobalFree(srcfilelinks);
        GlobalFree(dstfilelinks);

        printf("\n");
        IF(!diffs.Count)printf("│nothin' to compare :)\n");
                   ELSE ifinit();

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