#include "GR_misc.h"
#include "GR_Face.h"
#include "GR_Vertex.h"
#include "GR_Geometry.h"
#include "GR_Vec.h"

void EdgeFace::vAssignVerts(Vert* const pV0, Vert* const pV1)
{
  assert(pV0->qValid());
  assert(pV1->qValid());

  apVVerts[0] = pV0;
  apVVerts[1] = pV1;
}

void EdgeFace::
vNormal (double *const adNorm)
  const {
  // For three space dimensions, the result is ambiguous, being
  // restricted only to a plane.  In this case, the vector returned will
  // have a zero Z-component if possible.

  // Two dimensions, the result is ambiguous only with respect to sign.

  assert (qValid ());
  int iSpaceDim = apVVerts[0]->iSpaceDimen ();

  if (iSpaceDim == 2)
    {
      adNorm[0] = apVVerts[0]->dY () - apVVerts[1]->dY ();
      adNorm[1] = apVVerts[1]->dX () - apVVerts[0]->dX ();
    }
  else {
    double adTemp[] = adDIFF3D (apVVerts[0]->adCoords (),
				apVVerts[1]->adCoords ());
    if ((iFuzzyComp (adTemp[0], 0.) == 0) &&
	(iFuzzyComp (adTemp[1], 0.) == 0)) {
      adNorm[0] = fabs (adTemp[2]);
      adNorm[1] = 0;
      adNorm[2] = 0;
    }
    else {
      adNorm[0] = adTemp[1];
      adNorm[1] = -adTemp[0];
      adNorm[2] = 0;
      double dMult = dMAG3D (adTemp) / dMAG3D (adNorm);
      vSCALE3D (adNorm, dMult);
    }
  }
}

void EdgeFace::
vUnitNormal (double *const adNorm)
  const {
  // For three space dimensions, the result is ambiguous, being
  // restricted only to a plane.  In this case, the vector returned will
  // have a zero Z-component if possible.

  // Two dimensions, the result is ambiguous only with respect to sign.

  assert (qValid ());
  int iSpaceDim = apVVerts[0]->iSpaceDimen ();

  if (iSpaceDim == 2)
    {
      adNorm[0] = apVVerts[0]->dY () - apVVerts[1]->dY ();
      adNorm[1] = apVVerts[1]->dX () - apVVerts[0]->dX ();
      vNORMALIZE2D (adNorm);
    }
  else {
    double adTemp[] = adDIFF3D (apVVerts[0]->adCoords (),
				apVVerts[1]->adCoords ());
    if (iFuzzyComp (adTemp[2], 0.) != 0) {
      adNorm[0] = adTemp[1];
      adNorm[1] = -adTemp[0];
      adNorm[2] = 0;
      vNORMALIZE3D (adNorm);
    }
    else {
      adNorm[0] = 1;
      adNorm[1] = 0;
      adNorm[2] = 0;
    }
  }
}

int EdgeFace::iFullCheck() const
{
  if (!Face::iFullCheck()) return 0;

  // Check whether face location is correct.
  if (pCR->qValid() && pCL->qValid()) {
    switch (uiLoc) {
    case eBdryFace:
      if (pCR->eType() != Cell::eBdryEdge &&
	  pCL->eType() != Cell::eBdryEdge) {
	return 0;
      }
      break;
    case eBdryTwoSide:
      if (pCR->eType() != Cell::eIntBdryEdge &&
	  pCL->eType() != Cell::eIntBdryEdge) {
	return 0;
      }
      break;
    case eExterior:
    case eInterior:
      if ( (pCR->eType() != Cell::eTriCell &&
	    pCR->eType() != Cell::eQuadCell)
	   ||
	   (pCL->eType() != Cell::eTriCell &&
	    pCL->eType() != Cell::eQuadCell)
	   ||
	   pCL->iRegion() != pCR->iRegion()
	   ) {
	return 0;
      }
      break;
    default:
      // For all other face types, something is wrong.
      // Catches eUnknown, ePseudoSurface, and eInvalidFaceLoc.
      return 0;
    }
  }
  // Exit is here rather than case-by-case so that other checks can be
  // added easily at a later date. 
  return 1;
}

void EdgeFace::vCentroid(double adLoc[]) const
{
  if (qIsBdryFace()) {
    if (pCL->eType() == Cell::eBdryEdge ||
	pCL->eType() == Cell::eIntBdryEdge) {
      pCL->vCentroid(adLoc);
    }
    else {
      assert(pCR->eType() == Cell::eBdryEdge ||
	     pCR->eType() == Cell::eIntBdryEdge);
      pCR->vCentroid(adLoc);
    }
  }
  else
    Face::vCentroid(adLoc);
}

// The following routine is not yet needed, so it hasn't been written.
bool EdgeFace::qIsLocallyDelaunay() const {assert(0); return false;}

double EdgeFace::dSize() const
{
  double dRetVal;
  if (apVVerts[0]->iSpaceDimen() == 2) {
    double adTmp[] = adDIFF2D(apVVerts[0]->adCoords(),
			      apVVerts[1]->adCoords());
    dRetVal = dMAG2D(adTmp);
  }
  else {
    double adTmp[] = adDIFF3D(apVVerts[0]->adCoords(),
			      apVVerts[1]->adCoords());
    dRetVal = dMAG3D(adTmp);
  }
  return dRetVal;
}

