/**
 * @file thdb3d.cxx
 */
  
/* Copyright (C) 2000 Stacho Mudrak
 * 
 * $Date: $
 * $RCSfile: $
 * $Revision: $
 *
 * -------------------------------------------------------------------- 
 * 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
 * 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.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 * --------------------------------------------------------------------
 */
 
#include "thdb3d.h"
#include <math.h>
 

void thdb3dlim::update(double x, double y, double z) {
  if (this->ok) {
    if (x < this->minx) this->minx = x;
    if (y < this->miny) this->miny = y;
    if (z < this->minz) this->minz = z;
    if (x > this->maxx) this->maxx = x;
    if (y > this->maxy) this->maxy = y;
    if (z > this->maxz) this->maxz = z;
  } else {
    this->minx = x;
    this->miny = y;
    this->minz = z;
    this->maxx = x;
    this->maxy = y;
    this->maxz = z;
    this->ok = true;
  }
}


void thdb3dlim::update(thdb3dlim * limits) {
  if (limits->ok) {
    this->update(limits->minx, limits->miny, limits->minz);
    this->update(limits->maxx, limits->maxy, limits->maxz);
  }
}

void thdb3dnm::normalize() {
  double nl;
  if (!this->norm) {
    nl = sqrt(this->x * this->x + this->y * this->y + this->z * this->z);
    if (nl > 0.0) {
      this->x /= nl;
      this->y /= nl;
      this->z /= nl;
    } else {
      this->z = 1.0;
    }
    this->norm = true;
  }
}
 

thdb3dfc * thdb3ddata::insert_face(int type) {
  thdb3dfc * face;
  face = &(*thdatabase3d.face_list.insert(thdatabase3d.face_list.end(),thdb3dfc()));
  face->type = type;
  this->nfaces++;
  if (this->lastfc == NULL) {
    this->firstfc = face;
    this->lastfc = face;
  } else {
    this->lastfc->next = face;
    this->lastfc = face;
  }
  return face;
}

thdb3dvx * thdb3ddata::insert_vertex(lxVec v, std::any dt) {
  return this->insert_vertex(v.x, v.y, v.z, std::move(dt));
}


thdb3dvx * thdb3ddata::insert_vertex(double vxx, double vxy, double vxz, std::any dt) {
  thdb3dvx * vertex;
  vertex = &(*thdatabase3d.vertex_list.insert(thdatabase3d.vertex_list.end(),thdb3dvx()));
  vertex->id = this->nvertices;
  vertex->x = vxx;
  vertex->y = vxy;
  vertex->z = vxz;
  this->limits.update(vxx, vxy, vxz);
  vertex->data = std::move(dt);
  this->nvertices++;
  if (this->lastvx == NULL) {
    this->firstvx = vertex;
    this->lastvx = vertex;
  } else {
    this->lastvx->next = vertex;
    this->lastvx = vertex;
  }
  return vertex;
}

thdb3dfx * thdb3dfc::insert_vertex(thdb3dvx * vx, std::any dt) {
  thdb3dfx * fx;
  fx = &(*thdatabase3d.face_vertex_list.insert(thdatabase3d.face_vertex_list.end(),thdb3dfx()));
  this->nvx++;
  if (this->lastfx == NULL) {
    this->firstfx = fx;
    this->lastfx = fx;
  } else {
    this->lastfx->next = fx;
    this->lastfx = fx;
  }
  fx->vertex = vx;
  fx->data = std::move(dt);
  return fx;
}

thdb3dnm * thdb3dfx::insert_normal(double nx, double ny, double nz) {
  if (this->normal == NULL) {
    this->normal = &(*thdatabase3d.normal_list.insert(thdatabase3d.normal_list.end(),thdb3dnm()));
    this->normal->x = nx;
    this->normal->y = ny;
    this->normal->z = nz;
  } else {
    this->normal->x += nx;
    this->normal->y += ny;
    this->normal->z += nz;
  }
  return this->normal;
}

thdb3dnm * thdb3dvx::insert_normal(double nx, double ny, double nz) {
  if (this->normal == NULL) {
    this->normal = &(*thdatabase3d.normal_list.insert(thdatabase3d.normal_list.end(),thdb3dnm()));
    this->normal->x = nx;
    this->normal->y = ny;
    this->normal->z = nz;
  } else {
    this->normal->x += nx;
    this->normal->y += ny;
    this->normal->z += nz;
  }
  return this->normal;
}

void thdb3ddata::export_thm(FILE * out) {
  thdb3dfc * fc;
  thdb3dfx * fx;
  const char * typestr;
  for (fc = this->firstfc; fc != NULL; fc = fc->next) {
    switch (fc->type) {
      case THDB3DFC_LINES:
        typestr = "GL_LINES";
        break;      
      case THDB3DFC_LINE_STRIP:
        typestr = "GL_LINE_STRIP";
        break;      
      case THDB3DFC_LINE_LOOP:
        typestr = "GL_LINE_LOOP";
        break;      
      case THDB3DFC_TRIANGLES:
        typestr = "GL_TRIANGLES";
        break;      
      case THDB3DFC_TRIANGLE_STRIP:
        typestr = "GL_TRIANGLE_STRIP";
        break;      
      case THDB3DFC_TRIANGLE_FAN:
        typestr = "GL_TRIANGLE_FAN";
        break;      
      case THDB3DFC_QUADS:
        typestr = "GL_QUADS";
        break;      
      case THDB3DFC_QUAD_STRIP:
        typestr = "GL_QUAD_STRIP";
        break;      
      case THDB3DFC_POLYGON:
        typestr = "GL_POLYGON";
        break;      
      default:
        typestr = "GL_POINTS";
        break;      
    }
    fprintf(out,"glBegin $GL::%s\n", typestr);
    for (fx = fc->firstfx; fx != NULL; fx = fx->next) {
      if (fx->vertex->normal != NULL) {
        fprintf(out,"glNormal3f %8.4f %8.4f %8.4f\n", 
          fx->vertex->normal->x, fx->vertex->normal->y, fx->vertex->normal->z);
      } else if (fx->normal != NULL) {
        fprintf(out,"glNormal3f %8.4f %8.4f %8.4f\n", 
          fx->normal->x, fx->normal->y, fx->normal->z);
      }
      fprintf(out,"glVertex3f %8.2f %8.2f %8.2f\n", 
        fx->vertex->x - this->exp_shift_x, 
        fx->vertex->y - this->exp_shift_y, 
        fx->vertex->z - this->exp_shift_z);
    }
    fprintf(out,"glEnd\n");
  }
}







void thdb3ddata::export_vrml(FILE * out) {
  thdb3dfc * fc;
  thdb3dfx * fx, * fx1, * fx2, * fx3;
  thdb3dvx * vx;
  bool reverse;  
  fprintf(out,"coord Coordinate {\n\tpoint [\n");
  for(vx = this->firstvx; vx != NULL; vx = vx->next)
    fprintf(out,"\t%8.2f %8.2f %8.2f,\n", 
      vx->x - this->exp_shift_x, 
      vx->y - this->exp_shift_y, 
      vx->z - this->exp_shift_z);
  fprintf(out,"\t]\n}\n");

  fprintf(out,"\tcoordIndex [\n");
  for (fc = this->firstfc; fc != NULL; fc = fc->next) {
    switch (fc->type) {
      case THDB3DFC_TRIANGLES:
        fx = fc->firstfx;
        while (fx != NULL) {
          fx1 = fx; fx = fx->next; fx2 = NULL; fx3 = NULL;
          if (fx != NULL) {
            fx2 = fx; fx = fx->next;
          }
          if (fx != NULL) {
            fx3 = fx; fx = fx->next;
          }
          if (fx3 != NULL) {
            fprintf(out,"\t%6ld,%6ld,%6ld,    -1,\n",
              fx1->vertex->id,
              fx2->vertex->id,
              fx3->vertex->id);
          }
        }
        break;      
      case THDB3DFC_TRIANGLE_STRIP:
        reverse = true;
        fx = fc->firstfx;
        if (fx != NULL) {
          fx2 = fx; fx = fx->next;
        }
        if (fx != NULL) {
          fx3 = fx; fx = fx->next;
        }
        while (fx != NULL) {
          fx1 = fx2;
          fx2 = fx3;
          fx3 = fx; 
          fx = fx->next;
          fprintf(out, "\t%6ld,%6ld,%6ld,    -1,\n",
            reverse ? fx1->vertex->id : fx2->vertex->id,
            reverse ? fx2->vertex->id : fx1->vertex->id,
            fx3->vertex->id);
          reverse = ! reverse;
        }
        break;      
      case THDB3DFC_LINE_STRIP:
        fx = fc->firstfx;
        while (fx != NULL) {
          fprintf(out, "\t%6ld, ", fx->vertex->id);
          fx = fx->next;
        }
        fprintf(out, "-1,\n");
        break;
      case THDB3DFC_TRIANGLE_FAN:
      case THDB3DFC_QUADS:
      case THDB3DFC_QUAD_STRIP:
      case THDB3DFC_POLYGON:
      case THDB3DFC_LINES:
      case THDB3DFC_LINE_LOOP:
      default:
        break;      
    }
  }
  fprintf(out,"\t]\n");
}

void thdb3ddata::export_3dmf(FILE * out) {
  thdb3dfc * fc;
  thdb3dfx * fx, * fx1, * fx2, * fx3;
  thdb3dvx * vx;
  bool reverse;

  if ((this->firstfc != NULL) && (this->firstfc->type == THDB3DFC_LINE_STRIP)) {

    for (fc = this->firstfc; fc != NULL; fc = fc->next) {
      fx = fc->firstfx;
      nfaces = 0;
      while (fx != NULL) {
        nfaces++;
        fx = fx->next;
      }
      if (nfaces > 0) {
        fprintf(out,"\tPolyLine ( %ld\n", nfaces);
        fx = fc->firstfx;
        nfaces = 0;
        while (fx != NULL) {
          vx = fx->vertex;
          fprintf(out,"\t%.2f %.2f %.2f\n", 
              vx->x - this->exp_shift_x, 
              vx->z - this->exp_shift_z,
              this->exp_shift_y - vx->y);
          fx = fx->next;
        }
        fprintf(out,"\t)\n");
      }
    }

    return;
  }
  
  fprintf(out,"\t%ld # nVertices\n", this->nvertices);
  for(vx = this->firstvx; vx != NULL; vx = vx->next)
    fprintf(out,"\t%.2f %.2f %.2f # %ld\n", 
      vx->x - this->exp_shift_x, 
      vx->z - this->exp_shift_z,
      this->exp_shift_y - vx->y, 
      vx->id);
  
  unsigned long nfaces = 0;
  for (fc = this->firstfc; fc != NULL; fc = fc->next) {
    switch (fc->type) {
      case THDB3DFC_TRIANGLES:
      nfaces += fc->nvx / 3;
      break;
      case THDB3DFC_TRIANGLE_STRIP:
      nfaces += fc->nvx - 2;
      break;
    }
  }
  
  fprintf(out,"\t%ld # nFaces\n\t0 # nContours\n", nfaces);
  nfaces = 0;
  for (fc = this->firstfc; fc != NULL; fc = fc->next) {
    switch (fc->type) {
      case THDB3DFC_TRIANGLES:
        fx = fc->firstfx;
        while (fx != NULL) {
          fx1 = fx; fx = fx->next; fx2 = NULL; fx3 = NULL;
          if (fx != NULL) {
            fx2 = fx; fx = fx->next;
          }
          if (fx != NULL) {
            fx3 = fx; fx = fx->next;
          }
          if (fx3 != NULL) {
            fprintf(out,"\t3 %ld %ld %ld # %ld\n",
              fx1->vertex->id,
              fx2->vertex->id,
              fx3->vertex->id,
              nfaces++);
          }
        }
        break;      
      case THDB3DFC_TRIANGLE_STRIP:
        fx = fc->firstfx;
        reverse = true;
        if (fx != NULL) {
          fx2 = fx; fx = fx->next;
        }
        if (fx != NULL) {
          fx3 = fx; fx = fx->next;
        }
        while (fx != NULL) {
          fx1 = fx2;
          fx2 = fx3;
          fx3 = fx; 
          fx = fx->next;
          fprintf(out,"\t3 %ld %ld %ld # %ld\n",
            reverse ? fx1->vertex->id : fx2->vertex->id,
            reverse ? fx2->vertex->id : fx1->vertex->id,
            fx3->vertex->id,
            nfaces++);
          reverse = ! reverse;
        }
        break;      
      case THDB3DFC_TRIANGLE_FAN:
      case THDB3DFC_QUADS:
      case THDB3DFC_QUAD_STRIP:
      case THDB3DFC_POLYGON:
      case THDB3DFC_LINES:
      case THDB3DFC_LINE_STRIP:
      case THDB3DFC_LINE_LOOP:
      default:
        break;      
    }
  }
}




void thdb3ddata::postprocess() {
  // znormalizuje vsetky normaly
  thdb3dvx * vx;
  thdb3dfx * fx;
  thdb3dfc * fc;
  for(vx = this->firstvx; vx != NULL; vx = vx->next) {
    if (vx->normal != NULL)
      vx->normal->normalize();
  }
  for(fc = this->firstfc; fc != NULL; fc = fc->next) {
    for(fx = fc->firstfx; fx != NULL; fx = fx->next) {
      if (fx->normal != NULL)
        fx->normal->normalize();
    }
  }
}

#define dxfprint0(vertex) fprintf(out, "10\n%.3f\n20\n%.3f\n30\n%.3f\n", (vertex)->x - this->exp_shift_x, (vertex)->y - this->exp_shift_y, (vertex)->z - this->exp_shift_z)
#define dxfprint1(vertex) fprintf(out, "11\n%.3f\n21\n%.3f\n31\n%.3f\n", (vertex)->x - this->exp_shift_x, (vertex)->y - this->exp_shift_y, (vertex)->z - this->exp_shift_z)
#define dxfprint2(vertex) fprintf(out, "12\n%.3f\n22\n%.3f\n32\n%.3f\n", (vertex)->x - this->exp_shift_x, (vertex)->y - this->exp_shift_y, (vertex)->z - this->exp_shift_z)
#define dxfprint3(vertex) fprintf(out, "13\n%.3f\n23\n%.3f\n33\n%.3f\n", (vertex)->x - this->exp_shift_x, (vertex)->y - this->exp_shift_y, (vertex)->z - this->exp_shift_z)



void thdb3ddata::export_dxf(FILE * out, const char * LAYER) {
  thdb3dfc * fc;
  thdb3dfx * fx, * fx1, * fx2 = NULL, * fx3 = NULL;
  bool reverse;  
  
  for (fc = this->firstfc; fc != NULL; fc = fc->next) {
    switch (fc->type) {
      case THDB3DFC_TRIANGLES:
        fx = fc->firstfx;
        while (fx != NULL) {
          fx1 = fx; fx = fx->next; fx2 = NULL; fx3 = NULL;
          if (fx != NULL) {
            fx2 = fx; fx = fx->next;
          }
          if (fx != NULL) {
            fx3 = fx; fx = fx->next;
          }
          if (fx3 != NULL) {
            fprintf(out,"0\n3DFACE\n8\n%s\n",LAYER);
            dxfprint0(fx1->vertex);
            dxfprint1(fx2->vertex);
            dxfprint2(fx3->vertex);
            dxfprint3(fx3->vertex);
          }
        }
        break;      
      case THDB3DFC_TRIANGLE_STRIP:
        reverse = true;
        fx = fc->firstfx;
        if (fx != NULL) {
          fx2 = fx; fx = fx->next;
        }
        if (fx != NULL) {
          fx3 = fx; fx = fx->next;
        }
        while (fx != NULL) {
          fx1 = fx2;
          fx2 = fx3;
          fx3 = fx; 
          fx = fx->next;
          fprintf(out,"0\n3DFACE\n8\n%s\n",LAYER);
          dxfprint0(reverse ? fx1->vertex : fx2->vertex);
          dxfprint1(reverse ? fx2->vertex : fx1->vertex);
          dxfprint2(fx3->vertex);
          dxfprint3(fx3->vertex);
          reverse = ! reverse;
        }
        break;      
      case THDB3DFC_LINE_STRIP:
        fx = fc->firstfx;
        fx2 = fx->next;
        while (fx2 != NULL) {
          fprintf(out,"0\nLINE\n8\n%s\n",LAYER);
          dxfprint0(fx->vertex);
          dxfprint1(fx2->vertex);
          fx = fx2;
          fx2 = fx2->next;
        }
        break;
      case THDB3DFC_TRIANGLE_FAN:
      case THDB3DFC_QUADS:
      case THDB3DFC_QUAD_STRIP:
      case THDB3DFC_POLYGON:
      case THDB3DFC_LINES:
      case THDB3DFC_LINE_LOOP:
      default:
        break;      
    }
  }
}


thdb3d thdatabase3d;


