//========================================================================
//
// SplashClip.cc
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <string.h>
#include "gmem.h"
#include "SplashErrorCodes.h"
#include "SplashMath.h"
#include "SplashPath.h"
#include "SplashXPath.h"
#include "SplashXPathScanner.h"
#include "SplashClip.h"

//------------------------------------------------------------------------
// SplashClip.flags
//------------------------------------------------------------------------

#define splashClipEO       0x01	// use even-odd rule

//------------------------------------------------------------------------
// SplashClip
//------------------------------------------------------------------------

SplashClip::SplashClip(SplashCoord x0, SplashCoord y0,
		       SplashCoord x1, SplashCoord y1) {
  if (x0 < x1) {
    xMin = splashFloor(x0);
    xMax = splashFloor(x1);
  } else {
    xMin = splashFloor(x1);
    xMax = splashFloor(x0);
  }
  if (y0 < y1) {
    yMin = splashFloor(y0);
    yMax = splashFloor(y1);
  } else {
    yMin = splashFloor(y1);
    yMax = splashFloor(y0);
  }
  paths = NULL;
  flags = NULL;
  scanners = NULL;
  length = size = 0;
}

SplashClip::SplashClip(SplashClip *clip) {
  int i;

  xMin = clip->xMin;
  yMin = clip->yMin;
  xMax = clip->xMax;
  yMax = clip->yMax;
  length = clip->length;
  size = clip->size;
  paths = (SplashXPath **)gmallocn(size, sizeof(SplashXPath *));
  flags = (Guchar *)gmallocn(size, sizeof(Guchar));
  scanners = (SplashXPathScanner **)
                 gmallocn(size, sizeof(SplashXPathScanner *));
  for (i = 0; i < length; ++i) {
    paths[i] = clip->paths[i]->copy();
    flags[i] = clip->flags[i];
    scanners[i] = new SplashXPathScanner(paths[i], flags[i] & splashClipEO);
  }
}

SplashClip::~SplashClip() {
  int i;

  for (i = 0; i < length; ++i) {
    delete paths[i];
    delete scanners[i];
  }
  gfree(paths);
  gfree(flags);
  gfree(scanners);
}

void SplashClip::grow(int nPaths) {
  if (length + nPaths > size) {
    if (size == 0) {
      size = 32;
    }
    while (size < length + nPaths) {
      size *= 2;
    }
    paths = (SplashXPath **)greallocn(paths, size, sizeof(SplashXPath *));
    flags = (Guchar *)greallocn(flags, size, sizeof(Guchar));
    scanners = (SplashXPathScanner **)
                   greallocn(scanners, size, sizeof(SplashXPathScanner *));
  }
}

void SplashClip::resetToRect(SplashCoord x0, SplashCoord y0,
			     SplashCoord x1, SplashCoord y1) {
  int i;

  for (i = 0; i < length; ++i) {
    delete paths[i];
    delete scanners[i];
  }
  gfree(paths);
  gfree(flags);
  gfree(scanners);
  paths = NULL;
  flags = NULL;
  scanners = NULL;
  length = size = 0;

  if (x0 < x1) {
    xMin = splashFloor(x0);
    xMax = splashFloor(x1);
  } else {
    xMin = splashFloor(x1);
    xMax = splashFloor(x0);
  }
  if (y0 < y1) {
    yMin = splashFloor(y0);
    yMax = splashFloor(y1);
  } else {
    yMin = splashFloor(y1);
    yMax = splashFloor(y0);
  }
}

SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0,
				   SplashCoord x1, SplashCoord y1) {
  int x0I, y0I, x1I, y1I;

  if (x0 < x1) {
    x0I = splashFloor(x0);
    x1I = splashFloor(x1);
  } else {
    x0I = splashFloor(x1);
    x1I = splashFloor(x0);
  }
  if (x0I > xMin) {
    xMin = x0I;
  }
  if (x1I < xMax) {
    xMax = x1I;
  }
  if (y0 < y1) {
    y0I = splashFloor(y0);
    y1I = splashFloor(y1);
  } else {
    y0I = splashFloor(y1);
    y1I = splashFloor(y0);
  }
  if (y0I > yMin) {
    yMin = y0I;
  }
  if (y1I < yMax) {
    yMax = y1I;
  }
  return splashOk;
}

SplashError SplashClip::clipToPath(SplashPath *path, SplashCoord flatness,
				   GBool eo) {
  SplashXPath *xPath;

  xPath = new SplashXPath(path, flatness, gTrue);

  // check for an empty path
  if (xPath->length == 0) {
    xMax = xMin - 1;
    yMax = yMin - 1;
    delete xPath;

  // check for a rectangle
  } else if (xPath->length == 4 &&
      ((xPath->segs[0].x0 == xPath->segs[0].x1 &&
	xPath->segs[0].x0 == xPath->segs[1].x0 &&
	xPath->segs[0].x0 == xPath->segs[3].x1 &&
	xPath->segs[2].x0 == xPath->segs[2].x1 &&
	xPath->segs[2].x0 == xPath->segs[1].x1 &&
	xPath->segs[2].x0 == xPath->segs[3].x0 &&
	xPath->segs[1].y0 == xPath->segs[1].y1 &&
	xPath->segs[1].y0 == xPath->segs[0].y1 &&
	xPath->segs[1].y0 == xPath->segs[2].y0 &&
	xPath->segs[3].y0 == xPath->segs[3].y1 &&
	xPath->segs[3].y0 == xPath->segs[0].y0 &&
	xPath->segs[3].y0 == xPath->segs[2].y1) ||
       (xPath->segs[0].y0 == xPath->segs[0].y1 &&
	xPath->segs[0].y0 == xPath->segs[1].y0 &&
	xPath->segs[0].y0 == xPath->segs[3].y1 &&
	xPath->segs[2].y0 == xPath->segs[2].y1 &&
	xPath->segs[2].y0 == xPath->segs[1].y1 &&
	xPath->segs[2].y0 == xPath->segs[3].y0 &&
	xPath->segs[1].x0 == xPath->segs[1].x1 &&
	xPath->segs[1].x0 == xPath->segs[0].x1 &&
	xPath->segs[1].x0 == xPath->segs[2].x0 &&
	xPath->segs[3].x0 == xPath->segs[3].x1 &&
	xPath->segs[3].x0 == xPath->segs[0].x0 &&
	xPath->segs[3].x0 == xPath->segs[2].x1))) {
    clipToRect(xPath->segs[0].x0, xPath->segs[0].y0,
	       xPath->segs[2].x0, xPath->segs[2].y0);
    delete xPath;

  } else {
    grow(1);
    xPath->sort();
    paths[length] = xPath;
    flags[length] = eo ? splashClipEO : 0;
    scanners[length] = new SplashXPathScanner(xPath, eo);
    ++length;
  }

  return splashOk;
}

GBool SplashClip::test(int x, int y) {
  int i;

  // check the rectangle
  if (x < xMin || x > xMax || y < yMin || y > yMax) {
    return gFalse;
  }

  // check the paths
  for (i = 0; i < length; ++i) {
    if (!scanners[i]->test(x, y)) {
      return gFalse;
    }
  }

  return gTrue;
}

SplashClipResult SplashClip::testRect(int rectXMin, int rectYMin,
				      int rectXMax, int rectYMax) {
  if (rectXMax < xMin || rectXMin > xMax ||
      rectYMax < yMin || rectYMin > yMax) {
    return splashClipAllOutside;
  }
  if (rectXMin >= xMin && rectXMax <= xMax &&
      rectYMin >= yMin && rectYMax <= yMax &&
      length == 0) {
    return splashClipAllInside;
  }
  return splashClipPartial;
}

SplashClipResult SplashClip::testSpan(int spanXMin, int spanXMax, int spanY) {
  int i;

  if (spanXMax < xMin || spanXMin > xMax ||
      spanY < yMin || spanY > yMax) {
    return splashClipAllOutside;
  }
  if (!(spanXMin >= xMin && spanXMax <= xMax &&
	spanY >= yMin && spanY <= yMax)) {
    return splashClipPartial;
  }
  for (i = 0; i < length; ++i) {
    if (!scanners[i]->testSpan(xMin, xMax, spanY)) {
      return splashClipPartial;
    }
  }
  return splashClipAllInside;
}
