/*  ../xa/tess2dg.c
//               Interface GLU - Functions        2017-06-20      RF
 *
 *
 * Copyright (C) 2015 CADCAM-Services Franz Reiter (franz.reiter@cadcam.co.at)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 *
-----------------------------------------------------
TODO:
  ..

-----------------------------------------------------
Modifications:
  ..

-----------------------------------------------------
*/
#ifdef globTag
void GLT(){}
#endif
/*!
\file  ../xa/glt_tess.c
\brief aux. functions tesselate using GLU 
\code

=====================================================
List_functions_start:

GLT_TESS__     tesselate indexed; outerBoundary with holes above grid-quads.
                  
List_functions_end:
=====================================================

\endcode *//*----------------------------------------




*/


#ifdef _MSC_VER
#include "../xa/MS_Def1.h"
#endif

#ifdef _MSC_VER
#define GLU_CB static void CALLBACK
#else
#define GLU_CB static void
#endif


#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <GL/gl.h>
#include <GL/glu.h>

#include "../ut/ut_geo.h"              // Point ...
#include "../ut/ut_memTab.h"           // MemTab_..
#include "../ut/ut_itmsh.h"            // MSHIG_EDGLN_.. typedef_MemTab.. Fac3
#include "../ut/ut_face.h"              // UFA

/*
/// \brief Typ_GridBox2D
/// \code
/// horizontal gridBox
/// p1            position of lower left startpoint; NULL = box invalid
///               or table of points
/// dx, dy        size of a gridsection
/// ix, iy        nr of points in a row
/// \endcode
typedef struct {Point2 *p1; int pMaxNr, ix, iy; double dx, dy;}     GridBox2D;
*/


//----------------------------------------------------------------
// typedef_MemTab(int);
// typedef_MemTab(char);
// typedef_MemTab(Point);
// typedef_MemTab(IndTab);



//----------------------------------------------------------------
static int    GLT_iErr=0;
static int    GLT_pTyp;
static int    GLT_flag;
static Point  *GLT_pta;
static Vector GLT_vcz;    // planar normalvector
static char   GLT_planar; // Typ_PLN = planar-surface; else not.

#define GLT_IPA_MAX 1000  // temporary indexTable for next openGl-patch
static int GLT_ipa[GLT_IPA_MAX];
static int GLT_ipNr;


static MemTab(int)      *GLT_ia;
static MemTab(IndTab)   *GLT_ta;
static MemTab(Point)    *GLT_pa;
static MemTab(char)     *GLT_sa;      // status for points 


static int GLT_PA3_SIZ_MAX;
static int GLT_IPA_SIZ_MAX;



//----------------------------------------------------------------
// prototypes
GLU_CB GLT_beg_CB (GLenum);
GLU_CB GLT_end_CB ();
GLU_CB GLT_err_CB (GLenum);
GLU_CB GLT_edg_CB (GLboolean);

GLU_CB GLT_pt1_CB (GLdouble*);
GLU_CB GLT_pt2_CB (GLdouble*, GLdouble**, GLfloat*, GLdouble**);





//================================================================
  int GLT_init_tess () {
//================================================================
// init OpenGL-GLU-tesselation
 
  glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &GLT_PA3_SIZ_MAX);
  glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &GLT_IPA_SIZ_MAX);


  return 0;

}


//================================================================
  GLU_CB GLT_pt1_CB (GLdouble *vertex) {
//================================================================
// get next (existing) vertex
// add all points to temporary indexarray GLT_ipa;


  int   ipt;


   // printf ("GLT_pt1_CB (%5.3f, %5.3f, %5.3f)\n",
     // vertex[0], vertex[1], vertex[2]);


  if(GLT_iErr) return;

  // find point-index. For this all points (including combine-points)
  // must be in the same set.
  ipt = ((char*)vertex - (char*)GLT_pta) / sizeof(Point);
    printf(" _pt1_CB point-index-ipt=%d\n",ipt);

  if((ipt < 0)||(ipt > GLT_pa->rNr)) {
    printf("********* ERROR GLT_pt1_CB E001\n");
    GLT_iErr = 1;
    return;
  }

  // add point
  GLT_ipa[GLT_ipNr] = ipt;
  if(GLT_ipNr < GLT_IPA_MAX) ++GLT_ipNr;
  else {
    printf("********* ERROR GLT_pt1_CB EOF\n");
    GLT_iErr = 1;
    return;
  }

}


//=========================================================
  GLU_CB GLT_pt2_CB (GLdouble newPt[3],
                     GLdouble *data[4],
                     GLfloat  weight[4],
                     GLdouble **dataOut) {
//=========================================================
// get new vertex (combine-point)
// MUST set dataOut to address of static stored vertex newPt !
// This points must be kept until end of tesselation.
// GLU-PROBLEM: all combine-points must be stored in new location; else Error.

static char c1=0;

  int      irc, i1, ipt, pMax;
  long     l1;


  printf(" GLT_pt2_CB %lf %lf %lf\n",newPt[0],newPt[1],newPt[2]);
  printf("   rNr=%d rMax=%d\n",GLT_pa->rNr,GLT_pa->rMax);



  if(GLT_iErr) return;

  // pMax = GLT_pa->rNr;


/*
  // find point-index. For this all points (including combine-points)
  // must be in the same set.
  ipt = ((char*)newPt - (char*)GLT_pta) / sizeof(Point);


  // add point
  L_ck0:

  if(ipt < pMax) {
      printf(" existing-point-index-ipt=%d\n",ipt);
    goto L_add;


  } else {
   // point not in input-array GLT_pta

    // test if point already defined.
    for(ipt=0; ipt<pMax; ++ipt) {
      if(UT3D_ck2D_equ_2pt ((Point*)newPt, &GLT_pta[ipt], UT_TOL_cv)) {
        // points are equal
          printf("   point-already-defined=%d; GLT_ipNr=%d\n",ipt,GLT_ipNr);
        goto L_add;
      }
    }
*/
    // new point; add to GLT_pa
    ipt = GLT_pa->rNr;
      printf(" add-new: %d\n",ipt);
    irc = MemTab_add (GLT_pa, &l1, newPt, 1, 0);
    if(irc) {
      if(irc > 0) {
        GLT_pta = MEMTAB_DAT(GLT_pa); // reallocated
      } else {
        printf("************ ERROR REALLOC POINTS\n");
        GLT_iErr = 1;
        return;
      }
    }
    // add stat=0 to GLU_pst
    // if(GLT_sa) MemTab_add (GLT_sa, &l1, &c1, 1, 0);
/*
  }
*/


  L_add:
    // GLT_ipa[GLT_ipNr] = ipt;
    // if(GLT_ipNr < GLT_IPA_MAX) ++GLT_ipNr;
    // else {printf("********* ERROR GLT_pt2_CB EOF\n"); return;}


  // give pointer to point stored
  L_exit:
  *dataOut = (GLdouble*)&GLT_pta[ipt];
    // printf(" datPos = %p\n",*dataOut);


  return;
}


//=========================================================
  GLU_CB GLT_edg_CB (GLboolean flag) {
//=========================================================
// flag TRUE=1: folling vertex is on boundary; else not.

  // if(flag == GL_TRUE) printf("GLT_edg_CB %d\n",flag);

  GLT_flag = flag;

  return;

}


//================================================================
  GLU_CB GLT_beg_CB (GLenum type) {
//================================================================
// start of nxt patch;
// type: 4=GL_TRIANGLES;5=GL_TRIANGLE_STRIP;6=GL_TRIANGLE_FAN

   printf ("GLT_beg_CB %d\n",type);

  GLT_ipNr = 0;
  GLT_pTyp = type;

}


//================================================================
  GLU_CB GLT_end_CB () {
//================================================================
// create new opengl-patch from all points in GLT_ipa;
// add patch to fmt
// Input:
//   type of patch: GLP_typ; all patch-points are in GLT_ipa, nr = GLT_ipNr.

  int       ii;
  long      l1;
  IndTab   el1;

  printf ("GLT_end_CB\n");

  // add GLT_ipa to GLT_ia
  ii = MEMTAB_IND (GLT_ia);
  MemTab_add (GLT_ia, &l1, GLT_ipa, GLT_ipNr, 0);


  // create record in GLT_ta
  el1.typi = MSH_PATCH;  // indexed-openGl-patch
  el1.iNr  = GLT_ipNr;
  el1.aux  = GLT_pTyp;
  el1.typd = GLT_planar;     // Typ_PLN (planar) or (0=Typ_Error) not-planar
  el1.ibeg = ii;
  MemTab_add (GLT_ta, &l1, &el1, 1, 0);


    // TESTBLOCK
    UT3D_stru_dump (Typ_IndTab, &el1, " el1");
    // GL_view_ipatch_2 (GLT_pTyp, GLT_ipNr, MEMTAB__(GLT_ia,ii), GLT_pta, NULL,
                      // &GLT_vcz, -1L);
    // UPAT_ipatch_disp_opts (GLT_pTyp,GLT_ipNr,MEMTAB__(GLT_ia,ii),GLT_pta,
                           // 0, "sbv", -1L);
    // GR_Disp_ipatch (GLT_pTyp, 4, GLT_ipNr, MEMTAB__ (GLT_ia, ii), GLT_pta);
    // tess_test_disp_face (&el1, GLU_pmt, GLU_imt);
    // display as points
    // tst_gl2_point_d (GLP_pa, GLP_pNr, (GLfloat*)&colTab[COL_YELLOW]);
    // display triangles (transform patch into triangles and display)
    // tst_gl2_patch__ (GLP_typ, GLP_pa, GLP_pNr);
    // END TESTBLOCK

}


//=========================================================
  GLU_CB GLT_err_CB (GLenum errNo) {
//=========================================================

  const GLubyte *estring;

  printf("****** GLT_err_CB %d\n",errNo);

  estring = gluErrorString(errNo);
  fprintf (stderr, "Nurbs Error: %s\n", estring);
  GLT_iErr = -1;
  // exit (0);


}


//================================================================
  int GLT_TESS__ (MemTab(IndTab)  *ta,
                   MemTab(int)    *ia,
                   MemTab(Point)  *pa,
                   MemTab(char)   *sa,
                   GridBox        *gb,
                   Vector         *vcz) {
//================================================================
// Input:
//   ta     boundaries, grid; indices ia, points in pa. All CCW.
//   ia     point-indices
//   pa     points
//   vcz    for 2D-tesselation NULL; else (planar-surface) normalvector
// Output:
//   ta     tesselated patches; indexes in ia, points in pa.
//   sa     flags for points
//          bitVal 8 for used points, bitVal 1 for boundary-points.
// RetCod:  0=OK

// mode planar: pa = 3D-points of plane; gb = NULL; vcz = normalvector of plane;
// mode 2D: pa = U/V-values of surface; gb has iu/iv-values; vcz=NULL;

// creates triangles in ta; adds points and point-links to pa and ia.
// ta: tesselated-faces;
//   .ibeg=startIndex in ia; .iNr= nr of links in imt;
//   .aux=triangle-typ; eg GL_TRIANGLES;

// Gives the common of gridBox + outerBoundary - hole.
// using GLU_TESS_WINDING_ABS_GEQ_TWO (contours must give 2 or more)
// gridBox(CCW) + outerBoundary(CCW)            = 1+1   = 2 = ON
// gridBox(CCW) + outerBoundary(CCW) - hole(CW) = 1+1-1 = 1 = OFF

// was GLU_tess__ tst_gl2_2Dtess3


static GLUtesselator *ot1=NULL;
  int      i1, i2, ii, p1Nr, p2Nr, itNr, itInd, *ipa, *ipTab;
  int      ip1, ip2, ip3, ip4;
  Point    *pta;
  IndTab   *itAct, *itTab;

  Point  p4a[4];


  printf("GLT_TESS \n");

  GLT_iErr = 0;



  if(vcz) {
    GLT_planar = Typ_PLN;
    GLT_vcz = *vcz;   // copy planar normalvector
  } else {
    GLT_planar = Typ_Error;  // 0;
  }


  GLT_ta = ta;
  itNr = MEMTAB_IND (ta);  // nr of contours
  itTab = MEMTAB_DAT (ta); // contours

  GLT_pa = pa;
  GLT_pta = MEMTAB_DAT(pa);  // all points

  GLT_ia = ia;
  ipa = MEMTAB_DAT(ia);      // indices into GLT_pta

  GLT_sa = sa;               // pt-stat


  //----------------------------------------------------------------
  // create tess-obj
  if(ot1 == NULL) ot1 = gluNewTess();

  // glRenderMode(GL_FEEDBACK);


  //----------------------------------------------------------------
  // define the callback-functions
  gluTessCallback (ot1, GLU_TESS_BEGIN,      (void*)&GLT_beg_CB);
  gluTessCallback (ot1, GLU_TESS_VERTEX,     (void*)&GLT_pt1_CB);
  gluTessCallback (ot1, GLU_TESS_COMBINE,    (void*)&GLT_pt2_CB);
  gluTessCallback (ot1, GLU_TESS_EDGE_FLAG,  (void*)&GLT_edg_CB);
  gluTessCallback (ot1, GLU_TESS_END,        (void*)&GLT_end_CB);
  gluTessCallback (ot1, GLU_TESS_ERROR,      (void*)&GLT_err_CB);


  //----------------------------------------------------------------

  if(GLT_planar) {
    // 1(OB=CCW) = surface
    // 1(OB=CCW) + -1(IB=CW) = 0 = hole
    gluTessProperty (ot1, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);


  } else {
    // gridbox(CCW) + outerbound(CCW) = 2 = ON
    // gridbox(CCW) + outerbound(CCW) - innerbound(CW) = 1 = OFF
    // Fill ABSolute values Greater than EQual to TWO 
    // grd(CCW)+OB(CCW)=2; grd(CCW)+OB(CCW)-IB(CW)=1;
    gluTessProperty (ot1, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ABS_GEQ_TWO);
      // printf(" GLU_TESS_WINDING_ABS_GEQ_TWO\n");
  }


  // gluTessProperty (ot1, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
  // union oder a-b oder b-a aber nicht das gemeinsame von a,b
  // gluTessProperty (ot1, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE);


  // triangles, not boundary-lines
  gluTessProperty(ot1, GLU_TESS_BOUNDARY_ONLY, GL_FALSE);
  // gluTessProperty(ot1, GLU_TESS_BOUNDARY_ONLY, GL_TRUE); // get lines


  // gluTessProperty(ot1, GLU_TESS_TOLERANCE, 0.75); // maxVal 1. ?
  // unsupported ..


  // normalvector
  if(GLT_planar) {
    gluTessNormal(ot1, vcz->dx, vcz->dy, vcz->dz);
      UT3D_stru_dump(Typ_VC, vcz, " vcz:");
  } else {
    gluTessNormal (ot1, 0., 0.,  1.); //-1.);
    // defines inside-outside; +1=inside..??
  }


  // start definition boundaries
  gluTessBeginPolygon (ot1, NULL);
  // add contour from single point:     IGNORED !
  // add contour from linesegment only: IGNORED !


  //----------------------------------------------------------------
  // add all gridBoxes CCW.
  if(GLT_planar) goto L_OB;

    // create gridboxes
    // loop vertical with i1
    p1Nr = gb->iy - 1;
    p2Nr = gb->ix - 1;
      printf(" 2D-mode: p1Nr=%d p2Nr=%d\n", p1Nr, p2Nr);
    // pta = gb->p1;   // first point of gridbox

    // get ii = index of first point in ipa
    ii = -1;
    for(i1=0; i1<itNr; ++i1)
      if(itTab[i1].typi == MSH_GRIDBOX) ii = itTab[i1].ibeg;
    if(ii < 0) {printf("**** GLT_TESS__ E001\n"); return -2;}
    i1 = ipa[ii]; // index of first point of grid
       printf(" i1=%d ii=%d\n",i1,ii);
    pta = MEMTAB__ (pa, i1);   // first point of gridbox

    // i1 = loop vertical
    for(i1=0; i1<p1Nr; ++i1) {
      // loop horizontal with i2
      ii = i1 * gb->ix;  // ipos of 1. point in horiz. row
      for(i2=0; i2<p2Nr; ++i2) {
        // CCW:
        ip1 = ii;
        ip2 = ii + 1;
        ip3 = ii + gb->ix + 1;
        ip4 = ii + gb->ix;
          printf(" iga = %d %d %d %d\n",ip1,ip2,ip3,ip4);
          UT3D_stru_dump (Typ_PT, &pta[ip1], "ip1");
          UT3D_stru_dump (Typ_PT, &pta[ip2], "ip2");
          UT3D_stru_dump (Typ_PT, &pta[ip3], "ip3");
          UT3D_stru_dump (Typ_PT, &pta[ip4], "ip4");

        gluTessBeginContour (ot1);
          gluTessVertex (ot1, (double*)&pta[ip1], (double*)&pta[ip1]);
          gluTessVertex (ot1, (double*)&pta[ip2], (double*)&pta[ip2]);
          gluTessVertex (ot1, (double*)&pta[ip3], (double*)&pta[ip3]);
          gluTessVertex (ot1, (double*)&pta[ip4], (double*)&pta[ip4]);
        gluTessEndContour (ot1);

        if(GLT_iErr) {gluTessEndPolygon(ot1); goto L_glu_err;}
        ++ii;
      }
    }
      // printf(" nach gridBoxes ..\n");


  //----------------------------------------------------------------
  // add outer-boundary CCW.
  itInd = 0;
  L_OB:
      printf(" L_OB: itNr=%d\n",itNr);
    itAct = &itTab[itInd];  // first contour = outer-boundary
    if(itAct->typi != MSH_EDGLN_OB) {
      ++itInd;
      if(itInd < itNr) goto L_OB;
      // no outer boundary ..
      printf("**** GLT_TESS__ E001\n");
      return -1;
    }

    p2Nr = itAct->iNr;
    --p2Nr;  // skip closing point
    ipTab = &ipa[itAct->ibeg]; // first index
      printf(" OB-p2Nr=%d ibeg=%d\n",p2Nr,ipa[itAct->ibeg]);


    gluTessBeginContour (ot1);
    for(i1=0; i1<p2Nr; ++i1) {
      ii = ipTab[i1];
      gluTessVertex (ot1, (double*)&GLT_pta[ii], (double*)&GLT_pta[ii]);
        UT3D_stru_dump (Typ_PT, &GLT_pta[ii], " OB p[%d] ii=%d",i1,ii);
    }
    gluTessEndContour (ot1);

    if(GLT_iErr) {gluTessEndPolygon(ot1); goto L_glu_err;}


  //----------------------------------------------------------------
  // add all inner-boundaries CW (reverse order)
  L_IB:
    for(i1=0; i1<itNr; ++i1) {
      itAct = &itTab[i1];
      if(itAct->typi != MSH_EDGLN_IB) continue;
      p2Nr = itAct->iNr;
      --p2Nr;  // remove last point (else (BUG) GLU returns this pt as new pt)
        printf(" IB-tess-p2Nr=%d\n",p2Nr);
      // ipTab = MEMTAB__ (ia, itAct->ibeg);
      ipTab = &ipa[itAct->ibeg]; // first index

      gluTessBeginContour (ot1);
      for(i2=p2Nr; i2>=0; --i2) {
        ii = ipTab[i2];
        gluTessVertex (ot1, (double*)&GLT_pta[ii], (double*)&GLT_pta[ii]);
          // UT3D_stru_dump (Typ_PT,&GLT_pta[ii]," IB-C%d p[%d] ii=%d",i1,i2,ii);
      }
      gluTessEndContour (ot1);

      if(GLT_iErr) {gluTessEndPolygon(ot1); goto L_glu_err;}
    }



  //----------------------------------------------------------------
/*
  // loop tru boxes
  if(GLT_planar) goto L_end;

    // create gridboxes
    // loop vertical with i1
    p1Nr = gb->iy - 1;
    p2Nr = gb->ix - 1;
      printf(" 2D-mode: p1Nr=%d p2Nr=%d\n", p1Nr, p2Nr);
    // pta = gb->p1;   // first point of gridbox

    // get ii = index of first point in ipa
    ii = -1;
    for(i1=0; i1<itNr; ++i1)
      if(itTab[i1].typi == MSH_GRIDBOX) ii = itTab[i1].ibeg;
    if(ii < 0) {printf("**** GLT_TESS__ E001\n"); return -2;}
    i1 = ipa[ii]; // index of first point of grid
       printf(" i1=%d ii=%d\n",i1,ii);
    pta = MEMTAB__ (pa, i1);   // first point of gridbox

    ip1 = 0;
    ip2 = 1;
    ip3 = gb->ix + 1;
    ip4 = gb->ix;
      printf(" iga = %d %d %d %d\n",ip1,ip2,ip3,ip4);
    p4a[0] = pta[ip1];
    p4a[1] = pta[ip2];
    p4a[2] = pta[ip3];
    p4a[3] = pta[ip4];

        gluTessBeginContour (ot1);
          gluTessVertex (ot1, (double*)&p4a[0], (double*)&p4a[0]);
          gluTessVertex (ot1, (double*)&p4a[1], (double*)&p4a[1]);
          gluTessVertex (ot1, (double*)&p4a[2], (double*)&p4a[2]);
          gluTessVertex (ot1, (double*)&p4a[3], (double*)&p4a[3]);
        gluTessEndContour (ot1);

    gluTessEndPolygon(ot1);


    // i1 = loop vertical
    for(i1=1; i1<p1Nr; ++i1) {
      // loop horizontal with i2
      ii = i1 * gb->ix;  // ipos of 1. point in horiz. row
      for(i2=0; i2<p2Nr; ++i2) {
        // CCW:
        ip1 = ii;
        ip2 = ii + 1;
        ip3 = ii + gb->ix + 1;
        ip4 = ii + gb->ix;
          printf(" iga = %d %d %d %d\n",ip1,ip2,ip3,ip4);
        p4a[0] = pta[ip1];
        p4a[1] = pta[ip2];
        p4a[2] = pta[ip3];
        p4a[3] = pta[ip4];

        if(GLT_iErr) {gluTessEndPolygon(ot1); goto L_glu_err;}
        ++ii;

        // gluTessEndPolygon(ot1);
      }
    }
      // printf(" nach gridBoxes ..\n");
*/


  //----------------------------------------------------------------
  L_end:
  // start tesselation
  gluTessEndPolygon(ot1);

  // glRenderMode(GL_RENDER);


    // MemTab_dump (ta, "ta-2");
    // MemTab_dump (ia, "ia-2");

  return 0;


  //----------------------------------------------------------------
  L_glu_err:
    printf("**** GLT_TESS Error %d\n",GLT_iErr);
    return -1;


}


// EOF
