#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "wv.h"
#include "bintree.h"

/*
 * To apply a UPX.chpx to a UPE.chp, apply the UPX.chpx.grpprl to
 * UPE.chp. Note that a UPE.chp for a paragraph style should always have
 * UPE.chp.istd == istdNormalChar.
 */
void
wvAddCHPXFromBucket (CHP * achp, UPXF * upxf, STSH * stsh)
{
    U8 *pointer;
    U16 i = 0;
    U16 sprm;

#ifdef SPRMTEST
    fprintf (stderr, "\n");
    while (i < upxf->cbUPX)
      {
	  fprintf (stderr, "%x (%d) ", *(upxf->upx.chpx.grpprl + i),
		   *(upxf->upx.chpx.grpprl + i));
	  i++;
      }
    fprintf (stderr, "\n");
    i = 0;
#endif
    while (i + 2 < upxf->cbUPX) /* is this check sufficient ?? */
      {
	  sprm = bread_16ubit (upxf->upx.chpx.grpprl + i, &i);
#ifdef SPRMTEST
	  wvError (("sprm is %x, i is %d\n", sprm, i));
#endif
	  pointer = upxf->upx.chpx.grpprl + i;
	  wvApplySprmFromBucket (WORD8, sprm, NULL, achp, NULL, stsh, pointer,
				 &i, NULL);
      }
}

void
wvApplyCHPXFromBucket (CHP * achp, CHPX * chpx, STSH * stsh)
{
    U8 *pointer;
    U16 i = 0;
    U16 sprm;
#ifdef SPRMTEST
    fprintf (stderr, "\n");
    while (i < chpx->cbGrpprl)
      {
	  fprintf (stderr, "%x (%d) ", *(chpx->grpprl + i),
		   *(chpx->grpprl + i));
	  i++;
      }
    fprintf (stderr, "\n");
    i = 0;
#endif
    while (i < chpx->cbGrpprl)
      {
	  sprm = bread_16ubit (chpx->grpprl + i, &i);
	  wvTrace (("the sprm is %d\n", sprm));
	  pointer = chpx->grpprl + i;
	  wvApplySprmFromBucket (WORD8, sprm, NULL, achp, NULL, stsh, pointer,
				 &i, NULL);
      }
    achp->istd = chpx->istd;
}

void
wvAddCHPXFromBucket6 (CHP * achp, UPXF * upxf, STSH * stsh)
{
    U8 *pointer;
    U16 i = 0;
    U8 sprm8;
    U16 sprm;
    wvTrace (("cbUPX word 6 is %d\n", upxf->cbUPX));

#ifdef SPRMTEST
    fprintf (stderr, "\n");
    while (i < upxf->cbUPX)
      {
	  fprintf (stderr, "%x (%d) ", *(upxf->upx.chpx.grpprl + i),
		   *(upxf->upx.chpx.grpprl + i));
	  i++;
      }
    fprintf (stderr, "\n");
    i = 0;
#endif
    while (i < upxf->cbUPX)
      {
	  sprm8 = bread_8ubit (upxf->upx.chpx.grpprl + i, &i);
#ifdef SPRMTEST
	  wvError (("chp word 6 sprm is %x (%d)\n", sprm8, sprm8));
#endif
	  sprm = (U16) wvGetrgsprmWord6 (sprm8);
#ifdef SPRMTEST
	  wvError (("chp word 6 sprm is converted to %x\n", sprm));
#endif

	  pointer = upxf->upx.chpx.grpprl + i;
	  wvApplySprmFromBucket (WORD6, sprm, NULL, achp, NULL, stsh, pointer,
				 &i, NULL);
      }
}


void
wvInitCHPFromIstd (CHP * achp, U16 istdBase, STSH * stsh)
{
    wvTrace (("initing from %d\n", istdBase));
    if (istdBase == istdNil)
	wvInitCHP (achp);
    else
      {
	  if (istdBase >= stsh->Stshi.cstd)
	    {
		wvError (
			 ("ISTD out of bounds, requested %d of %d\n",
			  istdBase, stsh->Stshi.cstd));
		wvInitCHP (achp);	/*it can't hurt to try and start with a blank istd */
		return;
	    }
	  else
	    {
		if (stsh->std[istdBase].cupx == 0)	/*empty slot in the array, i don't think this should happen */
		  {
		      wvTrace (("Empty style slot used (chp)\n"));
		      wvInitCHP (achp);
		  }
		else
		  {
		      wvTrace (("type is %d\n", stsh->std[istdBase].sgc));
		      switch (stsh->std[istdBase].sgc)
			{
			case sgcPara:
			    wvCopyCHP (achp,
				       &(stsh->std[istdBase].grupe[1].achp));
			    break;
			case sgcChp:
			    wvInitCHP (achp);
			    wvApplyCHPXFromBucket (achp,
						   &(stsh->std[istdBase].
						     grupe[0].chpx), stsh);
				strncpy(achp->stylename,stsh->std[istdBase].xstzName, sizeof(achp->stylename));
			    break;
			}
		  }
	    }
      }

}



/*
The standard CHP is all zeros except:

hps               20 half-points
fcPic             -1
istd              10 (the standard character style)
lidDefault, lidFE 0x0400 (no proofing)
wCharScale        100
fUsePgsuSettings  -1
*/

void
wvInitCHP (CHP * item)
{
    int i;

    item->fBold = 0;
    item->fItalic = 0;
    item->fRMarkDel = 0;
    item->fOutline = 0;
    item->fFldVanish = 0;
    item->fSmallCaps = 0;
    item->fCaps = 0;
    item->fVanish = 0;
    item->fRMark = 0;
    item->fSpec = 0;
    item->fStrike = 0;
    item->fObj = 0;
    item->fShadow = 0;
    item->fLowerCase = 0;
    item->fData = 0;
    item->fOle2 = 0;
    item->fEmboss = 0;
    item->fImprint = 0;
    item->fDStrike = 0;
    item->fUsePgsuSettings = -1;     /*-1 ? */
    item->reserved1 = 0;
    item->reserved2 = 0;
    item->reserved11 = 0;
    item->ftc = 0;
    item->ftcAscii = 0;
    item->ftcFE = 0;
    item->ftcOther = 0;
    item->hps = 20;
    item->dxaSpace = 0;
    item->iss = 0;
    item->kul = 0;
    item->fSpecSymbol = 0;
    item->ico = 0;
    item->reserved3 = 0;
    item->fSysVanish = 0;
    item->hpsPos = 0;
    item->super_sub = 0;
    item->lid = 0;
    item->lidDefault = 0x0400;
    item->lidFE = 0x0400;
    item->idct = 0;
    item->idctHint = 0;
    item->wCharScale = 100;
    item->fcPic_fcObj_lTagObj = -1;
    item->ibstRMark = 0;
    item->ibstRMarkDel = 0;

    wvInitDTTM (&item->dttmRMark);
    wvInitDTTM (&item->dttmRMarkDel);

    item->reserved4 = 0;
    item->istd = istdNormalChar;
    item->ftcSym = 0;
    item->xchSym = 0;
    item->idslRMReason = 0;
    item->idslReasonDel = 0;
    item->ysr = 0;
    item->chYsr = 0;
    item->cpg = 0;
    item->hpsKern = 0;
    item->icoHighlight = 0;
    item->fHighlight = 0;
    item->kcd = 0;
    item->fNavHighlight = 0;
    item->fChsDiff = 0;
    item->fMacChs = 0;
    item->fFtcAsciSym = 0;
    item->reserved5 = 0;
    item->fPropRMark = 0;
    item->ibstPropRMark = 0;

    wvInitDTTM (&item->dttmPropRMark);

    item->sfxtText = 0;
    item->reserved6 = 0;
    item->reserved7 = 0;
    item->reserved8 = 0;
    item->reserved9 = 0;

    wvInitDTTM (&item->reserved10);

    item->fDispFldRMark = 0;
    item->ibstDispFldRMark = 0;

    wvInitDTTM (&item->dttmDispFldRMark);

    for (i = 0; i < 16; i++)
	item->xstDispFldRMark[i] = 0;

    wvInitSHD (&item->shd);

    wvInitBRC (&item->brc);

    /* bidi */
    item->fBidi = 0;
    item->fBoldBidi = 0;
    item->fItalicBidi = 0;
    item->ftcBidi = 0;
    item->hpsBidi = 0;
    item->icoBidi = 0;
    item->lidBidi = 0;

	item->stylename[0] = 0;
}

void
wvCopyCHP (CHP * dest, CHP * src)
{
    int i;

    dest->fBold = src->fBold;
    dest->fItalic = src->fItalic;
    dest->fRMarkDel = src->fRMarkDel;
    dest->fOutline = src->fOutline;
    dest->fFldVanish = src->fFldVanish;
    dest->fSmallCaps = src->fSmallCaps;
    dest->fCaps = src->fCaps;
    dest->fVanish = src->fVanish;
    dest->fRMark = src->fRMark;
    dest->fSpec = src->fSpec;
    dest->fStrike = src->fStrike;
    dest->fObj = src->fObj;
    dest->fShadow = src->fShadow;
    dest->fLowerCase = src->fLowerCase;
    dest->fData = src->fData;
    dest->fOle2 = src->fOle2;
    dest->fEmboss = src->fEmboss;
    dest->fImprint = src->fImprint;
    dest->fDStrike = src->fDStrike;
    dest->fUsePgsuSettings = src->fUsePgsuSettings;
    dest->reserved1 = src->reserved1;
    dest->reserved2 = src->reserved2;
    dest->reserved11 = src->reserved11;
    dest->ftc = src->ftc;
    dest->ftcAscii = src->ftcAscii;
    dest->ftcFE = src->ftcFE;
    dest->ftcOther = src->ftcOther;
    dest->hps = src->hps;
    dest->dxaSpace = src->dxaSpace;
    dest->iss = src->iss;
    dest->kul = src->kul;
    dest->fSpecSymbol = src->fSpecSymbol;
    dest->ico = src->ico;
    dest->reserved3 = src->reserved3;
    dest->fSysVanish = src->fSysVanish;
    dest->hpsPos = src->hpsPos;
    dest->super_sub = src->super_sub;
    dest->lid = src->lid;
    dest->lidDefault = src->lidDefault;
    dest->lidFE = src->lidFE;
    dest->idct = src->idct;
    dest->idctHint = src->idctHint;
    dest->wCharScale = src->wCharScale;
    dest->fcPic_fcObj_lTagObj = src->fcPic_fcObj_lTagObj;
    dest->ibstRMark = src->ibstRMark;
    dest->ibstRMarkDel = src->ibstRMarkDel;

    wvCopyDTTM (&dest->dttmRMark, &src->dttmRMark);
    wvCopyDTTM (&dest->dttmRMarkDel, &src->dttmRMarkDel);

    dest->reserved4 = src->reserved4;
    dest->istd = src->istd;
    dest->ftcSym = src->ftcSym;
    dest->xchSym = src->xchSym;
    dest->idslRMReason = src->idslRMReason;
    dest->idslReasonDel = src->idslReasonDel;
    dest->ysr = src->ysr;
    dest->chYsr = src->chYsr;
    dest->cpg = src->cpg;
    dest->hpsKern = src->hpsKern;
    dest->icoHighlight = src->icoHighlight;
    dest->fHighlight = src->fHighlight;
    dest->kcd = src->kcd;
    dest->fNavHighlight = src->fNavHighlight;
    dest->fChsDiff = src->fChsDiff;
    dest->fMacChs = src->fMacChs;
    dest->fFtcAsciSym = src->fFtcAsciSym;
    dest->reserved5 = src->reserved5;
    dest->fPropRMark = src->fPropRMark;
    dest->ibstPropRMark = src->ibstPropRMark;

    wvCopyDTTM (&dest->dttmPropRMark, &src->dttmPropRMark);

    dest->sfxtText = src->sfxtText;
    dest->reserved6 = src->reserved6;
    dest->reserved7 = src->reserved7;
    dest->reserved8 = src->reserved8;
    dest->reserved9 = src->reserved9;

    wvCopyDTTM (&dest->reserved10, &src->reserved10);

    dest->fDispFldRMark = src->fDispFldRMark;
    dest->ibstDispFldRMark = src->ibstDispFldRMark;

    wvCopyDTTM (&dest->dttmDispFldRMark, &src->dttmDispFldRMark);

    for (i = 0; i < 16; i++)
	dest->xstDispFldRMark[i] = src->xstDispFldRMark[i];

    wvCopySHD (&dest->shd, &src->shd);

    wvCopyBRC (&dest->brc, &src->brc);

    /* bidi */
    dest->fBidi = src->fBidi;
    dest->fBoldBidi = src->fBoldBidi;
    dest->fItalicBidi = src->fItalicBidi;
    dest->ftcBidi = src->ftcBidi;
    dest->hpsBidi = src->hpsBidi;
    dest->icoBidi = src->icoBidi;
    dest->lidBidi = src->lidBidi;

	strcpy(dest->stylename,src->stylename);
}

/*
 * The chpx for the null style has an istd of zero, a cbGrpprl of zero 
 * (and an empty grpprl) this only exists in the UPD/UPE
 */
void
wvInitCHPX (CHPX * item)
{
    item->istd = 0;
    item->cbGrpprl = 0;
    item->grpprl = NULL;
}

/*
 * For a character style, the UPE.chpx can be constructed by starting with 
 * the first UPE from the based-on style (std.istdBase).
*/
void
wvInitCHPXFromIstd (CHPX * chpx, U16 istdBase, STSH * stsh)
{
    if (istdBase == istdNil)
	wvInitCHPX (chpx);
    else
      {
	  if (istdBase >= stsh->Stshi.cstd)
	    {
		wvError (
			 ("ISTD out of bounds, requested %d of %d\n",
			  istdBase, stsh->Stshi.cstd));
		wvInitCHPX (chpx);	/*it can't hurt to try and start with a blank istd */
		return;
	    }
	  else
	      wvCopyCHPX (chpx, &(stsh->std[istdBase].grupe[0].chpx));
      }
}

void
wvCopyCHPX (CHPX * dest, CHPX * src)
{
    int i;
    dest->istd = src->istd;
    dest->cbGrpprl = src->cbGrpprl;
    if (dest->cbGrpprl)
	dest->grpprl = (U8 *) wvMalloc (dest->cbGrpprl);
    else
	dest->grpprl = NULL;
    if (dest->grpprl == NULL)
	return;
    for (i = 0; i < dest->cbGrpprl; i++)
	dest->grpprl[i] = src->grpprl[i];
}

void
wvReleaseCHPX (CHPX * item)
{
    wvFree (item->grpprl);
}

int
wvCompLT (void *a, void *b)
{
    U8 *a2, *b2;
    U16 sprm1, sprm2;
    a2 = (U8 *) a;
    b2 = (U8 *) b;
    sprm1 = sread_16ubit (a2);
    sprm2 = sread_16ubit (b2);
    return (sprm1 < sprm2);
}

int
wvCompEQ (void *a, void *b)
{
    U8 *a2, *b2;
    U16 sprm1, sprm2;
    a2 = (U8 *) a;
    b2 = (U8 *) b;
    sprm1 = sread_16ubit (a2);
    sprm2 = sread_16ubit (b2);
    return (sprm1 == sprm2);
}


/*
Apply the first UPX (UPX.chpx) in std.grupx to the UPE. 

To apply a UPX.chpx to a UPE.chpx, take the grpprl in UPE.chpx.grpprl (which 
has a length of UPE.chpx.cbGrpprl) and merge the grpprl in UPX.chpx.grpprl 
into it. 

Merging grpprls is a tricky business, but for character styles it is easy 
because no prls in character style grpprls should interact with each other. 
Each prl from the source (the UPX.chpx.grpprl) should be inserted into the 
destination (the UPE.chpx.grpprl) so that the sprm of each prl is in increasing 
order, and any prls that have the same sprm are replaced by the prl in the 
source. 

UPE.chpx.cbGrpprl is then set to the length of resulting grpprl, and 
UPE.chpx.istd is set to the style's istd.
*/
void
wvMergeCHPXFromBucket (CHPX * dest, UPXF * src)
{
    BintreeInfo tree;
    Node *testn, *testp;
    U16 i = 0, j;
    U16 sprm;
    U8 len = 0;
    U8 temp;
    Node *test = NULL;

    U8 *pointer, *dpointer;
    U8 *grpprl = NULL;

    /*
       use a binary tree ala the wmf stuff and first insert every dest sprm into it,
       then insert every src sprm into it, take the full count and take them out of
       the tree and create the list from them
     */
    InitBintree (&tree, wvCompLT, wvCompEQ);
    pointer = dest->grpprl;

    while (i < dest->cbGrpprl)
      {
	  wvTrace (("gotcha the sprm is %x\n", *((U16 *) pointer)));
	  test = InsertNode (&tree, (void *) pointer);
	  sprm = dread_16ubit (NULL, &pointer);
	  wvTrace (("the sprm is %x\n", sprm));
	  temp = wvEatSprm (sprm, pointer, &i);
	  pointer += temp;
	  i += 2;
	  if (test)
	      len += temp + 2;
      }

    i = 0;
    pointer = src->upx.chpx.grpprl;
    i = 0;
    while (i < src->cbUPX)
      {
	  /*wvTrace(("gotcha 2 the sprm is %x\n",*((U16 *)pointer))); */
	  test = InsertNode (&tree, (void *) pointer);
	  sprm = dread_16ubit (NULL, &pointer);
	  i += 2;
	  wvTrace (("the sprm is %x\n", sprm));
	  temp = wvEatSprm (sprm, pointer, &i);
	  wvTrace (("len of op is %d\n", temp));
	  pointer += temp;
	  wvTrace (("p dis is %d\n", pointer - src->upx.chpx.grpprl));
	  if (test)
	      len += temp + 2;
      }

    if (len != 0)
	grpprl = (U8 *) wvMalloc (len);
    else
	return;


    dpointer = grpprl;

    testn = NextNode (&tree, NULL);
    while (testn != NULL)
      {
	  pointer = (U8 *) testn->Data;
	  sprm = sread_16ubit (pointer);
	  wvTrace (("methinks the sprm is %x\n", sprm));
	  pointer += 2;

	  i = 0;
	  wvEatSprm (sprm, pointer, &i);
	  wvTrace (("i is now %d\n", i));

	  pointer = (U8 *) testn->Data;
	  for (j = 0; j < i + 2; j++)
	      *dpointer++ = *pointer++;

	  testp = NextNode (&tree, testn);
	  wvDeleteNode (&tree, testn);
	  testn = testp;
      }
    wvFree (dest->grpprl);
    dest->grpprl = grpprl;
    dest->cbGrpprl = len;

    /*test */
    i = 0;
    pointer = dest->grpprl;
    while (i < dest->cbGrpprl)
      {
	  sprm = dread_16ubit (NULL, &pointer);
	  wvTrace (("final test the sprm is %x\n", sprm));
	  temp = wvEatSprm (sprm, pointer, &i);
	  pointer += temp;
	  i += 2;
	  if (test)
	      len += temp + 2;
      }
}


void
wvUpdateCHPXBucket (UPXF * src)
{
    U16 i = 0, j;
    U16 sprm;
    U8 sprm8;
    U16 len = 0;
    int temp;

    U8 *pointer, *dpointer;
    U8 *grpprl = NULL;

    i = 0;
    if (src->cbUPX == 0)
	return;
    pointer = src->upx.chpx.grpprl;
    wvTrace (("Msrc->cbUPX len is %d\n", src->cbUPX));
    for (i = 0; i < src->cbUPX; i++)
	wvTrace (("%x\n", src->upx.chpx.grpprl[i]));
    wvTrace (("Mend\n"));
    i = 0;
    len = 0;
    while (i < src->cbUPX)
      {
	  sprm8 = dread_8ubit (NULL, &pointer);
	  wvTrace (("Mpre the sprm is %x\n", sprm8));
	  sprm = (U16) wvGetrgsprmWord6 (sprm8);
	  wvTrace (("Mpost the sprm is %x\n", sprm));
	  i++;
	  len += 2;
	  temp = wvEatSprm (sprm, pointer, &i);
	  wvTrace (("Mlen of op is %d\n", temp));
	  pointer += temp;
	  wvTrace (("Mp dis is %d\n", pointer - src->upx.chpx.grpprl));
	  len += temp;
      }
    wvTrace (("Mlen ends up as %d\n", len));

    if (len == 0)
	return;

    grpprl = (U8 *) wvMalloc (len);

    dpointer = grpprl;

    i = 0;
    pointer = src->upx.chpx.grpprl;
    while (i < src->cbUPX)
      {
	  sprm8 = dread_8ubit (NULL, &pointer);
	  sprm = (U16) wvGetrgsprmWord6 (sprm8);
	  i++;
	  *dpointer++ = (sprm & 0x00FF);
	  *dpointer++ = (sprm & 0xff00) >> 8;
	  temp = wvEatSprm (sprm, pointer, &i);
	  for (j = 0; j < temp; j++)
	      *dpointer++ = *pointer++;
	  wvTrace (("Mlen of op is %d\n", temp));
      }
    wvFree (src->upx.chpx.grpprl);
    src->upx.chpx.grpprl = grpprl;
    src->cbUPX = len;
    for (i = 0; i < src->cbUPX; i++)
	wvTrace (("%x\n", src->upx.chpx.grpprl[i]));
}

/*
 * taken from wvAssembleSimplePAP in pap.c and modified
 * to handle CHP's
 * -JB
 */

int
wvAssembleSimpleCHP (wvVersion ver, CHP * achp, const PAP * apap, U32 fc, CHPX_FKP * fkp,
		     STSH * stsh)
{
    CHPX *chpx;
    int index;
    UPXF upxf;
    int ret = 0;
    U16 tistd;

	
    /* initialize CHP to para's stylesheet character properties this
       * should have resolved all the other stylesheet dependencies
       * for us, when the stsh's were initialized. */

	wvInitCHPFromIstd (achp, apap->istd, stsh);
	/* having done this, we want to set the achp->istd to nil, since
    any char istd value stored in the para style is not applicable
    (its a para style */
	achp->istd = istdNil;
	tistd = istdNil;
	
 apply_chpx:
	/* get CHPX */
	if(fkp)
	{
		/* the PAPX version of the function only looks at rgfc's, which are
		 * the same for CHPX and PAPX FKPs, so we'll reuse the function */
		index = wvGetIndexFCInFKP_PAPX ((PAPX_FKP *) fkp, fc);

		wvTrace (("index is %d, using %d\n", index, index - 1));

		chpx = &(fkp->grpchpx[index - 1]);

		/* apply CHPX from FKP */
		if ((chpx) && (chpx->cbGrpprl > 0))
		{
			ret = 1;
			/* for (i = 0; i < chpx->cbGrpprl; i++) */
			upxf.cbUPX = chpx->cbGrpprl;
			upxf.upx.chpx.grpprl = chpx->grpprl;
			if (ver == WORD8)
				wvAddCHPXFromBucket (achp, &upxf, stsh);
			else
				wvAddCHPXFromBucket6 (achp, &upxf, stsh);
		}

		if(achp->istd < stsh->Stshi.cstd) {
		  if (0 != stsh->std[achp->istd].xstzName) {
		    strncpy(achp->stylename,stsh->std[achp->istd].xstzName, sizeof(achp->stylename));
		  }
		  else {
		    wvError (("trying to copy null string\n"));
		  }
		}

		if(achp->istd != tistd)
		{
			/* the chpx contained instruction to apply character
			   style: we have to start all over again, since we init
			   the chp from the wrong (paragraph) style */
			tistd = achp->istd;
			wvInitCHPFromIstd (achp, achp->istd, stsh);
			goto apply_chpx;
		}
	}
	
    return (ret);
}


void
wvGetCHPX (wvVersion ver, CHPX * item, U8 * page, U16 * pos)
{
    U8 i;
    item->cbGrpprl = bread_8ubit (&(page[*pos]), pos);
    if (item->cbGrpprl > 0)
      {
	  item->grpprl = (U8 *) wvMalloc (item->cbGrpprl);
	  memcpy (item->grpprl, &(page[*pos]), item->cbGrpprl);
      }
    else
	item->grpprl = NULL;

    item->istd = 0;		/* I have no idea what to set this to... --
						   nothing; the istd is contained in the grpprl*/

    for (i = 0; i < item->cbGrpprl; i++)
	wvTrace (("chpx byte is %x\n", item->grpprl[i]));
}
