/****************************************************************************
** GMAF - The Geometry Modeling Algorithms Framework
**
** Created : 03252000
**
** Copyright (C) 2000 by Le-Jeng Shiue.  All rights reserved.
**
****************************************************************************/
#ifndef gmafQuad_H
#define gmafQuad_H

#include <assert.h>
#include <iostream>
#include <vector>

#include "BlitzExtension/blitzConfig.h"
#include "CgalExtension/cgalConfig.h"
#include "DataStructure/gmafDataStructure.h"

class gmafQuadVertex;

/**
   gmafQuad: quad data structure
   (ref. to the appendix of PCCM)
 */
class gmafQuad : public gmafDataStructure {
// ENUMS
public:
  enum { UPPER_LEFT = 0,
	 LOWER_LEFT = 1,
	 LOWER_RIGHT = 2,
	 UPPER_RIGHT = 3};

// VARIABLES
/**@name Class Variables */
//@{
protected:
  /** 
      The original facet for this quad
  */
  Facet* mpFacet;
  /** 
      This array is the quad matrix 
  */
  vector<VertexMatrix2D*> mMipQuad;

  int mCurLayer; // start by 0

  // valance of upper-left corner is mCornerValance[0]
  int mCornerValance[4]; //CCW
  
  // These are 4 pointer linked back to the vertex saved in the 
  // vertex list in the QuadMap 
  // gmafQuadVertex* mpCorner[4];
  
  // Blend ratio of 4 vertices (each have two ratios)
  //     0       1       2       3       4       5       6       7 
  //   ul->ll, ll->ul, ll->lr, lr->ll, lr->ur, ur->lr, ur->ul, ul->ur
  //    0->1 ,  1->0 ,  1->2 ,  2->1 ,  2->3 ,  3->2 ,  3->0 ,  0->3
  float mBlendRatio[8];

  // Blend ration mapping table 
  //                                    0   1   2   3
  static const int mBRMap[4][4]; // { {-1,  0, -1,  7}, //0
				 //   { 1, -1,  2, -1}, //1
				 //   {-1,  3, -1,  4}, //2
				 //   { 6, -1,  5, -1}};//3
private:
  
//@}

// METHODS
/**@name Class Methods */
//@{
public:
  /**
     Constructor of gmafQuad
  */
  gmafQuad(const Facet* f);

  /**
     Destructor of gmafQuad
  */    
  virtual ~gmafQuad();
  
  /**
     Get the width of the current/lth layer of the quad
   */
  inline int getWidth() { return mMipQuad[mCurLayer]->cols(); }
  inline int getWidth(int l) { return mMipQuad[l]->cols(); }
  /**
     Get the height of the current/lth layer of the quad 
   */
  inline int getHeight() { return mMipQuad[mCurLayer]->rows(); }
  inline int getHeight(int l) { return mMipQuad[l]->rows(); }
  /**
     Get the quad matrix of the current/lth layer of the quad
   */
  inline VertexMatrix2D& getQuadArray() { return *(mMipQuad[mCurLayer]); }
  inline VertexMatrix2D& getQuadArray(int l) { return *(mMipQuad[l]); }

  inline int getCurLayerIdx() { return mCurLayer; }
  /**
     Get the valance of the cth corner.
     c = 0 : Upper-left corner
     c = 1 : Lower-left corner
     c = 2 : Lower-right corner
     c = 3 : Upper-right corner
   */
  inline int getCornerValance(int c) { return mCornerValance[c]; }

  /**
     Return false if the cth corner is extraordinary vertex
   */
  inline bool isCornerValid(int c) { return (mCornerValance[c] == 4); }
  /**
     Return the corner which is v
   */
  inline int whichCorner(Point& v);
  inline void createNewLayer();


  /**
     These functions will return the alpha/beta/gamma vertex of the
     cth corner of lth layer
   */
  inline Vertex3D& getAlphaCorner(int c, int l = -1);
  // it will return the second beta vertex by CW fasion
  inline const Vertex3D& getBetaCorner(int c, int l= -1);
  inline const Vertex3D& getGammaCorner(int c, int l = -1);

  inline Vertex3D& get1stBetaCorner(int c, int l= -1);
  inline Vertex3D& get2ndBetaCorner(int c, int l= -1);
  inline Vertex3D& get3rdBetaCorner(int c, int l= -1);
  inline Vertex3D& get4thBetaCorner(int c, int l= -1);
  
  /**
     Dump the quad matrix
  */
  void dump(int l = -1);
  /**
     Get and set te blend ratio of the edge of the corner c1 to corner c2
     br : the blend ratio (0.0 - 1.0)
  */
  inline void  setBlendRatio(int c1, int c2, float br);
  inline float getBlendRatio(int c1, int c2);

  /**
     This function will reset the blend ratios of the 4 edges in this 
     quad.
   */
  inline void setBlendRatio(float br);

protected:
  
private:

};

inline int gmafQuad::whichCorner(Point& v) {
#define Equal(i,j) (v.x() == (vm((i),(j)))(0) && v.y() == (vm((i),(j)))(1) && v.z() == (vm((i),(j)))(2))

  VertexMatrix2D& vm = *(mMipQuad[mCurLayer]);
  int w = vm.cols();
  int h = vm.rows();
  if (Equal(1,1)) return UPPER_LEFT;
  if (Equal(h-2,1)) return LOWER_LEFT;
  if (Equal(h-2,w-2)) return LOWER_RIGHT;
  if (Equal(1,w-2)) return UPPER_RIGHT;
  return -1;
}

inline void gmafQuad::createNewLayer() {
  VertexMatrix2D& curqm = *(mMipQuad[mCurLayer]);
  int w = curqm.cols();
  int h = curqm.rows();
  VertexMatrix2D* quadMatrix = new VertexMatrix2D((w-2)*2-1+2,(h-2)*2-1+2);
  *quadMatrix = 0;
  mMipQuad.push_back(quadMatrix);
  mCurLayer++;
}

inline Vertex3D& gmafQuad::getAlphaCorner(int c, int l) {
  l = l < 0 ? mCurLayer : l;
  VertexMatrix2D& qm = *(mMipQuad[l]);
  if (c == UPPER_LEFT) return qm(1, 1);
  else if (c == LOWER_LEFT) return qm(qm.rows()-2, 1);
  else if (c == LOWER_RIGHT) return qm(qm.rows()-2, qm.cols()-2);
  else if (c == UPPER_RIGHT) return qm(1, qm.cols()-2);
  else assert(false);
}

inline const Vertex3D& gmafQuad::getBetaCorner(int c, int l) {
  return get2ndBetaCorner(c, l);
}

inline const Vertex3D& gmafQuad::getGammaCorner(int c, int l) {
  l = l < 0 ? mCurLayer : l;
  VertexMatrix2D& qm = *(mMipQuad[l]);
  if (c == UPPER_LEFT) return qm(2, 2);
  else if (c == LOWER_LEFT) return qm(qm.rows()-3, 2);
  else if (c == LOWER_RIGHT) return qm(qm.rows()-3, qm.cols()-3);
  else if (c == UPPER_RIGHT) return qm(2, qm.cols()-3);
  else assert(false);
}

inline Vertex3D& gmafQuad::get1stBetaCorner(int c, int l) {
  l = l < 0 ? mCurLayer : l;
  VertexMatrix2D& qm = *(mMipQuad[l]);
  if (c == UPPER_LEFT) return qm(0, 1);
  else if (c == LOWER_LEFT) return qm(qm.rows()-2, 0);
  else if (c == LOWER_RIGHT) return qm(qm.rows()-1, qm.cols()-2);
  else if (c == UPPER_RIGHT) return qm(1, qm.cols()-1);
  else assert(false);
}

inline Vertex3D& gmafQuad::get2ndBetaCorner(int c, int l) {
  l = l < 0 ? mCurLayer : l;
  VertexMatrix2D& qm = *(mMipQuad[l]);
  if (c == UPPER_LEFT) return qm(1, 2);
  else if (c == LOWER_LEFT) return qm(qm.rows()-3, 1);
  else if (c == LOWER_RIGHT) return qm(qm.rows()-2, qm.cols()-3);
  else if (c == UPPER_RIGHT) return qm(2, qm.cols()-2);
  else assert(false);
}

inline Vertex3D& gmafQuad::get3rdBetaCorner(int c, int l) {
  l = l < 0 ? mCurLayer : l;
  VertexMatrix2D& qm = *(mMipQuad[l]);
  if (c == UPPER_LEFT) return qm(2, 1);
  else if (c == LOWER_LEFT) return qm(qm.rows()-2, 2);
  else if (c == LOWER_RIGHT) return qm(qm.rows()-3, qm.cols()-2);
  else if (c == UPPER_RIGHT) return qm(1, qm.cols()-3);
  else assert(false);
}

inline Vertex3D& gmafQuad::get4thBetaCorner(int c, int l) {
  l = l < 0 ? mCurLayer : l;
  VertexMatrix2D& qm = *(mMipQuad[l]);
  if (c == UPPER_LEFT) return qm(1, 0);
  else if (c == LOWER_LEFT) return qm(qm.rows()-1, 1);
  else if (c == LOWER_RIGHT) return qm(qm.rows()-2, qm.cols()-1);
  else if (c == UPPER_RIGHT) return qm(0, qm.cols()-2);
  else assert(false);
}


inline void gmafQuad::setBlendRatio(int c1, int c2, float br) {
  assert(c1 >= UPPER_LEFT && c1 <= UPPER_RIGHT);
  assert(c2 >= UPPER_LEFT && c2 <= UPPER_RIGHT);
  assert(br >= 0.0 && br <= 1.0);
  mBlendRatio[mBRMap[c1][c2]] = br;
}

inline float gmafQuad::getBlendRatio(int c1, int c2) {
  assert(c1 >= UPPER_LEFT && c1 <= UPPER_RIGHT);
  assert(c2 >= UPPER_LEFT && c2 <= UPPER_RIGHT);
  return mBlendRatio[mBRMap[c1][c2]];
}

inline void gmafQuad::setBlendRatio(float br) {
  assert(br >= 0.0 && br <= 1.0);
  for (int i = 0; i < 8; i++) mBlendRatio[i] = br;
}

#endif gmafQuad_H
