#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
		
    radius = 400;
    max = 60;
    
    ofEnableDepthTest(); //make sure we test depth for 3d

    ofSetVerticalSync(true);
    ofEnableLighting();
    ofEnableAlphaBlending();
    ofEnableSmoothing();

    mesh.addVertex(glm::vec3(0,0,0)); // add center vertex
    mesh.addColor(ofColor(137,137,140,255)); // center is same as bg
    mesh.addNormal(glm::vec3(0,0,1)); // center normal points up

    zfreq = 3.;
    zamt = .3;

    //loop around and make verts in a circle, with a bit of a z-wave
    for (int i = 0; i < max; i++){
        float step = 2*PI/max; // step size around circle
        float theta = ofMap(i, 0, max-1, 0, 2*PI - step); //map i as circle divisions to actual radian values around the circle (note we don't go quite all the way around by one step, because it will be the same as where we started, so we'll just index that starting vertex when we make faces)
 
        float prevTheta = theta - step; //one step back
        float nextTheta = theta + step; // one step forward
        
        // create vertices in polar coordinates, plus a sine wave for z
        glm::vec3 p(radius*cos(theta),radius*sin(theta), radius*zamt*sin(zfreq*theta) );
        // add this vertex
        mesh.addVertex(p);
        
        // we need these for calculating normals
        glm::vec3 prev(radius*cos(prevTheta),radius*sin(prevTheta),radius*zamt*sin(zfreq*prevTheta) );        
        glm::vec3 next(radius*cos(nextTheta),radius*sin(nextTheta),radius*zamt*sin(zfreq*nextTheta) );
        
        // our normals for each triangle face is the cross product of the two vectors making up that sliver 
        glm::vec3 previousFaceNormal = glm::cross(prev,p);
        glm::vec3 nextFaceNormal = glm::cross(p, next);
        
        /* notice here we go in the same direction: previous->current,current->next;
           we could similarly go next->current,current-prev, which would flip all of our normals;
           this might not be the best idea, but its certainly better than going previous->current,next->current, which would end up being quite awful.
           This is the concept of an "orientable mesh" or "face winding order", to be googled for more information.
         */
        
        // since we want smooth normals, we'll sum the two adjacent face normals, then normalize (since usually, only the direction and not the magnitude of the normal is what matters)
        mesh.addNormal(glm::normalize(previousFaceNormal + nextFaceNormal));
        
        //add a color too
        ofColor c;
        c.setHsb(40 + 30*sin(2*theta+PI),255,255,255);        
        mesh.addColor(c);
    }
    
    //index our verts/normals/colors as a triangle fan
    for (int i=0, j = max-1; i < max; j=i++){
        mesh.addIndex(0);
        mesh.addIndex(i+1);
        mesh.addIndex(j+1);
    }
    
    // light the scene to show off why normals are important
    light.enable();
    light.setPointLight();
    light.setPosition(0,0,300);
		
}

//--------------------------------------------------------------
void ofApp::update(){
}

//--------------------------------------------------------------
void ofApp::draw(){
    ofBackgroundGradient(ofColor(65,62,50),ofColor(25,22,10) );
    
    // disable normals if a key is pressed
    if(ofGetKeyPressed() || ofGetMousePressed()){
        mesh.disableNormals();
    }else{
        mesh.enableNormals();
    }
    
    cam.begin();
    mesh.enableColors();
    mesh.drawWireframe();


    mesh.disableColors();
    ofSetColor(137,137,140);
    ofFill();

    #ifndef TARGET_OPENGLES
    glEnable(GL_POLYGON_OFFSET_LINE);
    glPolygonOffset(-1,-1);
    #endif

    ofEnableLighting();
    mesh.drawFaces();
    ofDisableLighting();

    ofSetColor(255,255,255);
    light.draw();
    
    
    // draw our normals, and show that they are perpendicular to the vector from the center to the vertex
    auto n = mesh.getNormals();
    auto v = mesh.getVertices();
    float normalLength = 50.;
    
    if(!ofGetKeyPressed()){
        ofSetColor(255,255,255,70);         
        for(unsigned int i=0; i < n.size() ;i++){
            ofDrawLine(v[i].x,v[i].y,v[i].z,
                   v[i].x+n[i].x*normalLength,v[i].y+n[i].y*normalLength,v[i].z+n[i].z*normalLength);

            ofDrawLine(.98*v[i].x,.98*v[i].y,.98*v[i].z,
                   .98*v[i].x+n[i].x*normalLength*.2,.98*v[i].y+n[i].y*normalLength*.2,.98*v[i].z+n[i].z*normalLength*.2);
            ofDrawLine(.98*v[i].x+n[i].x*normalLength*.2,.98*v[i].y+n[i].y*normalLength*.2,.98*v[i].z+n[i].z*normalLength*.2,
                   v[i].x+n[i].x*normalLength*.2,v[i].y+n[i].y*normalLength*.2,v[i].z+n[i].z*normalLength*.2);
        }
    }
               

    cam.end();

    ofSetColor(255);
    ofDrawBitmapString("press any key or mouse button to disable mesh normals", 20,20);
    ofDrawBitmapString("light", cam.worldToScreen(light.getGlobalPosition()) + glm::vec3(20,0,0));
}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){

}

//--------------------------------------------------------------
void ofApp::keyReleased(int key){

}

//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y ){

}

//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y){

}

//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y){

}

//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){

}

//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){

}

//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){ 

}
