#include "grafVisualManager.h"

grafVisualManager::grafVisualManager()
{
    w = 320;
    h = 240;

    currentTag  = 0;
    waitTime    = 1.0f;
    waitCounter = waitTime;
    lastTime    = ofGetElapsedTimef();

    screenW     = 1024;
    screenH     = 768;

    avgRate     = .5;
    rotation    = -15;
    fadeRate    = 4;
    dt          = 0;

    mode        = MODE_NORM;
    trans_mode  = T_AVG;

    bPaused         = false;
    bDisplayInfo    = false;
    bPrintfile      = false;
    bDisplayGui     = false;
    bDrawFadeEdges  = true;

    fogStart = 370;
    fogEnd   = 970;
}

grafVisualManager::~grafVisualManager()
{
    myTags.clear();
    origTags.clear();

    delete GIO;
    delete GR;
    delete PL;
    delete VIZ_PS;
    delete VIZ_ST;
    delete TRANS_AVG;
}

void grafVisualManager::reset()
{
    myTags.clear();
    transTags.clear();
    origTags.clear();
}

void grafVisualManager::setup(int sW, int sH)
{
    screenW = sW;
    screenH = sH;

    GIO      = new grafIO();
    GR       = new grafTag();
    PL       = new ofxPocoDirectoryLister();
    VIZ_PS   = new grafParticleVecField();
    VIZ_ST   = new grafVizStroked();

    TRANS_AVG= new grafTransAverage();

    VIZ_PS->setup(screenW,screenH);

//    fbo.allocate(screenW,screenH,true);

    float FogCol[3]={0,0,0};

    glEnable(GL_FOG);
    glFogfv(GL_FOG_COLOR,FogCol);
    glFogi(GL_FOG_MODE, GL_LINEAR);
    glFogf(GL_FOG_DENSITY, 0.15f);

}


void grafVisualManager::loadTags()
{
    dirs.clear();

    // find all folders in directory "tags"
    PL->setPath( ofToDataPath("tags/",true) );
    PL->findSubDirectories(dirs);

    // check that the folders have tag file
    for( int i = dirs.size()-1; i >= 0; i--)
    {
         PL->setPath( ofToDataPath("tags/"+dirs[i]+"/",true) );
         PL->setExtensionToLookFor("graf");
         int num = PL->getNumberOfFiles();
         if( num <= 0 ) dirs.erase( dirs.begin()+i);
    }

    if( dirs.size() <= 0 ) return;

    // sort alphabetically
    std::sort(dirs.begin(),dirs.end());

    // clear and load each tag
    reset();

    grafTag tempRec;

    for( int i = 0; i < dirs.size(); i++)
    {

        // store the original data
        origTags.push_back( tempRec );

        cout << dirs[i] << endl;
        GIO->loadGrafTag(dirs[i], &origTags[i]);
    }


    for( int i = 0; i < origTags.size(); i++)
    {

        // set up smoothed tags
        myTags.push_back( tempRec );

        smoother.smoothTag(3,&origTags[i],&myTags[i]);
        myTags[i].calcMinMax();
        myTags[i].averagePts();
        myTags[i].averagePts();


        // set up tags for rendering (can be altered with transition)
        transTags.push_back( myTags[i] );
    }

}


void grafVisualManager::saveCurrent()
{
    GIO->saveGrafTag( dirs[currentTag], &origTags[currentTag] );
}

void grafVisualManager::saveScreen()
{
    GIO->saveScreenShot(dirs[currentTag], &origTags[currentTag]);
}

static float rvel = 0.f;
static float counterR = 0.f;

void grafVisualManager::grafVisualManager::update()
{

    // if no tags don't do anything
    if(myTags.size() < currentTag ) return;
    if( bPaused )
    {
        lastTime   = ofGetElapsedTimef();
        return;
    }

    // calculate time difference
    dt  = ofGetElapsedTimef()-lastTime;

    // pointer to tag we are dealing with
    grafTag * tag = &transTags[currentTag];

    // don't continue if there are no points
    if(tag->pts.size() == 0) return;


    // for particle upating after states are checked
    bool bUpdateParticles = false;

    // if tag finished drawing, pause
    if( tag->bReset )
    {
            // if pause is done, start transition mode
            if( waitCounter <= 0 )  mode = MODE_TRANS;
            else{
                // if waiting, decrease wait time, cont update particle system, remove arrow
                waitCounter = (waitCounter-dt < 0) ? 0 : waitCounter-dt;
                bUpdateParticles = true;
            }
    }else{
            // update tag and particles if we are animating
            transTags[currentTag].update();
            if(tag->getCurrentId() > 1 && mode != MODE_TRANS) bUpdateParticles = true;
    }


    if(bUpdateParticles)
       VIZ_PS->update(tag,tag->getCurrentPoint(),tag->getVelocityForTime( tag->getCurrentTime() ), dt);


    // if transition is dones, skip to next
    if( mode == MODE_TRANS && updateTransition(tag) ) nextTag();


    // calculate timing on rotation and update
    updateRotation(tag);



    lastTime   = ofGetElapsedTimef();

}

void grafVisualManager::updateRotation(grafTag * tag)
{
    float totalTime = tag->getDuration() + waitTime;
    float nowTime   = (waitTime-waitCounter) + tag->getCurrentTime();
    float pctDone   = nowTime/totalTime;

    if( mode == MODE_NORM )
    {
        rotation = -25 * powf(1-(pctDone),1.15);
        rvel = rotation;
        counterR = 0;
    }else{
         rotation = 25 * powf(counterR,1.15) + rvel;
         counterR += dt*.5;
    }
}

bool grafVisualManager::updateTransition(grafTag * tag)
{
    float ddt = dt*(1/16.f);

    bool bDoneWithTag = false;

    // could add more cases for other transition modes
    switch( trans_mode )
    {
        case T_AVG:


            //TRANS_AVG->averagePoints(tag,avgRate*.5);

            VIZ_ST->averagePoints(avgRate*.5);
            VIZ_ST->alterParam( -(.75*fadeRate) * ddt, VIZ_ST->alphaStroke);
            //VIZ_ST->alterParam( -(1.5*fadeRate) * ddt, VIZ_ST->alphaWireFrame);
            //VIZ_ST->alterParam( -(1.75*fadeRate) * ddt, VIZ_ST->fadeLineWidth);
            //VIZ_ST->alphaArrow       = 0;

            if( VIZ_ST->alphaStroke < 1 ){
                //VIZ_PS->retractParticles( ddt, screenW*((tag->max.x-tag->min.x) / 2),screenW*((tag->max.y-tag->min.y) / 2) );
                VIZ_ST->alterParam( -(.75*fadeRate) * ddt, VIZ_PS->alpha);
                if( VIZ_PS->alpha <= 0 ) bDoneWithTag =  true;
            }


            fogBlend += 1200*ddt;

            //
            break;
    }

    return bDoneWithTag;
}

void grafVisualManager::draw()
{

    // no tags, no draw
    if(myTags.size() == 0 ) return;


    glFogf(GL_FOG_START, fogStart - fogBlend );
    glFogf(GL_FOG_END, fogEnd - fogBlend );
    glEnable(GL_FOG);

    // pointer to the current tag
    grafTag * tag = &transTags[currentTag];


    glPushMatrix();

        glPushMatrix();

            // center screen
            glTranslatef(screenW/2, screenH/2, 0);

            // scale drawing
            glScalef(myTags[currentTag].position.z,myTags[currentTag].position.z,myTags[currentTag].position.z);

            // rotate
            glRotatef(myTags[currentTag].rotation.x+rotation,0,1,0);
            glRotatef(myTags[currentTag].rotation.y,1,0,0);
            glRotatef(myTags[currentTag].rotation.z,0,0,1);

            // translate to draw offset
            glTranslatef(myTags[currentTag].position.x,myTags[currentTag].position.y,0);

            // drawparticle field
            glDisable(GL_DEPTH_TEST);
            VIZ_PS->draw(tag, screenW, screenH);

             // draw stroke
            glPushMatrix();

                glTranslatef(-screenW/2, -screenH/2, 0);
                glScalef(screenW,screenW,1);
                VIZ_ST->draw( tag );

            glPopMatrix();


        glPopMatrix();

         glDisable(GL_FOG);
         //if(bDrawFadeEdges) drawEdgeFade();
         if(bDisplayGui) displayGui();
         if(bDisplayInfo) displayInfo();


    glPopMatrix();


}

void grafVisualManager::drawEdgeFade()
{
        float len = 50;
        int iLen = len+1;

        // left fade
        glBegin( GL_QUAD_STRIP);
        for( int i = 0; i < iLen; i+=10 )
        {

            float pct = 1 - (i / len);
            glColor4f(0,0,0, pct);
            glVertex2f(i,0);
            glVertex2f(i,screenH);
        }

        for( int i = screenW-iLen; i < screenW+10; i+=10 )
        {

            float pct = 1- ((screenW-i) / len);
            glColor4f(0,0,0, pct);
            glVertex2f(i,0);
            glVertex2f(i,screenH);
        }

        glEnd();

        // right fade
}

void grafVisualManager::displayInfo()
{
    ofSetColor(255,255,255);
    ofDrawBitmapString( dirs[currentTag], 30,20);
    ofDrawBitmapString( "p: pause  |  s: save  |  i: screen shot  |  h: toggle panel  |  f: toggle fps  |  arrows: cycle tags  |  w: write eps", 30,ofGetHeight()-40);
    ofDrawBitmapString( "mouse:  left: moves tag  right: zooms tag  shift-left: rotate tag", 30,ofGetHeight()-20);

}


void grafVisualManager::adjustTagPos( int x, int y, int z )
{
     if(myTags.size() < currentTag ) return;

     myTags[currentTag].position.x += x;
     myTags[currentTag].position.y += y;
     myTags[currentTag].position.z += z*.01f;

     origTags[currentTag].position.x += x;
     origTags[currentTag].position.y += y;
     origTags[currentTag].position.z += z*.01f;
}

void grafVisualManager::adjustTagRot( int x, int y )
{
    if(myTags.size() < currentTag ) return;

     myTags[currentTag].rotation.x += x;
     myTags[currentTag].rotation.y += y;

     origTags[currentTag].rotation.x += x;
     origTags[currentTag].rotation.y += y;
}

void grafVisualManager::resetRotation(){
    if(myTags.size() < currentTag ) return;
    myTags[currentTag].rotation.y = 0;//set(0,0,0);
    origTags[currentTag].rotation.y = 0;//set(0,0,0);
}

void grafVisualManager::nextTag()
{
    //VIZ_PS->reset();

    myTags[currentTag].bReset    = false;
    transTags[currentTag].bReset = false;
    waitCounter                  = waitTime;

    currentTag++;
    currentTag = currentTag % (int)myTags.size();

    resetTag();
    mode = MODE_NORM;
}

void grafVisualManager::prevTag()
{
    if(currentTag<=0 ) return;

    //VIZ_PS->reset();

    myTags[currentTag].bReset    = false;
    transTags[currentTag].bReset = false;
    waitCounter = waitTime;

    currentTag--;
    currentTag = currentTag % (int)myTags.size();

    resetTag();
    mode = MODE_NORM;
}

void grafVisualManager::resetTag()
{

    // reset tag to start point
    myTags[currentTag].resetToStart();

    // copy over original (because would have been altered with averaging in prev play)
    transTags[currentTag] = myTags[currentTag];

    // reset particles and stroke drawer
    VIZ_PS->reset();
    VIZ_ST->reset();

    // reset rotation
    rotation    = -15;
    rvel        = 0.f;
    fogBlend    = 0;

}
/*
void grafVisualManager::loadConfig(string filename)
{
    ofxXmlSettings  xml;

    xml.loadFile(filename);

    //waitTime    = xml.getValue("waitTime",3);
    //avgRate     = xml.getValue("avgRate",.5);
    //fadeRate    = xml.getValue("fadeRate",4);

    //VIZ_ST->strokeBaseAlpha = xml.getValue("drawing:strokeAlpha", VIZ_ST->strokeBaseAlpha );
    //VIZ_ST->strokeBaseVal   = xml.getValue("drawing:strokeValue", VIZ_ST->strokeBaseVal );

    waitCounter = waitTime;

    cout << "Load Config Settings" << endl;
}

void grafVisualManager::saveConfig(string filename)
{
    ofxXmlSettings  xml;

    xml.setValue("waitTime",waitTime);
    xml.setValue("avgRate",avgRate);
    xml.setValue("fadeRate",fadeRate);

    // drawing params
    //xml.setValue("drawing:strokeAlpha", VIZ_ST->strokeBaseAlpha );
    //xml.setValue("drawing:strokeValue", VIZ_ST->strokeBaseVal );

    cout << "Saved Config Settings" << endl;

    xml.saveFile(filename);
}*/
void grafVisualManager::writefile()
{

  bDisplayGui   = false;
  bDisplayInfo  = false;

  int opt       = GL2PS_DRAW_BACKGROUND;
  int format    = GL2PS_EPS;
  int sort      = GL2PS_SIMPLE_SORT;
  int nbcol     = 0;

  char *extension = "eps";

  FILE *fp;
  char file[256];

  int state = GL2PS_OVERFLOW, buffsize = 0;
  GLint viewport[4];

  strcpy(file,dirs[currentTag].c_str());
  strcat(file, ".");
  strcat(file, extension);

  int window_w, window_h;
  window_w = ofGetWidth();
  window_h = ofGetHeight();

  viewport[0] = 0;
  viewport[1] = 0;
  viewport[2] = window_w;
  viewport[3] = window_h;

  fp = fopen(file, "wb");

  if(!fp){
    printf("Unable to open file %s for writing\n", file);
    std::exit(1);
  }

  printf("Saving image to file %s... ", file);
  fflush(stdout);

  while(state == GL2PS_OVERFLOW){
    buffsize += 1024*1024;
    gl2psBeginPage(file, "test", viewport, format, sort, opt,
                   GL_RGBA, 0, NULL, nbcol, nbcol, nbcol,
                   buffsize, fp, file);
    draw();
    state = gl2psEndPage();
  }

  fclose(fp);

  printf("Done!\n");
  fflush(stdout);
}


void grafVisualManager::setupGuiControls()
{

    int sW  = 200;      // slider w and h
    int sH  = 15;
    int bW  = 15;       // button w and h
    int bH  = 15;
    int x   = 30;       // start x and y pos
    int y   = 100;
    int skip = 35;      // spacing



    gui.addSlider("Stroke Alpha", VIZ_ST->strokeBaseAlpha, 0, 1, x,y, sW, sH);
    gui.addSlider( "Stroke Value", VIZ_ST->strokeBaseVal, 0, 1, x,y+=skip, sW, sH );
    gui.addSlider( "Outline Value", VIZ_ST->outlineBaseVal, 0, 1, x,y+=skip, sW, sH );
    gui.addSlider( "Outline Weight", VIZ_ST->outlineBaseThick, 0, 10, x,y+=skip, sW, sH );
    gui.addSlider( "Scale Slim", VIZ_ST->sclLineSlim, 0, .2, x,y+=skip, sW, sH );
    gui.addSlider( "Scale Fat", VIZ_ST->sclLineFat, 0, .2, x,y+=skip, sW, sH );

    // transitions
    gui.addSlider( "Pause Time", waitTime, 0.f, 10.f, x,y+=(skip+10), sW, sH );
    gui.addSlider( "Average Rate", avgRate, 0.f, 1.f, x,y+=skip, sW, sH );
    gui.addSlider( "Fade Rate", fadeRate, 0.f, 100.f, x,y+=skip, sW, sH );


    // particles
    gui.addSlider( "Particle Damping", VIZ_PS->particle_damping, 0, 1, x,y+=(skip+10), sW, sH );
    gui.addSlider( "Particle Size", VIZ_PS->particle_size, 0, 10, x,y+=skip, sW, sH );
    gui.addSlider( "Particle Alpha", VIZ_PS->particle_alpha, 0, 1, x,y+=skip, sW, sH );

    // fog
    gui.addSlider( "Fog Start", fogStart, 0, 1000, x,y+=skip, sW, sH );
    gui.addSlider( "Fog End", fogEnd, 0, 2000, x,y+=skip, sW, sH );

    gui.addButton( "Stroke Mode", x,y+=skip,bW,bH, true);

    gui.loadDataFromXML();
    updateGuiDrag(0,0);
    updateGuiPress(0,0);

    // transition mode
    // gravity speed / mode

    // particles
    // num xtras

}

void grafVisualManager::updateGuiDrag( int mx, int my )
{
    gui.updateDrag(mx,my);

    VIZ_ST->strokeBaseAlpha     = gui.getFloatVal("Stroke Alpha");
    VIZ_ST->strokeBaseVal       = gui.getFloatVal("Stroke Value");
    VIZ_ST->outlineBaseThick    = gui.getFloatVal("Outline Weight");
    VIZ_ST->outlineBaseVal      = gui.getFloatVal("Outline Value");

    VIZ_ST->sclLineSlim         = gui.getFloatVal( "Scale Slim" );
    VIZ_ST->sclLineFat         = gui.getFloatVal( "Scale Fat" );

    waitTime = gui.getFloatVal("Pause Time");
    avgRate  = gui.getFloatVal("Average Rate");
    fadeRate  = gui.getFloatVal("Fade Rate");

    VIZ_PS->setDamping( gui.getFloatVal("Particle Damping") );
    VIZ_PS->setParticleSize( gui.getFloatVal("Particle Size") );
    VIZ_PS->particle_alpha = gui.getFloatVal("Particle Alpha");

    fogEnd = gui.getFloatVal("Fog End");
    fogStart = gui.getFloatVal("Fog Start");

    //saveConfig("settings/visual_config.xml");
}

void  grafVisualManager::updateGuiPress( int mx, int my )
{
    gui.updatePress(mx,my);

    VIZ_ST->bUseSlimMode = gui.getButtonVal("Stroke Mode");
}

void grafVisualManager::displayGui()
{
   ofFill();
   ofSetColor(0,0,0,200);
   ofRect(0,0,300,ofGetHeight() );

   gui.draw(); //slider_strokeAlpha.draw();
   // slider_strokeVal.draw();
}


