/****************************************************************************
** GMAF - The Geometry Modeling Algorithms Framewor
**
** Created : 04012000
**
** Copyright (C) 2000 by Le-Jeng Shiue.  All rights reserved.
**
****************************************************************************/
#include <iostream>
#include <assert.h>

#include "DataStructure/gmafDataStructureManager.h"

#include "gmafCCSubdivision.h"
#include "gmafQuadMap.h"
#include "gmafQuadVertex.h"

#include "gmafQuadIterator.h"

#include "gmafFacetValueIterator.h"
#include "gmafEdgeValueIterator.h"
#include "gmafVertexValueIterator.h"

#include "gmafFacetVertexIterator.h"
#include "gmafEdgeVertexIterator.h"
#include "gmafVertexVertexIterator.h"

#include "gmafCCSFacetValueIterator.h"
#include "gmafCCSEdgeValueIterator.h"

#include "gmafCCSubVisualObject.h"

#ifdef _GUI_QT
#include "qtCCSubWidget.h"
#endif _GUI_QT


gmafCCSubdivision::gmafCCSubdivision(gmafDataStructureManager* dsm, 
				     RULE rule)
  : gmafAlgorithm(dsm, "CC Subdiviion"), 
    mpQuadMap(NULL), mRule(rule), mDepth(2), mBlendRatio(0.5) {
}

gmafCCSubdivision::~gmafCCSubdivision() {
}

void gmafCCSubdivision::run() {
#ifdef _GUI_QT
  qtCCSubWidget* dialog = new qtCCSubWidget(this);
  dialog->show();
#endif _GUI_QT  
}

void gmafCCSubdivision::run(RULE rule, int depth, float blrat) {
  setRule(rule);
  setDepth(depth);
  setBlrat(blrat);

  exec();
}

void gmafCCSubdivision::exec() {
  mpQuadMap = mpDSM->createQuadMap();
  
  switch (mRule) {
  case STANDARD:
    for (int d = 0; d < mDepth; d++)
      subdivide();
    break;
  case SABIN_RULE:
    for (int d = 0; d < mDepth; d++)
      subdivide_sr();
    break;
  case BLEND_RATIO_RULE:
    mpQuadMap->setBlendRatio(mBlendRatio);
    for (int d = 0; d < mDepth; d++)
      subdivide_br();
    break;
  }
}

void gmafCCSubdivision::subdivide() {
  for (gmafQuadIterator qitr(mpQuadMap); qitr.valid(); qitr.next()) {
    gmafQuad& quad = qitr.current();
    gmafFacetValueIterator _fvitr(&quad);
    gmafEdgeValueIterator _evitr(&quad);
    gmafVertexValueIterator _vvitr(&quad);

    // create a higher layer
    quad.createNewLayer();
    gmafFacetVertexIterator fvitr(&quad);
    gmafEdgeVertexIterator evitr(&quad);
    gmafVertexVertexIterator vvitr(&quad);

    // traverse the facets and create the facet vertices
    for (; _fvitr.valid(); _fvitr.next(), fvitr.next())
      fvitr.current() = _fvitr.current();
    
    // traverse the edges and create the edges vertices
    for (; _evitr.valid(); _evitr.next(), evitr.next())
      evitr.current() = _evitr.current();

    // traverse the vertices and create the vertex vertices
    for (; _vvitr.valid(); _vvitr.next(), vvitr.next())
      vvitr.current() = _vvitr.current();
  }

  // traverse the etraordinary vertex list
  vector<gmafQuadVertex*>& vl = mpQuadMap->getVertexList();
  vector<gmafQuadVertex*>::iterator vlitr = vl.begin();
  int layer = mpQuadMap->getCurLayerIdx();
  Vertex3D a, b, r, newV;
  for (; vlitr != vl.end(); vlitr++) {
    int n = (*vlitr)->getValance();
    if ((*vlitr)->getValance() != 4) { // skip ordinary vertex
      a = (*vlitr)->sumAlphaVertices(mpQuadMap->getQuadList(), layer-1);
      b = (*vlitr)->sumBetaVertices(mpQuadMap->getQuadList(), layer-1);
      r = (*vlitr)->sumGammaVertices(mpQuadMap->getQuadList(), layer-1);
      newV = a*(4*n-7)/(4*n) + b*3/(2*n*n) + r/(4*n*n);
      (*vlitr)->writeBackAlphaVertex(newV, mpQuadMap->getQuadList(), layer);
      (*vlitr)->synchronizeBetaVertex(mpQuadMap->getQuadList(), layer);
    }
  }
}


void gmafCCSubdivision::subdivide_br() {
  subdivide();

  // Apply the blend ratios rule
  gmafQuadIterator qitr(mpQuadMap);
  int _layer =  (qitr.current()).getCurLayerIdx();
  if (_layer == 1) return; // skip the first subdivide
  int layer = _layer-1;
  int k = (int)std::pow(2, layer);

  for (; qitr.valid(); qitr.next()) {
    float Apq, Aqp, A;
    gmafQuad& quad = qitr.current();

    VertexMatrix2D& Q0 = quad.getQuadArray(layer);
    VertexMatrix2D& Q1 = quad.getQuadArray();
    int j = 0;
    Vertex3D C;

#define UpdateHPj(y,x) Q1((y)  ,(x)) = (A*C) + (1-A)*Q1((y)  ,(x)); \
                       Q1((y)-1,(x)) = (A*C) + (1-A)*Q1((y)-1,(x)); \
                       Q1((y)+1,(x)) = (A*C) + (1-A)*Q1((y)+1,(x));
#define UpdateVPj(y,x) Q1((y), (x))   = (A*C) + (1-A)*Q1((y), (x)); \
                       Q1((y), (x)-1) = (A*C) + (1-A)*Q1((y), (x)-1); \
                       Q1((y), (x)+1) = (A*C) + (1-A)*Q1((y), (x)+1);
#define GetBRat(v1,v2) Apq = quad.getBlendRatio(v1,v2); \
                       Aqp = quad.getBlendRatio(v2,v1); \
                       A = ((float)j)/((float)(2*k))*Apq + ((float)(2*k-j))/((float)(2*k))*Aqp;
    
    int w0 = Q0.cols()-2;
    int w1 = Q1.cols()-2;
    for (int i = 2; i <= k-1; i++) {
      j = 2*i - 1;

      // UPPER edge
      GetBRat(3,0);
      C = (Q0(1,(i+1)-1) + Q0(1,(i+1)))/2;
      UpdateHPj(1,j+1);
      // LOWER edge
      GetBRat(0,1);
      C = (Q0(w0,(i+1)-1) + Q0(w0,(i+1)))/2;
      UpdateHPj(w1,j+1);
      // LEFT edge
      GetBRat(1,2);
      C = (Q0((i+1)-1,1) + Q0((i+1),1))/2;
      UpdateVPj(j+1,1);
      // RIGHT edge
      GetBRat(2,3);
      C = (Q0((i+1)-1,w0) + Q0((i+1),w0))/2;
      UpdateVPj(j+1,w1);
    }

    for (int i = 1; i <= k-1; i++) {
      j = 2*i;

      // UPPER edge
      GetBRat(3,0);
      C = (Q0(1,(i+1)-1) + (6*Q0(1,(i+1))) + Q0(1,(i+1)+1))/8;
      UpdateHPj(1,j+1);
      // LOWER edge
      GetBRat(0,1);
      C = (Q0(w0,(i+1)-1) + (6*Q0(w0,(i+1))) + Q0(w0,(i+1)+1))/8;
      UpdateHPj(w1,j+1);
      // LEFT edge
      GetBRat(1,2);
      C = (Q0((i+1)-1,1) + (6*Q0((i+1),1)) + Q0((i+1)+1,1))/8;
      UpdateVPj(j+1,1);
      // RIGHT edge
      GetBRat(2,3);
      C = (Q0((i+1)-1,w0) + (6*Q0((i+1),w0)) + Q0((i+1)+1,w0))/8;
      UpdateVPj(j+1,w1);
    }
  }

  // Rule of input Vetex and neighbors of the vertex
  vector<gmafQuadVertex*>& vlist = mpQuadMap->getVertexList();
  for (vector<gmafQuadVertex*>::iterator itr = vlist.begin();
       itr != vlist.end(); itr++) {
    int n = (*itr)->getValance();
    VertexMatrix1D q11(n+1), q21(n+1), q22(n+1), 
                   _q11(n+1), _q21(n+1), _q22(n+1), 
                   Cq(n+1), tempQ(n+1);
    (*itr)->getVertices(q11, q21, q22, mpQuadMap->getQuadList(), layer);
    (*itr)->getVertices(_q11, _q21, _q22, mpQuadMap->getQuadList(), _layer);
    
    Matrix1D Apq(n+1), Aqp(n+1);
    (*itr)->getBlendRatios(Apq, Aqp, mpQuadMap->getQuadList());
    Matrix1D Aq(n+1), EAq(n+1), Aq_p(n+1), Aq_m(n+1); 
    Aq = Apq/(2*k) + Aqp*(2*k-1)/(2*k);

    Cq = (q21+q11)/2;

    DOUBLE eaq = 0;
    for (int i = 1; i <= n; i++) 
      eaq += Aq(i);
    eaq /= n;
    EAq = eaq;
    _q11 = ((1-EAq)*_q11) + (EAq*q11);

    // Rotate Aq
    for (int i = 2; i <= n-1; i++) {
      Aq_p(i) = Aq(i+1);
      Aq_m(i) = Aq(i-1);
    }
    Aq_p(1) = Aq(2);
    Aq_m(1) = Aq(n);
    Aq_p(n) = Aq(1);
    Aq_m(n) = Aq(n-1);

    tempQ = (1-(Aq_m + Aq_p)/2)*(Aq*Cq + ((1-Aq)*_q21));
    _q21 = ((Aq_m + Aq_p)/2*q11) + tempQ;

    // rotate the Cq by 1
    VertexMatrix1D Cq_1 = Cq(Range(1, blitz::toEnd));
    Cq_1.resizeAndPreserve(Cq.rows());
    Cq_1(Cq.rows()-1) = Cq(1);

    Cq_1 = (Aq*(1-Aq_p)*Cq) + ((1-Aq)*Aq_m*Cq_1);
    tempQ = (Aq*Aq_p*q11) + ((1-Aq)*(1-Aq_p)*_q22);
    _q22 = Cq_1 + tempQ;

    (*itr)->setVertices(_q11(1), _q21, _q22, mpQuadMap->getQuadList(), _layer);
  }
}


void gmafCCSubdivision::subdivide_sr() {
  if (mpQuadMap->getCurLayerIdx() == 0) 
    return subdivide();
  for (gmafQuadIterator qitr(mpQuadMap); qitr.valid(); qitr.next()) {
    gmafQuad& quad = qitr.current();
    gmafCCSFacetValueIterator _fvitr(&quad);
    gmafCCSEdgeValueIterator _evitr(&quad);
    gmafVertexValueIterator _vvitr(&quad);

    // create a higher layer
    quad.createNewLayer();
    gmafFacetVertexIterator fvitr(&quad);
    gmafEdgeVertexIterator evitr(&quad);
    gmafVertexVertexIterator vvitr(&quad);

    // traverse the facets and create the facet vertices
    for (; _fvitr.valid(); _fvitr.next(), fvitr.next())
      fvitr.current() = _fvitr.current();
    
    // traverse the edges and create the edges vertices
    for (; _evitr.valid(); _evitr.next(), evitr.next())
      evitr.current() = _evitr.current();

    // traverse the vertices and create the vertex vertices
    for (; _vvitr.valid(); _vvitr.next(), vvitr.next())
      vvitr.current() = _vvitr.current();
  }

  // traverse the etraordinary vertex list
  vector<gmafQuadVertex*>& vl = mpQuadMap->getVertexList();
  vector<gmafQuadVertex*>::iterator vlitr = vl.begin();
  int layer = mpQuadMap->getCurLayerIdx();
  Vertex3D a, b, r, newV;
  const static double Cn[8] = {
    0, 0, 0, 0.0652476, 0.25, 0.401983, 0.52342327, 0.6170318   
  };
  for (; vlitr != vl.end(); vlitr++) {
    int n = (*vlitr)->getValance();
    if ((*vlitr)->getValance() != 4) { // skip ordinary vertex
      (*vlitr)->synchronizeBetaVertex(mpQuadMap->getQuadList(), layer);
      a = (*vlitr)->sumAlphaVertices(mpQuadMap->getQuadList(), layer-1);
      b = (*vlitr)->sumBetaVertices(mpQuadMap->getQuadList(), layer);
      r = (*vlitr)->sumGammaVertices(mpQuadMap->getQuadList(), layer);
      // case 3
      b /= n;
      r /= n;
      newV = ((Cn[n]*a) + b) + (-Cn[n]*r);
      (*vlitr)->writeBackAlphaVertex(newV, mpQuadMap->getQuadList(), layer);
    }
  }
}


gmafVisualObject*  gmafCCSubdivision::visualize() {
  assert(mpQuadMap != NULL);
  mpVisualObject = new gmafCCSubVisualObject(mpQuadMap);
  return mpVisualObject;
}
