/*    
      glm.h
      Nate Robins, 1997, 2000
      nate@pobox.com, http://www.pobox.com/~nate
 
      Wavefront OBJ model file format reader/writer/manipulator.

      Includes routines for generating smooth normals with
      preservation of edges, welding redundant vertices & texture
      coordinate generation (spheremap and planar projections) + more.

 */


#include <GL/glut.h>
#include <math.h>
#include <map>
//#include <vector>
//#include <string>
//#include <src/lscm_with_c_api.h>
#include <QImage>

#ifndef M_PI
#define M_PI 3.14159265f
#endif

#define GLM_dEpsilon 0.0005f        /* 0.0005f*/
#define GLM_NONE     (0)            /* render with only vertices */
#define GLM_FLAT     (1 << 0)       /* render with facet normals */
#define GLM_SMOOTH   (1 << 1)       /* render with vertex normals */
#define GLM_TEXTURE  (1 << 2)       /* render with texture coords */
#define GLM_COLOR    (1 << 3)       /* render with colors */
#define GLM_MATERIAL (1 << 4)       /* render with materials */


class Vector2 {
public:
    Vector2(double x_in, double y_in) : x(x_in), y(y_in) { }
    Vector2() : x(0), y(0) { }
    double length() const {
        return sqrt(x*x + y*y);
    }
    double x ;
    double y ;
} ;

class Vector3 {
public:
    Vector3(double x_in, double y_in, double z_in) : x(x_in), y(y_in), z(z_in) { }
    Vector3() : x(0), y(0), z(0) { }
    double length() const {
        return sqrt(x*x + y*y + z*z) ;
    }
    void normalize() {
        double l = length() ;
        x /= l ; y /= l ; z /= l ;
    }
    double x ;
    double y ;
    double z ;
} ;
/*
  * Plane in Oxyz: f() = ax + by + cz + d
  */
class Plane {
public:
    Plane(double a_in, double b_in,double c_in,double d_in):a(a_in),b(b_in),c(c_in),d(d_in), n(a_in,b_in,c_in){}
    Plane():a(0),b(0),c(0),d(0),n(0,0,0){  }
    double f(double x, double y, double z) const {
        return (a*x +b*y + c*z + d);
    }
    void prinft(Vector3 v) const {

//        std::cout <<  "\t a=" << a << " b = " << b << " c= "  << c << " d=" << d << " y = " << v.y << std::endl;
        double result = (v.y +d);
//        result -=result;
//        std::cout <<  "\t result  =" << result << std::endl;
//        std::cout <<  "\t b*v.y +d  =" << (b*v.y + d) << std::endl;
//        std::cout <<  "\t f =" << f(v) << std::endl;
    }
    double f(Vector3 v) const {
        double result = (a*v.x +b*v.y + c*v.z + d);
        if(fabs(result) <= GLM_dEpsilon) result=0;

        return result;
    }
    double length() const {
        return sqrt(a*a + b*b + c*c) ;
    }
    double a;
    double b;
    double c;
    double d;
    Vector3 n;
};
class Line2{
public:
    Line2(Vector2 _v0, Vector2 _v1): v0(_v0.x,_v0.y),v1(_v1.x,_v1.y),n((_v1.x - _v0.x),(_v1.y - _v0.y)) {}
    Line2(double x0,double y0,double x1, double y1): v0(x0,y0),v1(x1,y1),n((x1 - x0),(y1 - y0)){}
    double fxy(double x, double y) const {
        return (n.y*(x-v0.x) - n.x*(y - v0.y));
    }
    Vector2 getPoint(double x) const {
        double t = (x - v0.x)/n.x;
        Vector2 p(x,(v0.y + n.y*t));
        return p;
    }
    double len_n() const {
        return n.length();
    }
    Vector2 v0;
    Vector2 v1;
    Vector2 n;
};
class Line3{
public:
    Line3(Vector3 v1, Vector3 v2):n(v2.x-v1.x,v2.y-v1.y,v2.z-v1.z),M0(v1.x,v1.y,v1.z){ }
    Vector3 getPoint(double y){
        double t = (y-M0.y)/n.y;
        Vector3 p(M0.x + n.x*t,
                  y,
                  M0.z + n.z*t);
        return p;
    }

    Vector3 M0;
    Vector3 n;
};

//class Line {
//public:
//    Line(Vector3 v1, Vector3 v2):n(v2.x-v1.x,v2.y-v1.y,v2.z-v1.z),M0(v1.x,v1.y,v1.z){ }
//    Line( Plane p1,Plane p2) : P1(p1.a,p1.b,p1.c,p1.d), P2(p2.a,p2.b,p2.c,p2.d){ }
//    Vector3 n;
//    Vector3 M0;
//    Plane P1;
//    Plane P2;
//};

/* GLMmaterial: Structure that defines a material in a model. 
 */
typedef struct _GLMmaterial
{
  char* name;                   /* name of material */
  GLfloat diffuse[4];           /* diffuse component */
  GLfloat ambient[4];           /* ambient component */
  GLfloat specular[4];          /* specular component */
  GLfloat emmissive[4];         /* emmissive component */
  GLfloat shininess;            /* specular exponent */
} GLMmaterial;

/* GLMtriangle: Structure that defines a triangle in a model.
 */
typedef struct _GLMtriangle {
  GLuint vindices[3];           /* array of triangle vertex indices */
  GLuint nindices[3];           /* array of triangle normal indices */
  GLuint tindices[3];           /* array of triangle texcoord indices*/
  GLuint findex;                /* index of triangle facet normal */
  GLuint oindex;                /* index of triangle in original model*/
} GLMtriangle;

/* GLMgroup: Structure that defines a group in a model.
 */
typedef struct _GLMgroup {
  char*             name;           /* name of this group */
  GLuint            numtriangles;   /* number of triangles in this group */
  GLuint*           triangles;      /* array of triangle indices */
  GLuint            material;       /* index to material for group */
  struct _GLMgroup* next;           /* pointer to next group in model */
} GLMgroup;

/* GLMmodel: Structure that defines a model.
 */
typedef struct _GLMmodel {
  char*    pathname;            /* path to this model */
  char*    mtllibname;          /* name of the material library */

  GLuint   numvertices;         /* number of vertices in model */
  GLfloat* vertices;            /* array of vertices  */

  GLuint   numnormals;          /* number of normals in model */
  GLfloat* normals;             /* array of normals */

  GLuint   numtexcoords;        /* number of texcoords in model */
  GLfloat* texcoords;           /* array of texture coordinates */

  GLuint   numfacetnorms;       /* number of facetnorms in model */
  GLfloat* facetnorms;          /* array of facetnorms */

  GLuint       numtriangles;    /* number of triangles in model */
  GLMtriangle* triangles;       /* array of triangles */

  GLuint       nummaterials;    /* number of materials in model */
  GLMmaterial* materials;       /* array of materials */

  GLuint       numgroups;       /* number of groups in model */
  GLMgroup*    groups;          /* linked list of groups */

  GLfloat position[3];          /* position of the model */

} GLMmodel;

class Mesh{
public:
    Mesh(char* name,std::vector<int> ids): _name(name), triangle_idx(ids) {}
    Mesh(char* name): _name(name) {}
    char*  _name;
    std::vector<int> triangle_idx;
};
bool IsEqualZero(double dX);
//========================================Set Y is contants==========================
std::map<float,std::map<float,Vector2> > glmGetPointsInLine(GLMmodel* model);
GLvoid glmGenerateTexCoord(GLMmodel* model,QString name);
GLvoid glmScanline(float y, int v_y0_idx,int &xzidx_index,float* &map_xzidx,int &nb,GLMmodel* model);
GLvoid glmScanline1(float y, int v_y0_idx,int &xzidx_index,float* &map_xzidx,int &nb,GLMmodel* model);
GLfloat glmGetTriangleDistance(GLMmodel* model, int tri_id);
GLvoid glmComplementPoints(float y, std::map<float,Vector2> &map_X_idx,GLMmodel* model);
GLvoid glmComplementPointsInLine(float y, float* &map_xz,int &nb,GLMmodel* model);
GLvoid glmGenerateImage(GLMmodel* model,QString outFileName);
int* glmLoadTextureFromFile(const char* filename, int &src_width, int &src_height, int &width, int &height,float* &dh);
GLvoid glmGenerateImage(const char *filename, QImage* &src_in, QString outFileName);
GLvoid glmGenerateImage(const char *filename, QString folder);
GLvoid glmGenerateImage(GLMmodel* model, QString outFileName, float ratewh/*rate of width and height*/);
GLvoid glmGetDataFromFile(int y,char* fileIndex,std::vector<std::string> &vectorIndex);
QImage blurred(const QImage& image, const QRect& rect, int radius, bool alphaOnly = false);
void morphologyErision(QImage &src,int k);
void morphologyDilatation(QImage &src,int k);
void morphologyClosing(QImage &src,int k);
void morphologyErision(QImage &src,int kenel_x, int kenel_y);
//========================================================Split Model into sub-models==========================
GLvoid getTex_Min_Max_XY_idx(int &tex_min_x_idx,int &tex_min_y_idx,int &tex_max_x_idx,int &tex_max_y_idx,GLMmodel* model);

GLvoid glmWriteOBJ(Mesh* mesh,GLMmodel* model, char filename[], std::map<int,int> &map_old_new_index);

GLvoid testTextureCoordinates(GLMmodel* model, char* texImageName,std::vector<int> seamsVertex, std::vector<int> seamsTriangle, std::map<int,int> map_old_new_vertex_idx, Mesh* mesh = NULL,int test_scale=1);
GLvoid testTextureCoordinates(GLMmodel *l_model, GLMmodel* r_model, char *texImageName, std::vector<int> seamsVertex, std::vector<int> seamsTriangle, int* p_max_min,std::map<int, int> l_map, std::map<int, int> r_map,Mesh* l_mesh, Mesh* r_mesh);

GLvoid glmNormalizeTexCoor(GLMmodel* model,float texScale, bool isNormalize=true);
GLvoid glmCorrectTexCoor(GLMmodel* l_model, GLMmodel* r_model, std::vector<int> seamsVertex, std::vector<int> seamsTriangle,
                         std::map<int,int> l_map, std::map<int,int> r_map, Mesh* l_mesh, Mesh* r_mesh);

GLvoid  glmRotateAroundMean(GLfloat* vertex, GLint nbVertex);
//========================================================Split Model into sub-models==========================

/* glmUnitize: "unitize" a model by translating it to the origin and
 * scaling it to fit in a unit cube around the origin.  Returns the
 * scalefactor used.
 *
 * model - properly initialized GLMmodel structure 
 */
GLfloat
glmUnitize(GLMmodel* model);

/* glmDimensions: Calculates the dimensions (width, height, depth) of
 * a model.
 *
 * model      - initialized GLMmodel structure
 * dimensions - array of 3 GLfloats (GLfloat dimensions[3])
 */
GLvoid
glmDimensions(GLMmodel* model, GLfloat* dimensions);

/* glmScale: Scales a model by a given amount.
 * 
 * model - properly initialized GLMmodel structure
 * scale - scalefactor (0.5 = half as large, 2.0 = twice as large)
 */
GLvoid
glmScale(GLMmodel* model, GLfloat scale);

/* glmReverseWinding: Reverse the polygon winding for all polygons in
 * this model.  Default winding is counter-clockwise.  Also changes
 * the direction of the normals.
 * 
 * model - properly initialized GLMmodel structure 
 */
GLvoid
glmReverseWinding(GLMmodel* model);

/* glmFacetNormals: Generates facet normals for a model (by taking the
 * cross product of the two vectors derived from the sides of each
 * triangle).  Assumes a counter-clockwise winding.
 *
 * model - initialized GLMmodel structure
 */
GLvoid
glmFacetNormals(GLMmodel* model);

/* glmVertexNormals: Generates smooth vertex normals for a model.
 * First builds a list of all the triangles each vertex is in.  Then
 * loops through each vertex in the the list averaging all the facet
 * normals of the triangles each vertex is in.  Finally, sets the
 * normal index in the triangle for the vertex to the generated smooth
 * normal.  If the dot product of a facet normal and the facet normal
 * associated with the first triangle in the list of triangles the
 * current vertex is in is greater than the cosine of the angle
 * parameter to the function, that facet normal is not added into the
 * average normal calculation and the corresponding vertex is given
 * the facet normal.  This tends to preserve hard edges.  The angle to
 * use depends on the model, but 90 degrees is usually a good start.
 *
 * model - initialized GLMmodel structure
 * angle - maximum angle (in degrees) to smooth across
 */
GLvoid
glmVertexNormals(GLMmodel* model, GLfloat angle);

/* glmLinearTexture: Generates texture coordinates according to a
 * linear projection of the texture map.  It generates these by
 * linearly mapping the vertices onto a square.
 *
 * model - pointer to initialized GLMmodel structure
 */
GLvoid
glmLinearTexture(GLMmodel* model);

/* glmSpheremapTexture: Generates texture coordinates according to a
 * spherical projection of the texture map.  Sometimes referred to as
 * spheremap, or reflection map texture coordinates.  It generates
 * these by using the normal to calculate where that vertex would map
 * onto a sphere.  Since it is impossible to map something flat
 * perfectly onto something spherical, there is distortion at the
 * poles.  This particular implementation causes the poles along the X
 * axis to be distorted.
 *
 * model - pointer to initialized GLMmodel structure
 */
GLvoid
glmSpheremapTexture(GLMmodel* model);

/* glmDelete: Deletes a GLMmodel structure.
 *
 * model - initialized GLMmodel structure
 */
GLvoid
glmDelete(GLMmodel* model);

/* glmReadOBJ: Reads a model description from a Wavefront .OBJ file.
 * Returns a pointer to the created object which should be free'd with
 * glmDelete().
 *
 * filename - name of the file containing the Wavefront .OBJ format data.  
 */
GLMmodel* 
glmReadOBJ(const char* filename);

/* glmWriteOBJ: Writes a model description in Wavefront .OBJ format to
 * a file.
 *
 * model    - initialized GLMmodel structure
 * filename - name of the file to write the Wavefront .OBJ format data to
 * mode     - a bitwise or of values describing what is written to the file
 *            GLM_NONE    -  write only vertices
 *            GLM_FLAT    -  write facet normals
 *            GLM_SMOOTH  -  write vertex normals
 *            GLM_TEXTURE -  write texture coords
 *            GLM_FLAT and GLM_SMOOTH should not both be specified.
 */
GLvoid
glmWriteOBJ(GLMmodel* model, char* filename, GLuint mode);

/* glmDraw: Renders the model to the current OpenGL context using the
 * mode specified.
 *
 * model    - initialized GLMmodel structure
 * mode     - a bitwise OR of values describing what is to be rendered.
 *            GLM_NONE    -  render with only vertices
 *            GLM_FLAT    -  render with facet normals
 *            GLM_SMOOTH  -  render with vertex normals
 *            GLM_TEXTURE -  render with texture coords
 *            GLM_FLAT and GLM_SMOOTH should not both be specified.
 */
GLvoid
glmDraw(GLMmodel* model, GLuint mode);

/* glmList: Generates and returns a display list for the model using
 * the mode specified.
 *
 * model    - initialized GLMmodel structure
 * mode     - a bitwise OR of values describing what is to be rendered.
 *            GLM_NONE    -  render with only vertices
 *            GLM_FLAT    -  render with facet normals
 *            GLM_SMOOTH  -  render with vertex normals
 *            GLM_TEXTURE -  render with texture coords
 *            GLM_FLAT and GLM_SMOOTH should not both be specified.  
 */
GLuint
glmList(GLMmodel* model, GLuint mode);

/* glmWeld: eliminate (weld) vectors that are within an epsilon of
 * each other.
 *
 * model      - initialized GLMmodel structure
 * epsilon    - maximum difference between vertices
 *              ( 0.00001 is a good start for a unitized model)
 *
 */
GLvoid
glmWeld(GLMmodel* model, GLfloat epsilon);

/* glmReadPPM: read a PPM raw (type P6) file.  The PPM file has a header
 * that should look something like:
 *
 *    P6
 *    # comment
 *    width height max_value
 *    rgbrgbrgb...
 *
 * where "P6" is the magic cookie which identifies the file type and
 * should be the only characters on the first line followed by a
 * carriage return.  Any line starting with a # mark will be treated
 * as a comment and discarded.   After the magic cookie, three integer
 * values are expected: width, height of the image and the maximum
 * value for a pixel (max_value must be < 256 for PPM raw files).  The
 * data section consists of width*height rgb triplets (one byte each)
 * in binary format (i.e., such as that written with fwrite() or
 * equivalent).
 *
 * The rgb data is returned as an array of unsigned chars (packed
 * rgb).  The malloc()'d memory should be free()'d by the caller.  If
 * an error occurs, an error message is sent to stderr and NULL is
 * returned.
 *
 * filename   - name of the .ppm file.
 * width      - will contain the width of the image on return.
 * height     - will contain the height of the image on return.
 *
 */
GLubyte* 
glmReadPPM(char* filename, int* width, int* height);
