/* wasp - copyright Steven Reiz 1990, 1991
 * see wasp.c for further info
 * wriff.c, 4/12/90 - 30/1/91,
 * 5/5/91 - 2/6/91, 23/6/91, 30/6/91 - 8/7/91
 */

#include "wasp.h"
#define WRIFFMAIN
#include "wriff.h"
#ifndef NOSH
#include "wriff.sh"
#endif

static int ticolors;


#ifdef __STDC__
write_iff(void)
#else
write_iff()
#endif
{
    long patchadr1, patchadr2;
    long t;

    err=0; err2=0;
    wriff_init();
    decide_mode();
    compute_nregs();
    wrl(id('F', 'O', 'R', 'M'));
    patchadr1=4;
    wrl(0L); /* size of the form ilbm, will be patched later */
    wrl(id('I', 'L', 'B', 'M'));
    write_header();
    write_cmap();
    write_sham();
    patchadr2=lseek(outfd, 0L, 1)+4; /* the size of the body chunk will be patched later */
    write_body();
    t=lseek(outfd, 0L, 1);
    lseek(outfd, patchadr1, 0);
    wrl(t-patchadr1-4);
    lseek(outfd, patchadr2, 0);
    wrl(t-patchadr2-4);
    write_cmap();
    write_sham();
    printf("IFF file written; %ld bytes, error: %ld, error2: %ld\n", t, err, err2);
}


#ifdef __STDC__
wriff_init(void)
#else
wriff_init()
#endif
{
	slicedo=0;
	counts=NULL1;
	cm=NULL1;
	curcm=NULL1;
	wf2rgbweight=NULL1;
	cmrgb=NULL1;
	newcol=NULL1;
	error=NULL1;
	error2=NULL1;
}


#ifdef __STDC__
decide_mode(void)
#else
decide_mode()
#endif
{
    float sx, sy, st;

    if (directrgb>=0) {
	if (directrgb%3)
		error1(E0_FATAL, E1_IFF_RGB, E2_OPTION, E3_WRONG_NR_PLANES, (int)directrgb);
	nplanes=directrgb;
	printf("IFF output; direct rgb, %d planes\n", nplanes);
	return;
    }
    if ((xmode!=UNSET || ymode!=UNSET) && asc)
	error0(E0_FATAL, E1_IFF, E2_OPTION, E3_ASC);
    if (xmode!=UNSET && hires!=HIRES_OK)
	error0(E0_FATAL, E1_IFF, E2_OPTION, E3_NOHIRES);
    count_pixels(0, (int)ysz-1, 1);
    ticolors=icolors=count_colors(1L);
    if (hires==HIRES_MAYBE)
        hires=(icolors<=16 ? HIRES_OK : HIRES_NOT);
    if (asc && (xsz>scrw || ysz>scrh)) {
        sx=(float)scrw/(float)xsz;
        sy=(float)scrh/(float)ysz;
        if (sy<sx)
            st=sy;
        else
            st=sx;
        ymode=LACE;
        sy=st*2.0;
        if (hires==HIRES_OK) {
            xmode=HIRES;
            sx=st*2.0;
        } else {
            sx=st;
            if (sy>1.0) {
                sx=sx/sy;
                sy=1.0;
            }
        }
        if (sx!=sy || sx<1.0) {
            scalef(1, sy);
            scalef(0, sx);
        }
    }
    if (xmode==UNSET) {
        if (icolors<=32)
            xmode=LORES;
        else
            xmode=HAM;
    }
    slicedo=sliced;
    if (ymode==UNSET)
        ymode=NOLACE;
    if (distrmeth2!=DISTRMETH_UNSET && xmode!=HAM)
	error0(E0_FATAL, E1_IFF, E2_OPTION, E3_ITMETH);
    if (!inoperation && distrmeth==DISTRMETH_UNSET) {
        if (xmode==EHB)
            distrmeth=DEF_EHB_DISTRMETH;
        else
            distrmeth=DEF_DISTRMETH;
    }
    if (countmeth==COUNTMETH_UNSET)
	countmeth=(xmode==HAM ? DEF_HAM_COUNTMETH : DEF_COUNTMETH);
    if (xmode==EHB && !is_ehb_distr())
	error1(E0_FATAL, E1_IFF, E2_OPTION, E3_EHB_METH, dmethnam(distrmeth));
    printf("IFF output; xmode: %s%s, ymode: %s, cmeth: %s, dmeth: %s",
     (slicedo ? "sliced " : ""), xmodenam(xmode), ymodenam(ymode),
     cmethnam(countmeth), dmethnam(distrmeth));
    if (distrmeth2!=DISTRMETH_UNSET)
	printf(", dmeth2: %s", dmethnam(distrmeth2));
    putchar('\n');
}


#ifdef __STDC__
int is_ehb_distr(void)
#else
int is_ehb_distr()
#endif
{
    return (distrmeth==DISTRMETH_EHB || distrmeth==DISTRMETH_MUE ? 1 : 0);
}


#ifdef __STDC__
compute_nregs(void)
#else
compute_nregs()
#endif
{
    if (directrgb>=0)
	return;
    switch (xmode) {
    case HAM:   nregs=16; nplanes=6; break;
    case EHB:   nregs=64; nplanes=6; break;
    case LORES: nregs=32; nplanes=5; break;
    case HIRES: nregs=16; nplanes=4; break;
    }
    if (slicedo) {
        cm=Malloc((ymode==LACE && sliced==SLICED_SHAM ? (ysz+1)/2 : ysz)*nregs*sizeof(u_short));
	curcm=cm;
        goto compute_npixvals;
    }
    count_pixels(0, (int)ysz-1, 0);
    icolors=count_colors(1L);
    if (icolors>nregs && threshold>1) {
        icolors=count_colors(threshold);
        if (icolors<nregs) {
            u_long mint, curt, maxt;

            printf("%d colors occur at least %ld times, trying to complete to %d\n",
             icolors, threshold, nregs);
            mint=1;
            maxt=threshold;
            while (maxt>mint+1) {
                curt=(mint+maxt)/2;
                icolors=count_colors(curt);
                if (icolors<nregs)
                    maxt=curt-1;
                else
                    mint=curt;
            }
            threshold=(mint+maxt)/2;
            icolors=count_colors(threshold);
            printf("%d colors occur at least %ld times\n", icolors, threshold);
        }
    }
    if (xmode==LORES || xmode==HIRES) {
        if (icolors<nregs)
            nplanes=ceillog2((u_long)icolors);
        else
            nplanes=ceillog2((u_long)nregs);
        nregs=1<<nplanes;
    }
    cm=Malloc(nregs*sizeof(u_short));
    curcm=cm;
compute_npixvals:
    if (distrmeth2!=DISTRMETH_UNSET) {
	cm0=Malloc(nregs*sizeof(u_short));
	cmprev=Malloc(nregs*sizeof(u_short));
    }
    npixvals=1<<nplanes;
    printf("%d colors", ticolors);
    if (icolors!=ticolors)
        printf(", counting %d colors", icolors);
    printf(", %d color registers, %d planes", nregs, nplanes);
    if (nregs-icolors>0)
        printf(", %d color registers will be wasted", nregs-icolors);
    printf("\n");
    if (nregs<2 || xsz<2 || ysz<2)
	error0(E0_FATAL, E1_IFF, E2_FORMAT, E3_2SIMPLE);
    assert(nplanes>0 && nplanes<=MAXNPLANES);
    assert(nregs<=MAXNREGS);
}


#ifdef __STDC__
PRIVATE write_header(void)
#else
PRIVATE write_header()
#endif
{
    struct bmhd_t {
        u_short w, h;
        short x, y;
        u_char nplanes, masking, compression, pad;
        u_short transpcol;
        u_char xaspect, yaspect;
        short pagew, pageh;
    } bmhd;
    u_long viewmodes;

    wrl(id('B', 'M', 'H', 'D'));
    wrl((long)sizeof(bmhd));
    bmhd.w=xsz;
    bmhd.h=ysz;
    bmhd.x=bmhd.y=0;
    bmhd.nplanes=nplanes;
    bmhd.masking=0;
    bmhd.compression=compression;
    bmhd.pad=0;
    bmhd.transpcol=0;
    bmhd.yaspect=11;
    if (xmode==HIRES && ymode==NOLACE)
        bmhd.xaspect=5;
    else if (xmode!=HIRES && ymode==LACE)
        bmhd.xaspect=20;
    else
        bmhd.xaspect=10;
    bmhd.pagew=bmhd.w;
    bmhd.pageh=bmhd.h;
    cwrite(&bmhd, sizeof(bmhd));
    if (directrgb>=0)
	return;
    wrl(id('C', 'A', 'M', 'G'));
    wrl(4L);
    viewmodes=0;
    if (xmode==HIRES)
        viewmodes|=VM_HIRES;
    else if (xmode==HAM)
        viewmodes|=VM_HAM;
    else if (xmode==EHB)
        viewmodes|=VM_EHB;
    if (ymode==LACE)
        viewmodes|=VM_LACE;
    wrl(viewmodes);
}


#ifdef __STDC__
PRIVATE write_cmap(void)
#else
PRIVATE write_cmap()
#endif
{
    u_char *cmbuf;
    long t;
    short i, j, nr;
    static long pos= -1;

    if (directrgb>=0)
	return;
    if (xmode==EHB)
        nr=nregs/2;
    else
        nr=nregs;
    t=nr*3;
    if (pos== -1) {
        wrl(id('C', 'M', 'A', 'P'));
        wrl(t);
        pos=lseek(outfd, 0L, 1);
    } else
        lseek(outfd, pos, 0);
    cmbuf=Malloc(t);
    for (i=0, j=0; i<nr; ++i) {
        cmbuf[j++]=(cm[i]&0x0f00)>>4;
        cmbuf[j++]= cm[i]&0x00f0;
        cmbuf[j++]=(cm[i]&0x000f)<<4;
    }
    cwrite(cmbuf, (int)t);
    free(cmbuf);
}


#ifdef __STDC__
PRIVATE write_sham(void)
#else
PRIVATE write_sham()
#endif
{
    long t;
    static long pos= -1;
    short version;

    if (!slicedo)
	return;
    if (xmode==EHB)
	error0(E0_FATAL, E1_IFF, E2_EHB, E3_SLICED_EHB);
    t=(ymode==LACE && sliced==SLICED_SHAM ? (ysz+1)/2 : ysz)*nregs*sizeof(u_short);
    if (pos== -1) {
	if (sliced==SLICED_SHAM) {
	        wrl(id('S', 'H', 'A', 'M'));
	        wrl(t+2);
        	version=0;
        	cwrite(&version, 2);
	} else {
	        wrl(id('C', 'T', 'B', 'L'));
	        wrl(t);
	}
        pos=lseek(outfd, 0L, 1);
    } else
        lseek(outfd, pos, 0);
    cwrite(cm, (int)t);
}


#ifdef __STDC__
write_body(void)
#else
write_body()
#endif
{
    int y, step;
#ifdef __STDC__
    int (*outfunc)(int);
#else
    int (*outfunc)();
#endif

    if (directrgb<0)
	fillconv();
    if (xmode==HAM)
	assert(nplanes==6);
    if (!inoperation) {
        wrl(id('B', 'O', 'D', 'Y'));
        wrl(0L);
    }
    bytesperrow=(xsz+15)/16*2;
    noutrows=MAXOUTBUFSZ/(bytesperrow*nplanes);
    if (noutrows==0)
        noutrows=1;
    outbufsz=noutrows*bytesperrow*nplanes;
    outrows=Malloc(outbufsz);
    if (compression)
	outcrows=Malloc(outbufsz*3/2);
    body_size=0;
    curcm=cm;
    if (xmode==HAM) {
	fill_prgbtab();
        outfunc=ham_row_out;
    } else
        outfunc=row_out;
    if (directrgb>=0) {
	nzero=nplanes/3-4;
	if (nzero<0)
		nzero=0;
	ncolor=nplanes/3;
	if (ncolor>4)
		ncolor=4;
	init_counter(0, (int)ysz, 10, NULL);
	for (y=0; y<ysz; ++y)
		rgb_row_out(y);
    } else if (slicedo) {
        if (ymode==LACE && sliced==SLICED_SHAM)
            step=2;
        else
            step=1;
	init_counter(0, (int)ysz, 5, NULL);
        for (y=0; y<ysz; y+=step) {
            if (y==ysz-1)
                step=1;
            count_pixels(y, y+step-1, 0);
            icolors=count_colors(threshold);
            if (icolors<nregs && threshold>1)
                    printf("in row %d (and %d) only %d colors occur at least %ld times\n",
                     y, y+step-1, icolors, threshold);
            compute_distr(y, y+step-1);
            outfunc(y);
            if (step==2)
                outfunc(y+1);
            curcm+=16;
        }
    } else {
        compute_distr(0, (int)ysz-1);
	init_counter(0, (int)ysz, 10, NULL);
        for (y=0; y<ysz; ++y)
            outfunc(y);
    }
    if (!inoperation)
        (void)next_row(1); /* to flush the row buffer */
    erase_counter(NULL);
}


#ifdef __STDC__
PRIVATE fillconv(void)
#else
PRIVATE fillconv()
#endif
{
    short i, j, k;
    u_char *p;

    conv=Malloc(npixvals*8*MAXNPLANES);
    p=conv;
    for (i=0; i<npixvals; ++i) {
        for (j=128; j; j>>=1) {
            for (k=0; k<nplanes; ++k) {
                if (i&(1<<k))
                    *p++ =j;
                else
                    *p++ =0;
            }
            p+=MAXNPLANES-nplanes;
        }
    }
}


#ifdef __STDC__
PRIVATE int cmpcolregs(short *p1, short *p2)
#else
PRIVATE int cmpcolregs(p1, p2)
short *p1, *p2;
#endif
{
    short c1, c2, i;

    c1= *p1;
    c2= *p2;
    i=((c1>>8) + ((c1&0x00f0)>>4) + (c1&0x000f))
    - ((c2>>8) + ((c2&0x00f0)>>4) + (c2&0x000f));
    if (i)
        return (int)i;
    i=(c1>>8)-(c2>>8);
    if (i)
        return (int)i;
    i=((c1&0x00f0)>>4)-((c2&0x00f0)>>4);
    if (i)
        return (int)i;
    i=(c1&0x000f)-(c2&0x000f);
    return (int)i;
}


#ifdef __STDC__
sort_cm(void)
#else
sort_cm()
#endif
{
    qsort((char *)curcm, (int)nregs, sizeof(u_short), cmpcolregs);
}


static char *qm="?";


static char *sliceds[]={
    "ok",
    "not",
    "maybe",
    NULL
};
#ifdef __STDC__
char *slicednam(int i)
#else
char *slicednam(i)
int i;
#endif
{
    if (i<0 || i>2)
        return qm;
    return sliceds[i];
}


static char *xmodes[]={
    "unset",
    "hires",
    "lores",
    "ehb",
    "ham",
    NULL
};
#ifdef __STDC__
char *xmodenam(int i)
#else
char *xmodenam(i)
int i;
#endif
{
    if (i<0 || i>4)
        return qm;
    return xmodes[i];
}


static char *ymodes[]={
    "unset",
    "lace",
    "nolace",
    NULL
};
#ifdef __STDC__
char *ymodenam(int i)
#else
char *ymodenam(i)
int i;
#endif
{
    if (i<0 || i>2)
        return qm;
    return ymodes[i];
}


/* should be in same order as DISTRMETH_ defines in wasp.h */
static char *dmeths[]={
    "unset",
    "mu",
    "wf",
    "ehb",
    "mue",
    "hs",
    "con",
    NULL
};
#ifdef __STDC__
char *dmethnam(int i)
#else
char *dmethnam(i)
int i;
#endif
{
    if (i<0 || i>6)
        return qm;
    return dmeths[i];
}
#ifdef __STDC__
int dmethnum(char *s)
#else
int dmethnum(s)
char *s;
#endif
{
	int i;

	for (i=1; dmeths[i]; ++i)
		if (!strcmp(s, dmeths[i]))
			return i;
	return -1;
}


/* should be in same order as COUNTMETH_ defines in wasp.h */
static char *cmeths[]={
    "unset",
    "all1",
    "alldif",
    "allfdif",
    "j1",
    "j21",
    "jdif",
    "jdifsh",
    "jfdif",
    "jfdifsh",
    "hmgs",
    "hmcubic",
    "hm",
    NULL
};
#ifdef __STDC__
char *cmethnam(int i)
#else
char *cmethnam(i)
int i;
#endif
{
    if (i<0 || i>12)
        return qm;
    return cmeths[i];
}
#ifdef __STDC__
int cmethnum(char *s)
#else
int cmethnum(s)
char *s;
#endif
{
	int i;

	for (i=1; cmeths[i]; ++i)
		if (!strcmp(s, cmeths[i]))
			return i;
	return -1;
}
