#include    <stdio.h>
#include    <stdlib.h>
#include    <string.h>
#include    <msdos.cf>
#include    <egb.h>
#include    <fgs.h>
#include    <xld.h>
#include    <jpeg.h>
#include    <tifflib.h>
#include    "graphic.h"
#include    "coldef.h"
#include    "dir.h"

#define	TRUE	1
#define	FALSE	0
#define	ERR	(-1)

#define LOAD_BUF_SIZE	(150 * 1024)
#define	PALT_BUF_SIZE	(256 * 8 + 4)
#define	DISP_LINE_SIZE	32

typedef	struct	{
	short	r, g, b;
	short	m;
} PIXS;

typedef	struct {
	int	no;
	unsigned char	b, r, g, d;
} PALE;

extern	int	screen_flg;
extern	char	work[];
extern	char	fgs_work[];

static	FILE    *input_fp;
static	int	orig_pixs;
static	int     orig_width;
static	int	orig_height;

static	char 	*palt_buff;

static	int	disp_pos = 0;
static	int	disp_width  = 0;
static	int	disp_height = 0;
static	int	scrn_width  = 128;
static	int	scrn_height = 96;
static	int	scrn_wmax = 4;
static	int	scrn_hmax = 4;
static	int	scrn_tmax = 16;
static	int	fgs_mode = 0;

static	int	comp_pixs = 24;		/* 16 or 24 */
static	int	comp_wgap = 8;
static	int	comp_hgap = 8;
static	int     comp_width = 80;
static	int	comp_height = 60;
static	char	*comp_buff = NULL;

static	int	pixs_line = 0;
static	PIXS	*pixs_buff = NULL;

static	char	*file_name[20];

	char	*strdup(char *str);

static	void	CompPixs()
{
    int n;
    int max, line;
    int ox = 0, oy = 0;
    char *p;
    short *s;
    BLOCK para;

    if ( (line = pixs_line / comp_hgap) >= comp_height )
	return;

    if ( comp_width < scrn_width )
	ox = (scrn_width - comp_width) / 2;
    if ( comp_height < scrn_height )
	oy = (scrn_height - comp_height) / 2;

    para.sel = getds();
    para.x1 = disp_width  + ox;
    para.x2 = disp_width  + ox + comp_width - 1;
    para.y1 = disp_height + oy + line;
    para.y2 = disp_height + oy + line;

    switch(comp_pixs) {
    case 16:
	s = (short *)(comp_buff + (2 * comp_width) * line);
	for ( n = 0 ; n < comp_width ; n++ ) {
	    max = pixs_buff[n].m * 8;
	    *(s++) = ((pixs_buff[n].g / max) << 10) |
		     ((pixs_buff[n].r / max) << 5) |
		      (pixs_buff[n].b / max);
	}

	para.ptn = comp_buff + (2 * comp_width) * line;
	EGB_putBlock(work, 0, (char *)&para);
	break;

    case 24:
	p = comp_buff +	(3 * comp_width) * line;
	for ( n = 0 ; n < comp_width ; n++ ) {
	    max = pixs_buff[n].m;
	    *(p++) = pixs_buff[n].r / max;
	    *(p++) = pixs_buff[n].g / max;
	    *(p++) = pixs_buff[n].b / max;
	}

	para.ptn = comp_buff + (3 * comp_width) * line;
	FGS_putBlock(fgs_work, 0, (char *)&para);
	break;
    }

    pixs_line += comp_hgap;
    memset((char *)pixs_buff, 0, sizeof(PIXS) * comp_width);
}
static	void	PutPixs(int x, int y, PIXS *pix)
{
    int cols;

    if ( y >= (pixs_line + comp_hgap) )
	CompPixs();

    if ( (y -= pixs_line) < 0 )		/* ??? */
	return;

    if ( (cols = x / comp_wgap) >= comp_width )
	return;

    pixs_buff[cols].r += pix->r;
    pixs_buff[cols].g += pix->g;
    pixs_buff[cols].b += pix->b;
    pixs_buff[cols].m += 1;
}
static	void	GetPalette(int no, PIXS *pix)
{
    int n, bs, mx;
    PALE *p;

    bs = 0;
    mx = DWORD(palt_buff);
    p = (PALE *)(palt_buff + 4);

    if ( (orig_pixs == 4 && mx == 16) ||
	 (orig_pixs == 8 && mx == 256) ) {
	pix->r = p[no].r;
	pix->g = p[no].g;
	pix->b = p[no].b;
	return;
    }

    while ( bs <= mx ) {
	n = (bs + mx) / 2;
	if ( no == p[n].no ) {
	    pix->r = p[n].r;
	    pix->g = p[n].g;
	    pix->b = p[n].b;
	} else if ( no < p[n].no )
	    bs = n + 1;
	else
	    mx = n - 1;
    }
}
static	int	PutData(char *buf, int lofs, int lines ) 
{
    int n, i;
    int x, y, b;
    PIXS pix;
    char *p;
    short *s;
    static char ptn[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };

    switch(orig_pixs) {
    case 1:	/* 2色 */
	i = (orig_width + 7) / 8;
	for ( n = 0 ; n < lines ; n++ ) {
	    y = lofs + n;
	    p = (buf + i * n);
	    for ( x = 0 ; x < orig_width ; x++ ) {
		b = *p;
		if ( (x % 8) == 7 )
		    p++;
		b = ((b & ptn[x % 8]) == 0 ? 0xFF: 0);
		pix.r = pix.g = pix.b = b;
		PutPixs(x, y, &pix);
	    }
	}
	break;

    case 4:     /* 16色 */
	i = (orig_width + 7) / 8 * 4;
	for ( n = 0 ; n < lines ; n++ ) {
	    y = lofs + n;
	    p = (buf + i * n);
	    for ( x = 0 ; x < orig_width ; x++ ) {
		if ( (x & 1) == 0 )
		    b = (unsigned char)*p & 0x0F;
		else
		    b = (unsigned char)*(p++) >> 4;
		GetPalette(b, &pix);
		PutPixs(x, y, &pix);
	    }
	}
	break;

    case 8:     /* 256色 */
	for ( n = 0 ; n < lines ; n++ ) {
	    y = lofs + n;
	    for ( x = 0 ; x < orig_width ; x++ ) {
		b = (unsigned char)*(buf++);
		GetPalette(b, &pix);
		PutPixs(x, y, &pix);
	    }
	}
	break;

    case 16:    /* 32K色 */
	s = (short *)buf;
	for ( n = 0 ; n < lines ; n++ ) {
	    y = lofs + n;
	    for ( x = 0 ; x < orig_width ; x++ ) {
		b = (unsigned short)*(s++);
		pix.g = ((b >> 10) & 0x1F) << 3;
		pix.r = ((b >>  5) & 0x1F) << 3;
		pix.b = (b & 0x1F) << 3;
		PutPixs(x, y, &pix);
	    }
	}
	break;

    case 24:	/* 1680K色 */
	for ( n = 0 ; n < lines ; n++ ) {
	    y = lofs + n;
	    for ( x = 0 ; x < orig_width ; x++ ) {
		pix.r = (unsigned char)*(buf++);
		pix.g = (unsigned char)*(buf++);
		pix.b = (unsigned char)*(buf++);
		PutPixs(x, y, &pix);
	    }
	}
	break;
    }

    return 0;
}
static	int	ReadData(char *buf, int size )
{
    fread(buf, 1, size, input_fp);
    return 0 ;
}
static	int	PaletteComp(PALE *sp, PALE *dp)
{
    return (sp->no - dp->no);
}
static	char	*SmallDisp(char *file)
{
    int     n;
    int     d_width, d_line;
    int     fill, comp;
    long    strip, pal;
    char    *load_buff;
    char    *data_buff;
    char    *deco_buff;
    BLOCK   *save;

    comp_buff = NULL;

    if ( (input_fp = fopen(file,"rb")) == NULL )
	goto ERROR1;

    if ( (load_buff = (char *)malloc(LOAD_BUF_SIZE)) == NULL )
	goto ERROR2;

    if ( (n = fread(load_buff, 1, LOAD_BUF_SIZE, input_fp)) <= 0 )
	goto ERROR3;

    if ( TIFF_getHead(load_buff, LOAD_BUF_SIZE) < 0 )
	goto ERROR3;

    if ( (orig_pixs = TIFF_checkMode
	 (&orig_width, &orig_height, &comp, &fill, &strip, &pal)) < 0 )
	goto ERROR3;

    switch(orig_pixs) {
    case 1:	/* 2色 */
    case 4:     /* 16色 */
    case 8:     /* 256色 */
    case 16:    /* 32K色 */
    case 24:	/* 1680K色 */
	break;
    default:
	goto ERROR3;
    }

    TIFF_setLoadFunc(PutData, ReadData);

    d_line = DISP_LINE_SIZE;
    d_width = orig_width;
    if ( orig_pixs == 4 && (d_width & 7) != 0 )
       d_width += 8 - (d_width & 7);

    comp_wgap = (orig_width + scrn_width - 1) / scrn_width;
    comp_hgap =	(orig_height + scrn_height - 1) / scrn_height;

    if ( comp_wgap > comp_hgap )
	comp_hgap = comp_wgap;
    else
	comp_wgap = comp_hgap;

    if ( comp_wgap <= 0 )
	comp_wgap = comp_hgap = 1;

    comp_width  = orig_width  / comp_wgap;
    comp_height = orig_height / comp_hgap;

    if ( (data_buff = malloc
		(d_line * ((d_width * orig_pixs + 7) / 8))) == NULL )
	goto ERROR3;
    if ( (pixs_buff = malloc(comp_width * sizeof(PIXS))) == NULL )
	goto ERROR4;
    if ( (deco_buff = malloc(DECOMP_WORK_SIZE)) == NULL )
	goto ERROR5;
    if ( (palt_buff = malloc(PALT_BUF_SIZE)) == NULL )
	goto ERROR6;
    if ( (comp_buff = malloc
		(comp_height * ((comp_width * comp_pixs + 7) / 8))) == NULL )
	goto ERROR7;

    switch(orig_pixs) {
    case 4:
    case 8:
	TIFF_getPal(palt_buff);
	qsort(palt_buff + 4, DWORD(palt_buff), sizeof(PALE), PaletteComp);
	break;
    }

    pixs_line = 0;
    memset((char *)pixs_buff, 0, sizeof(PIXS) * comp_width);

    TIFF_loadImage(orig_pixs, orig_width, orig_height, strip, fill,
		   comp, data_buff, d_width, d_line, deco_buff);

    if ( orig_height > pixs_line )
	CompPixs();

ERROR8:
    free(comp_buff);
ERROR7:
    free(palt_buff);
ERROR6:
    free(deco_buff);
ERROR5:
    free(pixs_buff);
ERROR4:
    free(data_buff);
ERROR3:
    free(load_buff);
ERROR2:
    fclose(input_fp);
ERROR1:
    return comp_buff;
}
static	int	ClipBox(int no)
{
    int n, i, len;
    short xy[4];
    BLOCK para;
    char *img;
    char *p;
    short pos[4][4] = {
	{ 0, 1, 2, 1 },
	{ 0, 3, 2, 3 },
	{ 0, 1, 0, 3 },
	{ 2, 1, 2, 3 },
    };

    len = (scrn_width * comp_pixs + 7) / 8;

    if ( (img = malloc(len)) == NULL )
	return ERR;

    xy[0] = scrn_width  * (no % scrn_wmax);
    xy[1] = scrn_height * (no / scrn_wmax);
    xy[2] = xy[0] + scrn_width - 1;
    xy[3] = xy[1] + scrn_height - 1;

    para.ptn = img;
    para.sel = getds();
    for ( n = 0 ; n < 4 ; n++ ) {
	para.x1 = xy[pos[n][0]];
	para.y1 = xy[pos[n][1]];
	para.x2 = xy[pos[n][2]];
	para.y2 = xy[pos[n][3]];

	switch(comp_pixs) {
	case 16:
	    EGB_getBlock(work, (char *)&para);
	case 24:
	    FGS_getBlock(fgs_work, 0, (char *)&para);
	}

	p = img;
	for ( i = 0 ; i < len ; i++ )
	    *(p++) ^= 0xFF;

	switch(comp_pixs) {
	case 16:
	    EGB_putBlock(work, 0, (char *)&para);
	case 24:
	    FGS_putBlock(fgs_work, 0, (char *)&para);
	}
    }

    free(img);
    return FALSE;
}
static	int	ScreenSave()
{
    int st = ERR;
    int len;
    BLOCK para;

    len = (scrn_height * scrn_hmax) *
	  (scrn_width  * scrn_wmax * comp_pixs + 7) / 8;

    if ( (comp_buff = malloc(len)) == NULL )
	goto ENDOF;

    para.ptn = comp_buff;
    para.sel = getds();
    para.x1 = 0;
    para.y1 = 0;
    para.x2 = scrn_width  * scrn_wmax - 1;
    para.y2 = scrn_height * scrn_hmax - 1;

    switch(comp_pixs) {
    case 16:
	EGB_getBlock(work, (char *)&para);
    case 24:
	FGS_getBlock(fgs_work, 0, (char *)&para);
    }

    st = FALSE;
    screen_flg = TRUE;

ENDOF:
    if ( comp_pixs == 24 )
	sub_FGS_close();
    EGB_displayPage(work, 0, 0);
    EGB_resolution(work, 0, 3);
    EGB_writePage(work, 0);
    EGB_clearScreen(work);
    EGB_displayPage(work, 0, 1);

    return st;
}
static	void	ScreenLoad()
{
    BLOCK para;

    MOS_disp(OFF);
    switch(fgs_mode) {
    case 0:
	FGS_init(fgs_work, 1536);
	FGS_clearScreen(fgs_work);
	break;
    case 1:
	FGS_init2(fgs_work, 1536);
	FGS_clearScreen(fgs_work);
	break;
    case 2:
	EGB_displayPage(work, 0, 0);
        EGB_resolution(work,0,17);
        EGB_displayStart(work, 3, 512, 480);
	EGB_writePage(work, 0);
	EGB_color(work, 1, 0);
	EGB_clearScreen(work);
	EGB_displayPage(work, 0, 1);
	break;
    }
    screen_flg = FALSE;

    para.ptn = comp_buff;
    para.sel = getds();
    para.x1 = 0;
    para.y1 = 0;
    para.x2 = scrn_width  * scrn_wmax - 1;
    para.y2 = scrn_height * scrn_hmax - 1;

    switch(comp_pixs) {
    case 16:
	EGB_putBlock(work, 0, (char *)&para);
    case 24:
	FGS_putBlock(fgs_work, 0, (char *)&para);
    }

    free(comp_buff);
}
static	int	MosStat()
{
    int sw, x, y;

    MOS_rdpos(&sw, &x, &y);
    return sw;
}
static	int	MosWait()
{
    int sw, x, y;
    int no, od;
    long st, ed;

    time(&st);
    st += 30;

    no = od = 0;
    ClipBox(no);
    MOS_setpos(scrn_width / 2, scrn_height / 2);

    for ( ; ; ) {
	time(&ed);
	if ( ed >= st )
	    return 0;

	MOS_rdpos(&sw, &x, &y);

	if ( (no = x / scrn_width + (y / scrn_height) * 4) < 0 )
	    no = 0;
	else if ( no >= scrn_tmax )
	    no = scrn_tmax - 1;

	if ( no != od ) {
	    ClipBox(od);
	    if ( ClipBox(no) )
		sw &= 2;
	    od = no;
	    st = ed + 30;
	}

	if ( (sw & 1) != 0 && file_name[no] != NULL ) {
	    if ( ScreenSave() ) {
		TIFF_disp(file_name[no]);
		break;
	    } else
		TIFF_disp(file_name[no]);
	    ScreenLoad();
	    st = ed + 30;

	} else if ( (sw & 2) != 0 )
	    break;
    }

    while ( sw != 0 )
	sw = MosStat();

    return sw;
}
static	int	TiffDisp(char *file)
{
    int n, sw;

    disp_width  = scrn_width  * (disp_pos % scrn_wmax);
    disp_height = scrn_height * (disp_pos / scrn_wmax);

    SmallDisp(file);
    file_name[disp_pos] = strdup(file);

    if ( ++disp_pos >= scrn_tmax ) {
	sw = MosWait();
	disp_pos = 0;

	if ( comp_pixs == 24 )
	    FGS_clearScreen(fgs_work);
	else
	    EGB_clearScreen(work);

	for ( n = 0 ; n < scrn_tmax ; n++ ) {
	    if ( file_name[n] != NULL )
		free(file_name[n]);
	    file_name[n] = NULL;
	}

    } else
	sw = MosStat();

    return sw;
}
static	int	DirTiffDisp(char *dir)
{
    int sw = 0;
    DIR *dirp;
    DIRECT *dp;
    char tmp[128];

    strcpy(tmp, dir);
    joint_path(tmp, "*.*");

    if ( (dirp = opendir(tmp)) == NULL )
	return 0;

    while ( (dp = readdir(dirp)) != NULL ) {
	strcpy(tmp, dir);
	joint_path(tmp, dp->d_name);

	if ( dp->d_name[0] == '.' )
	    continue;
	else if ( IS_DIR(dp) )
	    sw = DirTiffDisp(tmp);
	else if ( wild_mach("*.TIF", dp->d_name) )
	    sw = TiffDisp(tmp);

	if ( sw != 0 )
	    break;
    }

    closedir(dirp);

    return (sw & 2);
}
void	TIFF_catalog(char *dir)
{
    int n;
    BLOCK *save;

    MOS_disp(OFF);
    save = DSP_push_vram(0, 0, 639, 479);

    if ( FGS_openCheck() == 0 ) {
	FGS_init(fgs_work, 1536);
	FGS_clearScreen(fgs_work);
	comp_pixs   = 24;
	scrn_width  = 160;
	scrn_height = 120;
	scrn_wmax   = 4;
	scrn_hmax   = 4;
	fgs_mode = 0;
    } else if ( FGS_openCheck2() == 0 ) {
	FGS_init2(fgs_work, 1536);
	FGS_clearScreen(fgs_work);
	comp_pixs   = 24;
	scrn_width  = 160;
	scrn_height = 120;
	scrn_wmax   = 4;
	scrn_hmax   = 4;
	fgs_mode = 1;
    } else {
	EGB_displayPage(work, 0, 0);
        EGB_resolution(work,0,17);
        EGB_displayStart(work, 3, 512, 480);
	EGB_writePage(work, 0);
	EGB_color(work, 1, 0);
	EGB_clearScreen(work);
	EGB_displayPage(work, 0, 1);
	comp_pixs   = 16;
	scrn_width  = 128;
	scrn_height = 96;
	scrn_wmax   = 4;
	scrn_hmax   = 5;
	fgs_mode = 2;
    }

    scrn_tmax   = scrn_wmax * scrn_hmax;
    disp_pos = 0;
    if ( DirTiffDisp(dir) == 0 && disp_pos > 0 )
	MosWait();

    for ( n = 0 ; n < scrn_tmax ; n++ ) {
	if ( file_name[n] != NULL )
	    free(file_name[n]);
	file_name[n] = NULL;
    }

    if ( comp_pixs == 24 )
	sub_FGS_close();
    EGB_displayPage(work, 0, 0);
    EGB_resolution(work, 0, 3);
    EGB_resolution(work, 1, 3);
    EGB_writePage(work, 1);
    DSP_palette();
    EGB_clearScreen(work);
    EGB_writePage(work, 0);
    DSP_palette();
    EGB_clearScreen(work);
    DSP_pop_vram(save);
    EGB_displayPage(work, 0, 3);
    MOS_disp(ON);
    FILE_redisp();
}
