/* test glDrawElements glDrawArrays

tst_glDrawArrays1     glDrawArrays      draw patches from array
tst_glDrawElements1   glDrawElements    draw indexed-patches from array

comp_normals                            get normalvectors for triangles
comp_inormals
set_inormal
UT3F_vc_setLength
UT3F_vc_perp_3pt
UT3F_bp_vcz                             get main-BackPlaneNr from normalvec
ck_orient                               test / repair orientation


-----------------------------------------------------------------
glDrawElements - draw indexed triangles; for indices no stride possible !

glDrawArrays - alle vertices & normals NICHT indexed (subsequent)
  tst_glDrawArrays1

glIndexPointer only for colors useful ..

*/


#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>


#define COL_RED    0
#define COL_YELLOW 1
#define COL_SUR    2

static GLfloat colTab [][4] = {
  1.0, 0.0, 0.0, 1.0,
  1.0, 1.0, 0.0, 1.0,
  0.6, 0.5, 0.5, 1.0};


#define Typ_VC             2  ///< D   Vector
#define Typ_VC3f         514  ///< vector 3D float


typedef struct {double x, y, z;}                                    Point;
typedef struct {double dx, dy, dz;}                                 Vector;

typedef struct {float x, y, z;}                                     Pnt3f;
typedef struct {float dx, dy, dz;}                                  Vec3f; 


static Vec3f VEC3F_NUL = {0.f, 0.f,0.f};


// ../ut/AP_types.h
#define BCKPLN_XY          2  ///< Z-Vec BCKVEC_Z
#define BCKPLN_XZ          1  ///< Y-Vec BCKVEC_Y 
#define BCKPLN_YZ          0  ///< X-Vec BCKVEC_X




//================================================================
  int UT3F_bp_vcz (Vec3f *vcn) {
//================================================================
/// \code
/// UT3D_bp_vcz               returns main-BackPlaneNr (0-2) from normalvec
///
/// eine Hilfsebene auswaehlen; Input ist der Normalvektor der Ebene;
/// es wird jene Hauptebene mit dem geringsten Wert gewaehlt.
/// In:               Out:
/// 1,0,0  X-Vektor:   0 = BCKPLN_YZ   es wird Y-Z-Plane gewaehlt;
/// 0,1,0  Y-Vektor:   1 = BCKPLN_XZ   es wird X-Z-Plane gewaehlt;
/// 0,0,1  Z-Vektor:   2 = BCKPLN_XY   es wird X-Y-Plane gewaehlt;
/// \endcode
  
  int      mode;
  float    dx, dy, dz;

  // UT3D_stru_dump(Typ_VC, vcn, "UT3D_bp_vcz:");

  // die Koordinate mit dem hoechsten Wert stillegen ..
  dx = fabsf(vcn->dx);
  dy = fabsf(vcn->dy);
  dz = fabsf(vcn->dz);

  if(dy > dx) {             // Y > X
    if(dz > dy) mode = BCKPLN_XY;     // skip Z-Coord
    else        mode = BCKPLN_XZ;     // skip Y-Coord
  } else {                  // X > Y
    if(dz > dx) mode = BCKPLN_XY;     // skip Z-Coord
    else        mode = BCKPLN_YZ;     // skip X-Coord
  }

  // printf("ex UT3D_bp_vcz %d\n",mode);
  return mode;

}



//==========================================================================
  int UT3F_vc_perp_3pt (Vec3f *vp, Point *ptc, Point *ptx, Point *pty) {
//==========================================================================
/// \code
/// UT3D_vc_perp_3pt           float-vector = perpendic. to 3 points (crossprod)
/// 
/// Liefert den Z-Vektor von Origin, X-AchsEndpunkt und Y-AchsEndpunkt
/// Get VZ from PO, PX, PY:    UT3D_vc_perp3pt (&vz, &po, &px, &py);
/// Get VY from PO, PX, PZ:    UT3D_vc_perp3pt (&vy, &po, &pz, &px);
/// \endcode


  float dxx,dxy,dxz,dyx,dyy,dyz;


  dxx = ptx->x - ptc->x;
  dxy = ptx->y - ptc->y;
  dxz = ptx->z - ptc->z;

  dyx = pty->x - ptc->x;
  dyy = pty->y - ptc->y;
  dyz = pty->z - ptc->z;

  vp->dx = dxy * dyz - dxz * dyy;
  vp->dy = dxz * dyx - dxx * dyz;
  vp->dz = dxx * dyy - dxy * dyx;

  return 0;

}


/// UT3F_len_vc          length of 3D-vector
float UT3D_len_vc (Vec3f *);
#define UT3F_len_vc(vc)\
 (sqrtf((vc)->dx*(vc)->dx + (vc)->dy*(vc)->dy + (vc)->dz*(vc)->dz))


//====================================================================
  void UT3F_vc_setLength (Vec3f *vco, Vec3f *vci, float new_len) {
//====================================================================
/// \code
/// UT3D_vc_setLength               change vectorlength
/// 
/// vco und vci duerfen ident sein
/// 
/// use UT3D_vc_multvc with vectors of length=1
/// \endcode


  float len;

  /* printf("UT2D_vc_setLength %f %f %f %f\n", */
                   /* vci->dx,vci->dy,vci->dz,new_len); */

  len = UT3F_len_vc (vci);   printf(" len=%f\n",len);


  if (len != 0.f)
    {
      len = new_len / len;
      vco->dx = vci->dx*len;
      vco->dy = vci->dy*len;
      vco->dz = vci->dz*len;
      /* printf("UT3D_vc_setLength %f %f %f\n",vco->dx,vco->dy,vco->dz); */
    }
  else
    {
      printf("**** UT3F_vc_setLength error\n");
      // *vco = UT3D_VECTOR_X;
    }

  

}



//================================================================
  int set_inormal (Vec3f *nvca, Vec3f *vc1) {
//================================================================
// see UT3D_vc_add2vc


  // add vector
  nvca->dx += vc1->dx; //nvca->dx /= 2.;
  nvca->dy += vc1->dy; //nvca->dy /= 2.;
  nvca->dz += vc1->dz; //nvca->dz /= 2.;

    printf(" in %f %f %f \n",nvca->dx,nvca->dy,nvca->dz);


  return 0;

}
 

//=====================================================================
  int ck_orient (Vec3f *vcn, Point *p1, Point *p2, Point *p3) {
//=====================================================================
// test / repair orientation
/// Output:
///   RetCod   1 = CCW
///           -1 = CW
// see UT3D_sr_npt_bp

  int     ibp, isr;
  double  d1;

  ibp = UT3F_bp_vcz (vcn);      printf(" ibp=%d\n",ibp);


  // isr = UT3D_sr_3pt ( ptTab, &pl1.vz, ibp);

  if(ibp == 0) {
    d1 = p1->y * (p2->z - p3->z); // skip X
    d1 += p2->y * (p3->z - p1->z); // skip X
    d1 += p3->y * (p1->z - p2->z); // skip X



  } else if(ibp == 1) {
    d1 = p1->z * (p2->x - p3->x); // skip Y
    d1 += p2->z * (p3->x - p1->x); // skip Y
    d1 += p3->z * (p1->x - p2->x); // skip Y


  } else {
    d1 = p1->x * (p2->y - p3->y); // skip Z
    d1 += p2->x * (p3->y - p1->y); // skip Z
    d1 += p3->x * (p1->y - p2->y); // skip Z
  }


  if(d1 < 0.) {  // CW
    isr =  1;
    // *aro = d1 / -2.;
    // die Flaeche muesste um einen bestimmten Faktor vergroessert werden ..
  } else {       // CCW
    isr = 0;
    // *aro = d1 / 2.0;
  }

    // printf(" ex ck_orient %d\n",isr);


  if(!isr) isr = 1;   // 0 -> 1 (CCW)
  else     isr = -1;  // 1 -> -1; 2017-05-15 


  // nochmal invertieren wenn Ebene verkehrt ..
  if(ibp == 0)        {   // skip X
    if(vcn->dx < 0.f) isr = -isr;
  } else if(ibp == 1) {   // skip Y
    if(vcn->dy < 0.f) isr = -isr;
  } else               {   // skip Z
    if(vcn->dz < 0.f) isr = -isr;
  }


    printf(" ex ck_orient %d\n",isr);


  return isr;

}


//================================================================
  int comp_inormals (Vec3f *nvca, Point *pta, int ptNr,
                     unsigned short *ixa, int ixNr) {
//================================================================
//  loop tru all triangles; compute normal (add all normals)
// Method not exact; many small triangles (fan) against single triangle
// sharing the same point make wrong vector ..

  int    ii, i1, tNr;
  Vec3f  vc1;

  tNr = ixNr / 3;


  printf("comp_inormals %d %d %d\n",ptNr,ixNr,tNr);


  for(ii=0; ii<ptNr; ++ii) nvca[ii] = VEC3F_NUL;


  // loop tru all triangles
  for(i1=0; i1<ixNr; i1 += 3) {
      printf(" t%d = %d %d %d\n",i1,ixa[i1],ixa[i1+1],ixa[i1+2]);

    // compute normal
    UT3F_vc_perp_3pt (&vc1, &pta[ixa[i1]], &pta[ixa[i1+1]], &pta[ixa[i1+2]]);
    UT3F_vc_setLength (&vc1, &vc1, 1.f);
      printf(" tri %d = %d %d %d\n",ii,ixa[i1],ixa[i1+1],ixa[i1+2]);
      printf(" vc[%d] %f %f %f\n",ii,vc1.dx,vc1.dy,vc1.dz);


    // test / repair orientation
    ck_orient (&vc1, &pta[ixa[i1]], &pta[ixa[i1+1]], &pta[ixa[i1+2]]);

    // set normalVector
    set_inormal (&nvca[ixa[i1]], &vc1);
    set_inormal (&nvca[ixa[i1+1]], &vc1);
    set_inormal (&nvca[ixa[i1+2]], &vc1);
  }

  for(ii=0; ii<ptNr; ++ii) UT3F_vc_setLength (&nvca[ii], &nvca[ii], 1.f);


  for(ii=0; ii<ptNr; ++ii)
    printf(" nvc[%d] %f %f %f\n",ii,nvca[ii].dx,nvca[ii].dy,nvca[ii].dz);


  return 0;

}


//================================================================
  int comp_normals (float *nvca, int vNr, Point *pa) {
//================================================================
// get normalvectors for triangles in pa;


  int    i1, ii, tNr;
  Vec3f  vc1;
  Point  p1, p2, p3;

  tNr = vNr / 3;

  printf("comp_normals %d\n",tNr);


  i1 = 0;
  for(ii=0; ii<tNr; ++ii) {

    // get vc1 = normalvector 
    UT3F_vc_perp_3pt (&vc1, &pa[i1], &pa[i1+1], &pa[i1+2]);
    // UT3F_vc_perp_3pt (&vc1, &p1, &p2, &p3);
    // UT3F_vc_setLength (&vc1, &vc1, 1.);
      printf(" vc1 = %f %f %f\n",vc1.dx,vc1.dy,vc1.dz);

    memcpy (nvca, &vc1, sizeof(Vec3f));  nvca += 3;
    memcpy (nvca, &vc1, sizeof(Vec3f));  nvca += 3;
    memcpy (nvca, &vc1, sizeof(Vec3f));  nvca += 3;

    i1 += 3;
  }


  return 0;

}


double vertices1[] = { 1, 1, 1,  -1, 1, 1,  -1,-1, 1,      // v0-v1-v2 (front)
                      -1,-1, 1,   1,-1, 1,   1, 1, 1,      // v2-v3-v0

                       1, 1, 1,   1,-1, 1,   1,-1,-1,      // v0-v3-v4 (right)
                       1,-1,-1,   1, 1,-1,   1, 1, 1,      // v4-v5-v0

                       1, 1, 1,   1, 1,-1,  -1, 1,-1,      // v0-v5-v6 (top)
                      -1, 1,-1,  -1, 1, 1,   1, 1, 1,      // v6-v1-v0

                      -1, 1, 1,  -1, 1,-1,  -1,-1,-1,      // v1-v6-v7 (left)
                      -1,-1,-1,  -1,-1, 1,  -1, 1, 1,      // v7-v2-v1

                      -1,-1,-1,   1,-1,-1,   1,-1, 1,      // v7-v4-v3 (bottom)
                       1,-1, 1,  -1,-1, 1,  -1,-1,-1,      // v3-v2-v7

                       1,-1,-1,  -1,-1,-1,  -1, 1,-1,      // v4-v7-v6 (back)
                      -1, 1,-1,   1, 1,-1,   1,-1,-1 };    // v6-v5-v4



//================================================================
  int tst_glDrawArrays1 () {
//================================================================
// test glDrawArrays     draw patches from array



GLfloat normals1[]  = { 0, 0, 1,   0, 0, 1,   0, 0, 1,      // v0-v1-v2 (front)
                        0, 0, 1,   0, 0, 1,   0, 0, 1,      // v2-v3-v0

                        1, 0, 0,   1, 0, 0,   1, 0, 0,      // v0-v3-v4 (right)
                        1, 0, 0,   1, 0, 0,   1, 0, 0,      // v4-v5-v0

                        0, 1, 0,   0, 1, 0,   0, 1, 0,      // v0-v5-v6 (top)
                        0, 1, 0,   0, 1, 0,   0, 1, 0,      // v6-v1-v0

                       -1, 0, 0,  -1, 0, 0,  -1, 0, 0,      // v1-v6-v7 (left)
                       -1, 0, 0,  -1, 0, 0,  -1, 0, 0,      // v7-v2-v1
    
                        0,-1, 0,   0,-1, 0,   0,-1, 0,      // v7-v4-v3 (bottom)
                        0,-1, 0,   0,-1, 0,   0,-1, 0,      // v3-v2-v7

                        0, 0,-1,   0, 0,-1,   0, 0,-1,      // v4-v7-v6 (back)
                        0, 0,-1,   0, 0,-1,   0, 0,-1 };    // v6-v5-v4


GLfloat colors1[]   = { 1, 1, 1,   1, 1, 0,   1, 0, 0,      // v0-v1-v2 (front)
                        1, 0, 0,   1, 0, 1,   1, 1, 1,      // v2-v3-v0

                        1, 1, 1,   1, 0, 1,   0, 0, 1,      // v0-v3-v4 (right)
                        0, 0, 1,   0, 1, 1,   1, 1, 1,      // v4-v5-v0
    
                        1, 1, 1,   0, 1, 1,   0, 1, 0,      // v0-v5-v6 (top)
                        0, 1, 0,   1, 1, 0,   1, 1, 1,      // v6-v1-v0

                        1, 1, 0,   0, 1, 0,   0, 0, 0,      // v1-v6-v7 (left)
                        0, 0, 0,   1, 0, 0,   1, 1, 0,      // v7-v2-v1

                        0, 0, 0,   0, 0, 1,   1, 0, 1,      // v7-v4-v3 (bottom)
                        1, 0, 1,   1, 0, 0,   0, 0, 0,      // v3-v2-v7

                        0, 0, 1,   0, 0, 0,   0, 1, 0,      // v4-v7-v6 (back)
                        0, 1, 0,   0, 1, 1,   0, 0, 1 };    // v6-v5-v4

GLfloat *nvcDyn1;


  printf("tst_glDrawArrays1 \n");


  // glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
  // glEnable(GL_LIGHTING);
  // glEnable(GL_COLOR_MATERIAL);

  // glEnable(GL_CULL_FACE);                  // do not display backside
  // glCullFace (GL_BACK);                    // hide backside = default

  // glEnable(GL_DEPTH_TEST);
  // glDepthFunc(GL_LEQUAL);

  // glEnable(GL_NORMALIZE);
  // glEnable(GL_AUTO_NORMAL);


   // create per-vertex-normals
   nvcDyn1 = (GLfloat*) alloca (sizeof(GLfloat) * 36 * 3);
   // nvcDyn1 = (GLfloat*) malloc (sizeof(GLfloat) * 36 * 3);
   comp_normals (nvcDyn1, 36, (Point*)vertices1);


   glEnableClientState(GL_NORMAL_ARRAY);  // normals per vertex !
   glNormalPointer(GL_FLOAT, 0, normals1);
   // glNormalPointer(GL_FLOAT, 0, vertices1);
   // glNormalPointer(GL_FLOAT, 0, nvcDyn1);


   // glEnableClientState(GL_COLOR_ARRAY);
   glEnableClientState(GL_VERTEX_ARRAY);
   glVertexPointer(3, GL_DOUBLE, 0, vertices1);
   // glVertexPointer(3, GL_FLOAT, 0, vertices1);
   

   // glColorPointer(3, GL_FLOAT, 0, colors1);


   glDrawArrays(GL_TRIANGLES, 0, 36);

   glDisableClientState(GL_VERTEX_ARRAY);  // disable vertex arrays
   // glDisableClientState(GL_COLOR_ARRAY);
   glDisableClientState(GL_NORMAL_ARRAY);



  return 0;

}



//================================================================
  int tst_glDrawElements1 () {
//================================================================
// test glDrawElements1     draw indexed-patches from array
// Normals: only one normal for one vertex;
//   normal = the average value of the normals of the neighbour-faces
//   
 

// unsigned short indices1[] = {
int indices1[] = {
                       0,1,2, 2,3,0,   // o
                       0,3,4, 4,5,0,   // r
                       0,5,6, 6,1,0,   // vo
                       1,6,7, 7,2,1,   // li
                       7,4,3, 3,2,7,   // hi
                       4,7,6, 6,5,4};  // un

double vertices1[] = { 1, 1, 1,      // 0
                      -1, 1, 1,      // 1
                      -1,-1, 1,      // 2
                       1,-1, 1,      // 3
                       1,-1,-1,      // 4
                       1, 1,-1,      // 5
                      -1, 1,-1,      // 6
                      -1,-1,-1};     // 7
            
  int   ixNr, ptNr;
  Vec3f *nvcDyn1;


  ixNr = sizeof(indices1) / sizeof(short);
  ptNr = sizeof(vertices1) / sizeof(Point);


  printf(" tst_glDrawElements1  %d %d\n",ixNr,ptNr);


  // test / repair orientation
  // ck_orient ((Point*)vertices1, ptNr, indices1, ixNr);

  // create per-vertex-normals; 
  nvcDyn1 = (Vec3f*) alloca (sizeof(float) * ptNr * 3);
  comp_inormals (nvcDyn1, (Point*)vertices1, ptNr, indices1, ixNr);


  // activate and specify pointer to vertex array
  // glEnableClientState(GL_NORMAL_ARRAY);
  glEnableClientState(GL_VERTEX_ARRAY);
  glVertexPointer(3, GL_DOUBLE, 0, vertices1);

  glEnableClientState(GL_NORMAL_ARRAY);  // normals per vertex !
  // glNormalPointer(GL_DOUBLE, 0, vertices1);
  // glNormalPointer(GL_FLOAT, 0, nvcDyn1);
  glNormalPointer(GL_FLOAT, 0, nvcDyn1);

  // glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, indices1);
  glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, indices1);

  glDisableClientState(GL_VERTEX_ARRAY);
  // glDisableClientState(GL_NORMAL_ARRAY);

  return 0;

}


//=========================================================
  int gCad_fini () {
//=========================================================

  AP_User_reset ();

  return 0;
}


//=========================================================
  int gCad_main () {
//=========================================================

  long   dli;
  GLuint dlInd;
  GLint maxVertices, maxIndices;


  TX_Print(">>>>>>>>> gCad_main aus tst_glDrawElements1 <<<<<<<<<<\n");

    glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &maxVertices);
    glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &maxIndices);
  printf(" Max Elements Vertices: %d\n",maxVertices);
  printf(" Max Elements Indices:: %d\n",maxIndices);




/*
{
Point pa[4] = {{ 0., 0., 0.},    // 0
               { 1., 0., 0.},    // 1
               { 1.,-1., 0.},    // 2
               { 1.,-1., 0.}};    // 3
Vec3f vc1;
UT3F_vc_perp_3pt (&vc1, &pa[0], &pa[1], &pa[2]);
printf(" %f %f %f\n",vc1.dx,vc1.dy,vc1.dz);

}
return 0;
*/




  //----------------------------------------------------------------
  // open DispList
  dli = -1;
  dlInd = GL_fix_DL_ind (&dli);

  glNewList (dlInd, GL_COMPILE);

  glEnable (GL_LIGHTING);

  glColor3fv ((GLfloat*)&colTab[COL_YELLOW]);


  //----------------------------------------------------------------
  // draw Opengl-patches
  // tst_glDrawArrays1 ();

  // draw indexed-Opengl-patches
  tst_glDrawElements1 ();


  //----------------------------------------------------------------
  // close DispList
  glEndList ();

  DL_Redraw ();

  gCad_fini ();

  return 0;
}


// EOF
