//  main.cpp
//  OpenGL_HelloWorld
//  Created by pgAgent on 13/06/2015.
//  Copyright (c) 2015 RJM Programming. All rights reserved.
//  Can draw a cube and hexagonal prism
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include </Applications/MAMP/htdocs/glew-1.12.0/include/GL/glew.h>
#ifdef __APPLE__
#  include <GLUT/glut.h>
#else
#  include <GL/glut.h>
#endif


// Thanks to https://www.opengl.org/archives/resources/code/samples/glut_examples/examples/examples.html
//GLfloat light_diffuse[] = {1.0, 0.0, 0.0, 1.0};  /* Red diffuse light. */
GLfloat light_diffuse[] = {0.0, 1.0, 0.0, 1.0};  /* Green ambient light. */
//GLfloat light_position[] = {1.0, 1.0, 1.0, 0.0};  /* Infinite light location. */
GLfloat light_position[] = {1.0, 1.0, 1.0, 100.0};  /* Not Infinite light location. */
GLfloat n[6][3] = {  /* Normals for the 6 faces of a cube. */
    {-1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {1.0, 0.0, 0.0},
    {0.0, -1.0, 0.0}, {0.0, 0.0, 1.0}, {0.0, 0.0, -1.0} };
GLint faces[6][4] = {  /* Vertex indices for the 6 faces of a cube. */
    {0, 1, 2, 3}, {3, 2, 6, 7}, {7, 6, 5, 4},
    {4, 5, 1, 0}, {5, 6, 2, 1}, {7, 4, 0, 3} };
GLfloat v[8][3];  /* Will be filled in with X,Y,Z vertexes. */


GLfloat n6[8][3] = {  /* Normals for the 8 faces of a  hexagonal prism. */
    {-1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 1.0, 0.0}, {1.0, 0.0, 0.0},
    {0.0, -1.0, 0.0}, {0.0, 0.0, 1.0}, {0.0, 0.0, 1.0}, {0.0, 0.0, -1.0} };
GLint faces6[8][6] = {  /* Vertex indices for the 8 faces of a hexagonal prism. */
    {0, 1, 2, 3, 4, 5}, {0, 0, 11, 5, 6, 6}, {0, 0, 11, 1, 10, 10}, {11, 10, 9, 8, 7, 6},
    {1, 1, 10, 2, 9, 9}, {2, 2, 9, 3, 8, 8}, {4, 4, 7, 3, 8, 8}, {5, 5, 6, 4, 7, 7} };
GLfloat v6[12][3];  /* Will be filled in with X,Y,Z vertexes. */

GLfloat sqrt3 = 1.73205080757f;

GLfloat sidelen = 0.0;

struct pointXZ {
    GLfloat x;
    GLfloat z;
} pointXZ;


void drawHexagonalPrism(void) {
    int i;
    
    for (i = 0; i < 8; i++) {
        glBegin(GL_POLYGON);
        glNormal3fv(&n6[i][0]);
        glVertex3fv(&v6[faces6[i][0]][0]);
        glVertex3fv(&v6[faces6[i][1]][0]);
        glVertex3fv(&v6[faces6[i][2]][0]);
        glVertex3fv(&v6[faces6[i][3]][0]);
        glVertex3fv(&v6[faces6[i][4]][0]);
        glVertex3fv(&v6[faces6[i][5]][0]);
        glEnd();
    }
}

// macro to use to set a = b X c
void crossProduct(GLfloat a[], GLfloat b[], GLfloat c[]) {  // thanks to http://cs.indstate.edu
    a[0] = b[1] * c[2] - c[1] * b[2];
    a[1] = b[2] * c[0] - c[2] * b[0];
    a[2] = b[0] * c[1] - c[0] * b[1];
}

void drawHexPrism(GLfloat s, GLfloat h) { // thanks to http://cs.indstate.edu
    GLfloat a = s/2.0f*sqrt3; // apothem
    GLfloat halfs = s/2.0f;   // half side length
    struct pointXZ hex[] = {{-halfs, a}, {halfs, a}, {a, 0.0f},
        {halfs, -a}, {-halfs, -a}, {-a, 0.0f},
        {-halfs, a}};
    // base.
    glNormal3f(0.0f,-1.0f,0.0f);
    glBegin(GL_POLYGON);
    for(int i=0;i<6;i++)
        glVertex3f(hex[i].x, 0.0f, hex[i].z);
    glEnd();
    // top
    glPushMatrix();
    glTranslatef(0.0f,h,0.0f);
    glNormal3f(0.0f,1.0f,0.0f);
    glBegin(GL_POLYGON);
    for(int i=0;i<6;i++)
        glVertex3f(hex[i].x, 0.0f, hex[i].z);
    glEnd();
    glPopMatrix();
    // for use in computing normal vectors
    GLfloat v[3], p1[3], p2[3];
    // 6 sides
    for(int i=0;i<6;i++) {
        // compute normal vector for this polygon
        // p1 points up
        p1[0] = 0.0f; p1[1] = 1.0f; p1[2] = 0.0f;
        // p2 points along the base of the rectangle
        p2[0] = hex[i].x-hex[i+1].x; p2[1] = 0.0f; p2[3] = hex[i].z-hex[i+1].z;
        crossProduct(v, p1, p2);  // now v points out from rectangle
        glNormal3f(v[0],v[1],v[2]);
        //cout << v[0] << " " << v[1] << " " << v[2] << endl;
        
        glBegin(GL_QUADS);
        glVertex3f(hex[i].x, 0.0f, hex[i].z);
        glVertex3f(hex[i].x, h, hex[i].z);
        glVertex3f(hex[i+1].x, h, hex[i+1].z);
        glVertex3f(hex[i+1].x, 0.0f, hex[i+1].z);
        glEnd();
    }
}

void drawBox(void) {
    int i;
    
    for (i = 0; i < 6; i++) {
        glBegin(GL_QUADS);
        glNormal3fv(&n[i][0]);
        glVertex3fv(&v[faces[i][0]][0]);
        glVertex3fv(&v[faces[i][1]][0]);
        glVertex3fv(&v[faces[i][2]][0]);
        glVertex3fv(&v[faces[i][3]][0]);
        glEnd();
    }
}

void displaynew(void) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    drawBox();
    glutSwapBuffers();
}

void displayhexagonalprism(void) {
    int cnum_sides = 6;
    int clength_of_side = 2;
    double capothem = (double)clength_of_side * sqrt(3.0)/2.0; // = R sqrt(3)/2
    if (7 == 7) {
    
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     if (sidelen > 0.0) {
            drawHexPrism(sidelen, (GLfloat)1);   //drawHexagonalPrism();
     } else {
            drawHexPrism((GLfloat)clength_of_side, (GLfloat)1);   //drawHexagonalPrism();
     }
    } else {
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     glPushMatrix();
     glBegin(GL_POLYGON);
     for(int i = 0; i < cnum_sides; i++) {
      glBegin(GL_POLYGON);
      glVertex3f(0 - (clength_of_side / 2), -capothem, 0.0);
      glRotatef(360.0 / cnum_sides, 0.0, 0.0, 1.0); //I'm not sure if this statement will work inside a glBegin/glEnd pair
      glEnd();
     }
     glEnd();
     glPopMatrix();
    }
    glutSwapBuffers();
}

void init(void) {
    /* Setup cube vertex data. */
    v[0][0] = v[1][0] = v[2][0] = v[3][0] = -1;
    v[4][0] = v[5][0] = v[6][0] = v[7][0] = 1;
    v[0][1] = v[1][1] = v[4][1] = v[5][1] = -1;
    v[2][1] = v[3][1] = v[6][1] = v[7][1] = 1;
    v[0][2] = v[3][2] = v[4][2] = v[7][2] = 1;
    v[1][2] = v[2][2] = v[5][2] = v[6][2] = -1;
    
    /* Enable a single OpenGL light. */
    //glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
    glLightfv(GL_LIGHT0, GL_AMBIENT, light_diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);
    
    /* Use depth buffering for hidden surface elimination. */
    glEnable(GL_DEPTH_TEST);
    
    /* Setup the view of the cube. */
    glMatrixMode(GL_PROJECTION);
    gluPerspective( /* field of view in degree */ 40.0,
                   /* aspect ratio */ 1.0,
                   /* Z near */ 1.0, /* Z far */ 10.0);
    glMatrixMode(GL_MODELVIEW);
    gluLookAt(0.0, 0.0, 5.0,  /* eye is at (0,0,5) */
              0.0, 0.0, 0.0,      /* center is at (0,0,0) */
              0.0, 1.0, 0.0);      /* up is in positive Y direction */
    
    /* Adjust cube position to be asthetic angle. */
    glTranslatef(0.0, 0.0, -1.0);
    glRotatef(60, 1.0, 0.0, 0.0);
    glRotatef(-20, 0.0, 0.0, 1.0);
}

void init6(char *cl_o_s) {
    if (strstr(cl_o_s, ",")) {
        /* Setup hexagonal prism vertex data. */
        sscanf(cl_o_s, "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f", &v6[0][0], &v6[0][1], &v6[1][0], &v6[1][1], &v6[2][0], &v6[2][1], &v6[3][0], &v6[3][1], &v6[4][0], &v6[4][1], &v6[5][0], &v6[5][1]);
        sscanf(cl_o_s, "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f", &v6[11][0], &v6[11][1], &v6[10][0], &v6[10][1], &v6[9][0], &v6[9][1], &v6[8][0], &v6[8][1], &v6[7][0], &v6[7][1], &v6[6][0], &v6[6][1]);
    } else {
     /* Setup hexagonal prism vertex data. */
     v6[0][0] = v6[1][0] = v6[2][0] = v6[3][0] = -1;
     v6[4][0] = v6[5][0] = v6[6][0] = v6[7][0] = 1;
     v6[0][1] = v6[1][1] = v6[4][1] = v6[5][1] = -1;
     v6[2][1] = v6[3][1] = v6[6][1] = v6[7][1] = 1;
    }
    v6[0][2] = v6[3][2] = v6[4][2] = v6[7][2] = v6[8][2] = v6[11][2] = 1;
    v6[1][2] = v6[2][2] = v6[5][2] = v6[6][2] = v6[9][2] = v6[10][2] = -1;
    
    /* Enable a single OpenGL light. */
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
    //glLightfv(GL_LIGHT0, GL_AMBIENT, light_diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);
    
    /* Use depth buffering for hidden surface elimination. */
    glEnable(GL_DEPTH_TEST);
    
    /* Setup the view of the cube. */
    glMatrixMode(GL_PROJECTION);
    gluPerspective( /* field of view in degree */ 40.0,
                   /* aspect ratio */ 1.0,
                   /* Z near */ 1.0, /* Z far */ 10.0);
    glMatrixMode(GL_MODELVIEW);
    gluLookAt(0.0, 0.0, 5.0,  /* eye is at (0,0,5) */
              0.0, 0.0, 0.0,      /* center is at (0,0,0) */
              0.0, 1.0, 0.0);      /* up is in positive Y direction */
    
    /* Adjust cube position to be asthetic angle. */
    glTranslatef(0.0, 0.0, -1.0);
    glRotatef(60, 1.0, 0.0, 0.0);
    glRotatef(-20, 0.0, 0.0, 1.0);
}

void init6(int l_o_s) {
    /* Setup hexagonal prism vertex data. */
    v6[0][2] = v6[3][2] = v6[4][2] = v6[7][2] = v6[8][2] = v6[11][2] = l_o_s;
    v6[1][2] = v6[2][2] = v6[5][2] = v6[6][2] = v6[9][2] = v6[10][2] = -l_o_s;
    
    /* Enable a single OpenGL light. */
    //glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
    glLightfv(GL_LIGHT0, GL_AMBIENT, light_diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);
    
    /* Use depth buffering for hidden surface elimination. */
    glEnable(GL_DEPTH_TEST);
    
    /* Setup the view of the cube. */
    glMatrixMode(GL_PROJECTION);
    gluPerspective( /* field of view in degree */ 40.0,
                   /* aspect ratio */ 1.0,
                   /* Z near */ 1.0, /* Z far */ 10.0);
    glMatrixMode(GL_MODELVIEW);
    gluLookAt(0.0, 0.0, 5.0,  /* eye is at (0,0,5) */
              0.0, 0.0, 0.0,      /* center is at (0,0,0) */
              0.0, 1.0, 0.0);      /* up is in positive Y direction */
    
    /* Adjust cube position to be asthetic angle. */
    glTranslatef(0.0, 0.0, -1.0);
    glRotatef(60, 1.0, 0.0, 0.0);
    glRotatef(-20, 0.0, 0.0, 1.0);
}

void display (void) {
    if (1 == 1) {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        drawBox();
        glutSwapBuffers();
    } else {
    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glLoadIdentity();
    
    glFlush();
    }
}

static int make_resources(void) {
    return 1;
}

static void update_fade_factor(void) {
}

static void render(void) {
    glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
    glClear(GL_COLOR_BUFFER_BIT);
    glutSwapBuffers();
}

int main (int argc, char **argv) {
    if (6 == 6) {  // thanks to http://www.experts-exchange.com/Programming/Game/Q_21374680.html and http://mathforum.org/dr.math/faq/formulas/faq.regpoly.html
        int num_sides = 6;
        int length_of_side = 2;
        double apothem = (double)length_of_side * sqrt(3.0)/2.0; // = R sqrt(3)/2
        char creal[100];
        sprintf(creal, "Hexagonal prism %lf", apothem);
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
        if (argc > 1) {
            sscanf(argv[1], "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f", &v6[0][0], &v6[0][1], &v6[1][0], &v6[1][1], &v6[2][0], &v6[2][1], &v6[3][0], &v6[3][1], &v6[4][0], &v6[4][1], &v6[5][0], &v6[5][1]);
            sidelen = fmod(sqrt((v6[0][0] - v6[1][0]) * (v6[0][0] - v6[1][0]) + (v6[0][1] - v6[1][1]) * (v6[0][1] - v6[1][1])), 2.1);
            sprintf(creal, "Hexagonal Prism %f", sidelen);
            sscanf(argv[1], "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f", &v6[11][0], &v6[11][1], &v6[10][0], &v6[10][1], &v6[9][0], &v6[9][1], &v6[8][0], &v6[8][1], &v6[7][0], &v6[7][1], &v6[6][0], &v6[6][1]);
            glutCreateWindow(creal);
            glutDisplayFunc(displayhexagonalprism);
            init6(argv[1]);
        } else {
            glutCreateWindow(creal); //"Hexagonal prism"); //creal);
            glutDisplayFunc(displayhexagonalprism);
            init6(length_of_side);
        }
        glutMainLoop();
    } else if (1 == 1) { // thanks to https://www.opengl.org/archives/resources/code/samples/glut_examples/examples/examples.html
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
        glutCreateWindow("Green 3D lighted cube");
        glutDisplayFunc(displaynew);
        init();
        glutMainLoop();
    } else if (1 == 1) {  // thanks to http://duriansoftware.com/joe/An-intro-to-modern-OpenGL.-Chapter-2:-Hello-World:-The-Slideshow.html
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
        glutInitWindowSize(400, 300);
        glutCreateWindow("Hello World");
        glutDisplayFunc(&render);
        glutIdleFunc(&update_fade_factor);
        glutMainLoop();
    } else {
        glutInit(&argc, argv);
        glutInitDisplayMode (GLUT_SINGLE);
        glutInitWindowSize (500, 500);
        glutInitWindowPosition (100, 100);
        glutCreateWindow ("Your first OpenGL Window ... Hello World");
        glutDisplayFunc(display);
        glutMainLoop();
    }
}
