#define	LIBQTOOLS_CORE
#include "../include/libqtools.h"

/*
 * pmalloc/pfree
 */

struct palpic *pmalloc(register short int width, register short int height, register struct rgb *palette, register char *picName)
{
  struct palpic *Picture = 0;

  if ((Picture = (struct palpic *)tmalloc((width * height) + sizeof(struct palpic)))) {
    Picture->width = width;
    Picture->height = height;
    if (!palette)
      palette = GetPalette();
    Picture->palette = palette;
    if (!picName)
      picName = "nameLess";
    Picture->name = smalloc(picName);
  }
  else
    eprintf(failed_memory, (width * height) + sizeof(struct palpic), "palpicture");

  return Picture;
}

bool pfree(register struct palpic * Picture)
{
  if (Picture) {
    if (Picture->palette)
      tfree(Picture->palette);
    if (Picture->name)
      tfree(Picture->name);
    tfree(Picture);
    return TRUE;
  }
  else
    return FALSE;
}

struct rawdata *rmalloc(register int size, register char *rawName)
{
  struct rawdata *rawData = 0;

  if ((rawData = (struct rawdata *)tmalloc(size + sizeof(struct rawdata)))) {
    rawData->size = size;
    if (!rawName)
      rawName = "nameLess";
    rawData->name = smalloc(rawName);
  }
  else
    eprintf(failed_memory, size + sizeof(struct rawdata), "raw data");

  return rawData;
}

void rfree(register struct rawdata *rawData)
{
  if (rawData) {
    if (rawData->name)
      tfree(rawData->name);
    tfree(rawData);
  }
}

/*
 * palette
 */

HANDLE palFile = 0, colrFile = 0, transFile = 0;
short int darkness = 0, transparency = -1;
struct rgb *cachedPalette = 0;
unsigned char *cachedColormap = 0;
unsigned char *cachedTransparency = 0;

unsigned char *GetTransparency(int transVal)
{
  char transTemplate[NAMELEN_PATH + 1] = "TArray.%2d";
  char transName[NAMELEN_PATH + 1] = "TArray.??";
  unsigned char *NewTransparency;

  if ((NewTransparency = (unsigned char *)tmalloc(256 * 256 * sizeof(unsigned char)))) {
    if (!cachedTransparency || (transVal != transparency)) {
      sprintf(transName, transTemplate, transVal);
      if ((transFile = __open(transName, H_READ_BINARY)) < 0) {
	char *transBase;

	if ((transBase = getenv("QUAKE_TRANSPARENCY"))) {
	  __strncpy(transName, transBase, NAMELEN_PATH);
	  __strncat(transName, transTemplate, NAMELEN_PATH);
	  __strcpy(transTemplate, transName);
	  sprintf(transName, transTemplate, transVal);
	  transFile = __open(transName, H_READ_BINARY);
	}
      }

      if (transFile < 0) {
	int i, j;
	struct rgb *actPel;

	mprintf("build transparency for density %2d\n", transVal);

	for (i = 0; i < 256; i++) {
	  actPel = &cachedPalette[i];
	  for (j = 0; j < 256; j++) {
	    int R, G, B;
	    struct rgb matchPel;

	    R = ((((int)(cachedPalette[j].r - actPel->r) * transVal) / 100) + (int)actPel->r);
	    G = ((((int)(cachedPalette[j].g - actPel->g) * transVal) / 100) + (int)actPel->g);
	    B = ((((int)(cachedPalette[j].b - actPel->b) * transVal) / 100) + (int)actPel->b);

	    matchPel.r = (unsigned char)R;
	    matchPel.g = (unsigned char)G;
	    matchPel.b = (unsigned char)B;
	    NewTransparency[(i << 8) + j] = Match(&matchPel, cachedPalette);
	  }
	  mprogress(256, i + 1);
	}

	if ((transFile = __open(transName, H_WRITE_BINARY)) > 0)
	  __write(transFile, NewTransparency, 256 * 256 * sizeof(unsigned char));
      }
      else
	__read(transFile, NewTransparency, 256 * 256 * sizeof(unsigned char));

      if (transFile) {
	__close(transFile);
	transFile = 0;
      }
      if (cachedTransparency)					/* release wrong cached transval */
	tfree(cachedTransparency);

      transparency = transVal;
      cachedTransparency = NewTransparency;
      NewTransparency = GetTransparency(transVal);
    }
    else
      __memcpy(NewTransparency, cachedTransparency, 256 * 256 * sizeof(unsigned char));
  }
  return NewTransparency;
}

struct rgb *GetDarkness(struct rgb *Palette)
{
  struct rgb *NewPalette;

  if ((NewPalette = (struct rgb *)tmalloc(256 * 3))) {
    short int i;

    if (!cachedColormap) {
      bool closecolr = FALSE;
      unsigned char *DarkLevel;

      if ((DarkLevel = (unsigned char *)tmalloc(256 * 64))) {

	if (!colrFile) {
	  if ((colrFile = __open("colormap.lmp", H_READ_BINARY)) < 0) {
	    char *colrName;

	    if ((colrName = getenv("QUAKE_COLORMAP"))) {
	      if ((colrFile = __open(colrName, H_READ_BINARY)) < 0)
		eprintf("no colormap available, build new from scratch\n");
	      else
		closecolr = TRUE;
	    }
	    else
	      eprintf("no colormap available, build new from scratch\n");
	  }
	  else
	    closecolr = TRUE;
	}

	if (colrFile) {
	  __read(colrFile, DarkLevel, 256 * 64);
	  if (closecolr)
	    __close(colrFile);
	  else
	    __lseek(colrFile, 0, SEEK_SET);
	}
	else {
	  /* TODO: how to build colormap */
	}

	cachedColormap = DarkLevel;
      }
      else {
	eprintf(failed_memory, 256 * 64, "colormap");
	return Palette;
      }
    }

    for (i = 256; i < 256; i++)
      NewPalette[i] = Palette[cachedColormap[i]];

    tfree(Palette);
  }

  if (NewPalette)
    return NewPalette;
  else
    return Palette;
}

struct rgb *GetPalette(void)
{
  struct rgb *Palette;

  if ((Palette = (struct rgb *)tmalloc(256 * 3))) {
    if (!cachedPalette) {
      bool closepal = FALSE;

      if (!palFile) {
	if ((palFile = __open("palette.lmp", H_READ_BINARY)) < 0) {
	  char *palName;

	  if ((palName = getenv("QUAKE_PALETTE"))) {
	    if ((palFile = __open(palName, H_READ_BINARY)) < 0) {
	      eprintf("no palette available\n");
	      tfree(Palette);
	      return 0;
	    }
	    else
	      closepal = TRUE;
	  }
	  else {
	    eprintf("no palette available\n");
	    tfree(Palette);
	    return 0;
	  }
	}
	else
	  closepal = TRUE;
      }

      __read(palFile, Palette, 256 * 3);

      if (closepal)
	__close(palFile);
      else
	__lseek(palFile, 0, SEEK_SET);

      /*
       * small trick
       */
      cachedPalette = Palette;
      Palette = GetPalette();
    }
    else
      __memcpy(Palette, cachedPalette, 256 * 3);
  }

  if (darkness)
    Palette = GetDarkness(Palette);

  return Palette;
}

/*
 * Lumps
 */
struct palpic *GetLMP(register HANDLE file, register char *lmpName)
{
  struct lump Lump;
  struct palpic *Picture;

  __read(file, &Lump, sizeof(struct lump));

  if ((Picture = pmalloc(LittleLong(Lump.width), LittleLong(Lump.height), 0, lmpName)))
    __read(file, Picture->rawdata, Picture->width * Picture->height);

  return Picture;
}

struct palpic *ParseLMP(register struct lump *Lump, register char *lmpName)
{
  struct palpic *Picture;

  if ((Picture = pmalloc(LittleLong(Lump->width), LittleLong(Lump->height), 0, lmpName)))
    __memcpy(Picture->rawdata, Lump->rawdata, Picture->width * Picture->height);

  return Picture;
}

bool PutLMP(register HANDLE file, register struct palpic * Picture)
{
  struct lump Lump;
  int size = sizeof(struct lump) + (Picture->width * Picture->height);

  Lump.width = LittleLong(Picture->width);
  Lump.height = LittleLong(Picture->height);
  __write(file, &Lump, 2 * sizeof(int));

  if (__write(file, Picture->rawdata, size) != size)
    return FALSE;
  else
    return TRUE;
}

bool PasteLMP(register struct lump * Lump, register struct palpic * Picture)
{
  Lump->width = LittleLong(Picture->width);
  Lump->height = LittleLong(Picture->height);
  __memcpy(((char *)Lump) + sizeof(struct lump), Picture->rawdata, (Picture->width * Picture->height));

  return TRUE;
}

/*
 * compressed
 */

char Compression = CMP_NONE;

char *GetLZ77(register HANDLE file, register int readsize)
{
  char *inData, *outData = 0;

  if (!readsize) {
    int oldoffset = __ltell(file);

    __lseek(file, 0, SEEK_END);
    readsize = __ltell(file);
    __lseek(file, oldoffset, SEEK_SET);
  }

  if ((inData = (char *)tmalloc(readsize))) {
    int size;

    __read(file, inData, readsize);
    if ((size = LZWSSize(inData)) > ERROR) {
      mprintf("prepare to decrunch %d bytes ... \n", size);
      if ((outData = (char *)tmalloc(size))) {
	if (LZWSDecrunch(size, inData, outData) < ERROR) {
	  eprintf("failed to decrunch\n");
	  tfree(outData);
	  outData = 0;
	}
      }
      else
	eprintf(failed_memory, size, "decrunch");
    }
    tfree(inData);
  }
  else
    eprintf(failed_memory, readsize, "read crunched");

  return outData;
}

char *ParseLZ77(register char *inData, register int readsize)
{
  char *outData = 0;

  if (readsize > 0) {
    int size;

    if ((size = LZWSSize(inData)) > ERROR) {
      mprintf("prepare to decrunch %d bytes ... \n", size);
      if ((outData = (char *)tmalloc(size))) {
	if (LZWSDecrunch(size, inData, outData) < ERROR) {
	  eprintf("failed to decrunch\n");
	  tfree(outData);
	  outData = 0;
	}
      }
      else
	eprintf(failed_memory, size, "decrunch");
    }
  }

  return outData;
}

int PutLZ77(register HANDLE file, register char *inData, register int size)
{
  char *outData, *saveData;
  int retval = -1;

  if ((outData = (char *)tmalloc(size))) {
    if ((retval = LZWSCrunch(size, size, size * 2, inData, outData)) > ERROR) {
      mprintf("crunched %d to %d (%d%%)\n", size, retval, ((retval * 100) / size));
      if (__write(file, outData, retval) != retval)
	retval = -1;

      if ((saveData = (char *)tmalloc(size))) {
	int error, byte;

	if ((error = LZWSDecrunch(size, outData, saveData)) > ERROR) {
	  for (byte = 0; byte < size; byte++) {
	    if (inData[byte] != saveData[byte])
	      break;
	  }
	  if (byte != size)
	    eprintf("difference at %d\n", byte);
	}
	tfree(saveData);
      }
    }
    tfree(outData);
  }
  else
    eprintf(failed_memory, size, "write crunched");

  return retval;
}

int PasteLZ77(register char *outData, register char *inData, register int size)
{
  return LZWSCrunch(size, size, size * 2, inData, outData);
}

/*
 * Unknown/Raw
 */
struct rawdata *GetRaw(register HANDLE file, register char *rawName, register int size)
{
  struct rawdata *rawData = 0;

  if (!size) {
    int oldoffset = __ltell(file);

    __lseek(file, 0, SEEK_END);
    size = __ltell(file);
    __lseek(file, oldoffset, SEEK_SET);
  }

  if ((rawData = rmalloc(size + 1, rawName))) {
    __read(file, rawData->rawdata, size);
    rawData->size--;
  }

  return rawData;
}

struct rawdata *ParseRaw(register char *mem, register char *rawName, register int size)
{
  struct rawdata *rawData = 0;

  if (size)
    if ((rawData = rmalloc(size + 1, rawName))) {
      __memcpy(rawData->rawdata, mem, size);
      rawData->size--;
    }

  return rawData;
}

bool PutRaw(register HANDLE file, register struct rawdata * rawData)
{
  if (__write(file, rawData->rawdata, rawData->size) != rawData->size)
    return FALSE;
  else
    return TRUE;
}

bool PasteRaw(register char *mem, register struct rawdata * rawData)
{
  __memcpy(mem, rawData->rawdata, rawData->size);
  return TRUE;
}
