#include    <stdio.h>
#include    <stdlib.h>
#include    <malloc.h>
#include    <ctype.h>
#include    <jctype.h>
#include    <string.h>
#include    "defs.h"
#include    "key.h"

#define	TAB	8

#define	READMAX	4096

typedef	struct	_LP {
	struct	_LP	*next;
	struct	_LP	*back;
	char		line[1];
} LINPTR;

static	FILE	*fp;
static	LINPTR	*now;
static	int	line = (-1);
static	int	top = 0;
static	int	scan = 0;

static	int	text_mode = TRUE;
static	unsigned long	addr = 0L;
static	int	read_pos = 0;
static	int	read_max = 0;
static	unsigned char	read_buf[READMAX];

static	void	readinit()
{
    int n;
    int len = 0;

    addr = 0L;
    read_pos = 0;
    read_max = fread(read_buf, 1, READMAX, fp);

    for ( n = 0 ; n < read_max ; n++ ) {
	switch(read_buf[n]) {
	case '\n': case '\r': case '\t': case '\f': case '\b':
	    break;
	case 0x7F: case 0xFE: case 0xFF:
	    len++;
	    break;
	default:
	    if ( read_buf[n] < ' ' )
		len++;
	    break;
	}
    }

    text_mode = (len > 10 ? FALSE : TRUE);
}
static	int	readchar()
{
    if ( read_pos >= read_max ) {
	read_pos = 0;
	if ( (read_max = fread(read_buf, 1, READMAX, fp)) <= 0 )
	    return EOF;
    }
    return (read_buf[read_pos++]);
}
static	LINPTR	*readline()
{
    int n;
    int ch;
    int len = 0;
    LINPTR *lp;
    unsigned char *p;
    char buf[LINSIZ];

    if ( text_mode == FALSE ) {
/********
01234567890123456789012345678901234567890123456789012345678901234567890123456
00000000  00 01 02 03 04 05 06 07 - 08 09 0a 0b 0c 0d 0e 0f  0123456789abcdef
*********/
	sprintf(buf, "%08lx ", addr);
	len = 9;
	p = buf + 61;
	for ( n = 0 ; n < 16 ; n++ ) {
	    if ( (ch = readchar()) == EOF )
		break;
	    sprintf(buf + len, "%s%02x", (n == 8 ? " - " : " "), ch);
	    len += strlen(buf + len);
	    if ( ch < ' ' || ch == 0x7F )
		ch = '.';
	    *(p++) = ch;
	}
	if ( n == 0 )
	    return NULL;
	while ( len < 61 )
	    buf[len++] = ' ';
	len = (p - buf);
	*p = '\0';
	for ( p = buf + 61 ; *p != '\0' ; p++ ) {
	    if ( iskanji(p[0]) ) {
		if ( iskanji2(p[1]) )
		    p++;
		else
		    p[0] = '.';
	    } else if ( (p[0] >= 0x80 && p[0] <= 0x9F) ||
			(p[0] >= 0xE0 && p[0] <= 0xFF) )
		p[0] = '.';
	}
	addr += 16;

    } else {
	while ( len < SCR_X && (ch = readchar()) != EOF ) {
	    if ( iskanji(ch) ) {
		if ( len >= (SCR_X - 1) ) {
		    read_pos--;
		    break;
		}
		buf[len++] = ch;
		if ( (ch = readchar()) == EOF ) {
		    len--;
		    break;
		}
		buf[len++] = ch;

	    } else if ( ch == '\t' ) {
		n = TAB - (len % TAB);
		while ( len < SCR_X && n-- > 0 )
		    buf[len++] = ' ';

	    } else if ( ch == '\n' ) {
		break;

	    } else if ( ch != '\r' && ch != 0x1A )
		buf[len++] = ch;
	}
    }

    buf[len] = '\0';

    if ( len == 0 && ch == EOF )
	return NULL;

    if ( (lp = (LINPTR *)malloc(sizeof(LINPTR) + len)) == NULL )
	return NULL;

    lp->next = lp->back = NULL;
    strcpy(lp->line, buf);

    return lp;
}
char	*getline(int no)
{
    while ( no < line ) {
	if ( now->back == NULL )
	    return NULL;
	now = now->back;
	line--;
    }

    while ( no > line ) {
	if ( now->next == NULL ) {
	    if ( (now->next = readline()) == NULL )
		return NULL;
	    now->next->back = now;
	}
	now = now->next;
	line++;
    }

    return now->line;
}
int	putline(int y, int no)
{
    char *p;

    LOCATE(0, y);
    if ( (p = getline(no)) != NULL ) {
	PUTS(p);
	if ( strlen(p) < SCR_X )
	    ERALINE();
	return FALSE;
    } else {
	ERALINE();
	return ERR;
    }
}
void	forscrool()
{
    if ( getline(top + (SCR_Y - 1)) == NULL )
	return;
    top++;
    FORSCROOL();
    putline(SCR_Y - 2, line);
    LOCATE(0, SCR_Y - 1);
/**********
    FLUSH();
***********/
}
void	backscrool()
{
    if ( top <= 0 )
	return;
    top--;
    BAKSCROOL();
    putline(0, top);
    LOCATE(0, SCR_Y - 1);
    ERALINE();
/*************
    FLUSH();
**************/
}
int	more(char *file)
{
    int n, ch;
    LINPTR *lp;
    char *p;
    char buf[128 + 2];

    if ( (fp = fopen(file, "rb")) == NULL )
	return ERR;

    line = top = 0;
    readinit();
    if ( (now = readline()) == NULL ) {
	fclose(fp);
	return ERR;
    }

    SAVESCREEN();

    for ( n = 0 ; n < (SCR_Y - 1) ; n++ )
	putline(n, n);
    LOCATE(0, n);
    ERALINE();
    FLUSH();

    buf[0] = '\0';

    for ( ; ; ) {
	ch = GETCH();

	switch(ch) {
	case K_ABORT:
	case K_END_OF:
	case 'Q': case 'q':
	    goto ENDOF;

	case ' ':
	    for ( n = 0 ; n < (SCR_Y - 3) ; n++ )
		forscrool();
	    break;

	case K_BACK_SPC:
	    for ( n = 0 ; n < (SCR_Y - 3) ; n++ )
		backscrool();
	    break;

	case K_UP_NODE:
	    backscrool();
	    break;

	case K_DOWN_NODE:
	case K_END_LINE:
	    forscrool();
	    break;

	case K_SCREEN_FLUSH:
	REDISP:
	    for ( n = 0 ; n < (SCR_Y - 1) ; n++ )
		putline(n, top + n);
	    LOCATE(0, SCR_Y - 1);
	    break;
	
	case 'n': case 'N':
	SERCH:
	    if ( buf[0] == '\0' )
		break;
	    LOCATE(0, SCR_Y - 1);
	    FPUTS("/%-78.78s", buf);
	    ERALINE();
	    for ( n = scan + 1; ; n++ ) {
		if ( (p = getline(n)) == NULL )
		    break;
		else if ( strstr(p, buf) != NULL ) {
		    if ( (scan = top = n) > 10 )
			top -= 10;
		    goto REDISP;
		}
	    }

	NOTFOUND:
	    scan = 0;
	    BEEP();
	    LOCATE(0, SCR_Y - 1);
	    FPUTS("not found %s", buf);
	    LOCATE(0, SCR_Y - 1);
	    break;

	case 'p': case 'P':
	    if ( buf[0] == '\0' )
		break;
	    LOCATE(0, SCR_Y - 1);
	    FPUTS("/%-78.78s", buf);
	    ERALINE();
	    for ( n = scan - 1 ; n > 0 ; n-- ) {
		if ( (p = getline(n)) == NULL )
		    break;
		else if ( strstr(p, buf) != NULL ) {
		    if ( (scan = top = n) > 10 )
			top -= 10;
		    goto REDISP;
		}
	    }
	    goto NOTFOUND;

	case '/':
	    LOCATE(0, SCR_Y - 1);
	    PUTS("/");
	    for ( ; ; ) {
		ch = input(1, SCR_Y - 1, 78, 128, 0, buf);
		if ( ch == K_END_LINE )
		    goto SERCH;
	    }
	    LOCATE(0, SCR_Y - 1);
	    ERALINE();
	    break;
	}
	FLUSH();
    }
    ENDOF:

    fclose(fp);

    while ( now->back != NULL )
	now = now->back;

    while ( (lp = now) != NULL ) {
	now = now->next;
	free(lp);
    }

    LOADSCREEN();
    FLUSH();

    return FALSE;
}
