#include    "defs.h"

#define	NIL	(-1)

#define	HASH_MAX	(1024)
#define	HASH_MSK	(HASH_MAX - 1)

static	short	*node = NULL;
static	ulong	*tab = NULL;

static	int	hash(char *p)
{
    int     hs;

    hs = *(p++); hs *= 31;
    hs += *(p++); hs *= 31;
    hs += *(p++);

    return (hs & HASH_MSK);
}
static	int	encode(char *buf, int len, FILE *fp)
{
    int     i, n, a, b, p;
    int     cnt, pos, wrt;
    char    tmp[8 * 2 + 1];

    cnt = 0;
    pos = 1;
    tmp[0] = 0;
    b = 0;
    wrt = 0;

    for ( n = 2048 ; n < (2048 + HASH_MAX) ; n++ )
	node[n] = NIL;

    while ( b < len ) {
	a = 1;
	if ( (len - b) >= 3 ) {
	    for ( i = node[2048 + hash(buf + b)] ; i != NIL ; i = node[i] ) {
		if ( buf[i + a] != buf[b + a] )
		    continue;
	        for ( n = 0 ; n < 34 && (b + n) < len && 
			buf[i + n] == buf[b + n] ; n++ )
		    ;
	        if ( n > a ) {
		    a = n;
		    p = i;
	            if ( n >= 34 || (b + n) >= len )
		        break;
	        }
	    }
	}

	if ( a > 2 ) {
	    n = (p << 5) | (a - 3);
	    tmp[pos++] = n >> 8;
	    tmp[pos++] = n & 0xff;
	} else {
	    tmp[0] |= (1 << cnt);
	    tmp[pos++] = buf[b];
	    a = 1;
	}

	if ( a >= 34 || (b + a) >= 2048 ) {
	    b += a;

	} else {
	    while ( a-- > 0 ) {
	        n = 2048 + hash(buf + b);
	        node[b] = node[n];
	        node[n] = b++;
	    }
	}

	if ( ++cnt >= 8 ) {
	    fwrite(tmp, 1, pos, fp);
	    wrt += pos;
	    cnt = 0;
	    pos = 1;
	    tmp[0] = 0;
	}
    }

    if ( cnt > 0 ) {
	fwrite(tmp, 1, pos, fp);
	wrt += pos;
    }

    return wrt;
}
int	compress(int flag, char *file)
{
    int     i, n, blk;
    ulong   top, size, pos, block;
    ulong   skip_size = 0L;
    int     skip_max = 0;
    ulong   skip_sta[16];
    ulong   skip_end[16];
    FILE    *fp;
    uchar   *p;

    if ( (node = (short *)malloc(sizeof(short) * (2048 + HASH_MAX))) == NULL ||
         (tab  = (ulong *)malloc(sizeof(ulong) * 256)) == NULL )
	return ERR;

#ifdef	MSDOS
    if ( (fp = fopen(file, "wb")) == NULL )
#else
    if ( (fp = fopen(file, "w")) == NULL )
#endif
	return ERR;

    if ( now_dic_mode == DIC_DEVICE_MODE ) {
    	size = file_end_block - file_start_block + 1;
    } else {
	fseek(dev_fp, 0L, 2);
	size = ftell(dev_fp);
	fseek(dev_fp, 0L, 0);
	size = (size + 2047) / 2048;
    }

    if ( flag ) {
	if ( IO_block_read(1) )
	    return ERR;

	i = (dic_buf[0] << 8) | dic_buf[1];
	for ( n = 1 ; n <= i ; n++ ) {
	    p = dic_buf + (n * 16);
	    if ( (*p & 0xF0) == 0xD0 || *p == 0xE0 ) {
		if ( skip_max < 16 ) {
		    skip_sta[skip_max] = tolong(p + 2);
		    block = tolong(p + 6);
		    skip_end[skip_max] = skip_sta[skip_max] + block;
		    skip_size += block;
		    skip_max++;
		}
	    }
	}
    }

    fwrite("EDIC", 1, 4, fp);
    pos = size * sizeof(long) + 4L;
    fseek(fp, pos, 0);

    printf("Dictonary Size %ld - %ld = %ld Byte\n",
	size * 2048L, skip_size * 2048L, (size - skip_size) * 2048);
    printf("compress   0%%\r"); fflush(stdout);

    top = 0L;
    blk = 0;

    for ( block = 1 ; block <= size ; block++ ) {
	for ( i = 0 ; i < skip_max ; i++ ) {
	    if ( block >= skip_sta[i] && block < skip_end[i] )
		break;
	}

	if ( i >= skip_max ) {
	    if ( IO_block_read(block) )
	        return ERR;
	    tab[blk++] = pos;
	    pos += encode(dic_buf, 2048, fp);
	} else {
	    tab[blk++] = 0L;
	}

	if ( blk >= 256 ) {
	    fseek(fp, top * sizeof(long) + 4L, 0);
	    fwrite(tab, sizeof(long), blk, fp);
	    fseek(fp, pos, 0);
	    top += blk;
	    blk = 0;
	    printf("compress %3d%%(%3d%%)\r",
		(int)(top * 100 / size), (int)((pos / 20L) / top));
	    fflush(stdout);
	}
    }

    if ( blk > 0 ) {
	fseek(fp, top * sizeof(long) + 4L, 0);
	fwrite(tab, sizeof(long), blk, fp);
	top += blk;
    }

    printf("compress %3d%%(%3d%%)\n",
	(int)(top * 100 / size), (int)((pos / 20L) / top));

    fclose(fp);
    free(node);
    free(tab);

    return FALSE;
}
