#include <mine/toolkit.h>
#include <dos/dos.h>
#include <dos/rdargs.h>
#include <exec/memory.h>

#include <proto/asyncio.h>
#include <proto/dos.h>
#include <proto/exec.h>

#include <string.h>

ulong __asm GetSize       (register __a0 ubyte *, register __d0 ulong);
bool  __asm CheckLine     (register __a0 ubyte *, register __a1 aptr, register __d0 ulong);
ulong __asm HashLines     (register __a3 ubyte *, register __a5 aptr, register __a2 ulong *, register __a0 aptr, register __d1 ulong, register __d2 ulong, register __d3 ulong);
void  __asm FreeVecPooled (register __a0 aptr   , register __a1 aptr);
aptr  __asm AllocVecPooled(register __a0 aptr   , register __d0 ulong);

#define CASE      1
#define EQU       2
#define STRIP     4

struct MyArgs
{
  char  *file_cmp;
  char  *file_old;
  char  *file_inc;
  char  *file_exc;
  ulong *buf;
  ulong  strip;
  ulong  usecase;
  ulong  equ;
};

static const char ver[]     = "$VER: ListCmp 1.1 ("__DATE__")";
static const char template[]= "FILE/A,FILE/A,I=INCLUDE/K,E=EXCLUDE/K,B=BUFSIZE/N/K,STRIP=STRIPSUFFIX/S,CASE/S,EQU/S";

#define alloc_pool(x) AllocVecPooled(pool,x)
#define  free_pool(x)  FreeVecPooled(pool,x)

ulong main(void)
{
struct RDArgs     *rdargs      = 0;
struct MyArgs      args        = { 0,0,0,0,0,0,0,0 };
struct Library    *AsyncIOBase = 0;
struct AsyncFile  *cmp_fh      = 0,
                  *old_fh      = 0,
                  *inc_fh      = 0,
                  *exc_fh      = 0;
struct DosLibrary *DOSBase     = 0;
aptr               pool        = 0,
                   hash        = 0;
ulong              hl_ret      = 0,
                   rd_size        ,
                   rd_old         ,
                   error       = 0,
                   mode           ,
                   hl_lines    = 0,
                   line_size      ,
                   lines_inc   = 0,
                   lines_exc   = 0,
                   buf_read       ,
                   buf_size       ;
ubyte             *buf_ptr     = 0,
                  *buf_now        ,
                  *line_ptr       ;

  if ((DOSBase     = OpenLibrary("dos.library",37))     == 0 OR
      (AsyncIOBase = OpenLibrary("asyncio.library",37)) == 0) {
    error = 10;
  } else if ((rdargs=ReadArgs(template,(LONG *)&args,NULL)) == 0) {
    error = IoErr();
  } else {
    buf_size = (args.buf) ? ((*args.buf > 256) ? *args.buf : 256) : 8192;
    mode     = (args.usecase) ? CASE  : 0;
    mode    |= (args.equ    ) ? EQU   : 0;
    mode    |= (args.strip  ) ? STRIP : 0;
    if ((pool = CreatePool(MEMF_ANY,buf_size,buf_size)) == 0) {
      error = 10;
    } else if (hash = alloc_pool(256*1024)) {
      memset(hash,0,256*1024);
      if ((buf_ptr = alloc_pool(buf_size)) == 0) {
        error = 10;
      } else if ((cmp_fh = OpenAsync(args.file_cmp,MODE_READ,buf_size)) == 0) {
        error = IoErr();
      } else {
        /*** read and hash ***/
        while ( rd_size = ReadAsync(cmp_fh,buf_ptr,buf_size) )
        {
          if (rd_size == -1) break;
          if ((hl_ret = HashLines(buf_ptr,hash,&hl_lines,pool,rd_size,mode,hl_ret)) == -1) break;
          Printf("\rHashing, %ld lines analyzed...",hl_lines);
          if (SetSignal(0,0) & SIGBREAKF_CTRL_C) {
            hl_ret = -1;
            SetIoErr(5);
            break;
          }
        }
        Printf("\rDone   , %ld lines analyzed...\n",hl_lines);
        if (rd_size == -1 OR hl_ret == -1) {
          if ((error = IoErr()) == 0) error = 10;
        } else {
          CloseAsync(cmp_fh); cmp_fh = 0;
          if ((old_fh = OpenAsync(args.file_old,MODE_READ,buf_size)) == 0) {
            error = IoErr();
          } else {
            if (args.file_inc) {
              if ((inc_fh = OpenAsync(args.file_inc,MODE_WRITE,buf_size)) == 0) {
                error = IoErr();
              }
            }
            if (args.file_exc) {
              if ((exc_fh = OpenAsync(args.file_exc,MODE_WRITE,buf_size)) == 0) {
                error = IoErr();
              }
            }
            if (error == 0) {
              /*** cmp files, this will fail if line cannot fit in buffer ***/
              buf_now  = buf_ptr;
              buf_read = buf_size;
              rd_old   = 0;
              while ( rd_size = ReadAsync(old_fh,buf_now,buf_read) )
              {
                if (rd_size == -1) {
                  error = IoErr();
                  break;
                } else {
                  line_ptr = buf_ptr;
                  rd_size += rd_old;
                  rd_old   = 0;
                  while (rd_size)
                  {
                    if ((line_size = GetSize(line_ptr,rd_size)) == 0) {
                      if ((buf_read = buf_size - rd_size) == 0) {
                        error = 20;
                        break;
                      }
                      CopyMem(line_ptr,buf_ptr,rd_size);
                      buf_now = &buf_ptr[rd_size];
                      rd_old  = rd_size;
                      rd_size = 0;
                    } else {
                      if (CheckLine(line_ptr,hash,mode)) {
                        lines_inc++;
                        if (inc_fh) {
                          if (WriteAsync(inc_fh,line_ptr,line_size) == -1) {
                            error = IoErr();
                            break;
                          }
                        }  
                      } else {
                        lines_exc++;
                        if (exc_fh) {
                          if (WriteAsync(exc_fh,line_ptr,line_size) == -1) {
                            error = IoErr();
                            break;
                          }
                        }  
                      }
                      line_ptr = &line_ptr[line_size];
                      if ((rd_size -= line_size) == 0) {
                        buf_now  = buf_ptr;
                        buf_read = buf_size;
                        rd_old   = 0;
                      }
                    }
                  }
                  Printf("\rMatched: %ld, unmatched: %ld, total: %ld",lines_inc,lines_exc,lines_inc+lines_exc);
                  if (SetSignal(0,0) & SIGBREAKF_CTRL_C) { SetIoErr(5); break; }
                }
              }
              Printf("\rMatched: %ld, unmatched: %ld, total: %ld\n",lines_inc,lines_exc,lines_inc+lines_exc);
            }
          }
        }
      }
    }
  }

  if (DOSBase) {
    if (error > 20 ) {
      PrintFault(error,"ListCmp");
    } else {
      if (error ==  5) PutStr("ListCmp: **CTRL-C\n");
      if (error == 10) PutStr("ListCmp: Not enough memory\n");
      if (error == 20) PutStr("ListCmp: Line too long to fit buffer\n");
    }
    if (cmp_fh ) CloseAsync(cmp_fh);
    if (old_fh ) CloseAsync(old_fh);
    if (inc_fh ) CloseAsync(inc_fh);
    if (exc_fh ) CloseAsync(exc_fh);
    if (pool   ) DeletePool(pool);
    if (rdargs ) FreeArgs(rdargs);
    if (DOSBase) CloseLibrary(DOSBase);
  }
  if (AsyncIOBase) CloseLibrary(AsyncIOBase);
  return(error);
}
