#include "grafVizStroked.h"

grafVizStroked::grafVizStroked()
{
    reset();
    bSwap           = false;
    sclLineSlim     = .03f;
    sclLineFat      = .025f;
    strokeBaseAlpha = .95f;
    strokeBaseVal   = 1.f;
    outlineBaseVal  = .9f;;
    outlineBaseThick = 1.f;
    bUseSlimMode = true;

}

grafVizStroked::~grafVizStroked()
{
    //dtor
}

void grafVizStroked::reset()
{
    alphaWireFrame      = 0;
    alphaArrow          = 1;
    alphaStroke         = 1;
    alphaStrokeOutline  = 1;
    fadeLineWidth       = 1;

    leftPts.clear();
    rightPts.clear();

    leftPtsInv.clear();
    rightPtsInv.clear();

    distSlim.clear();
}

void grafVizStroked::draw(grafTag * PR)
{
    if( PR->pts.size() == 0 ) return;

    // draw fat lines!
    drawTimeStroked(PR);

}

void grafVizStroked::drawWireframe( grafTag * PR )
{
    glColor4f(0,0,0, .1*alphaWireFrame);

    glBegin(GL_LINE_STRIP);
    for (int i = 1; i < PR->pts.size()-1; i++)
    {
        glVertex3f( PR->pts[i].pos.x, PR->pts[i].pos.y, .51 + (1000*PR->pts[i].time) / PR->z_const );
    }
    glEnd();


}

void grafVizStroked::drawArrow( grafTag * PR )
{

        if( PR->getCurrentId() < 1 ) return;

        float time_time = (1000*PR->getCurrentTime() ) / PR->z_const;

        // scale arrow to the slow == thick line width
        float size      = sclLineSlim * ( 1-(PR->pts[ PR->getCurrentId()-1 ].dist / PR->distMax) );

        // scale line during fade?(arrow should be gone by then but leave it anyway)
        //size *= fadeLineWidth;

        // get velocity
        ofPoint vel       = PR->getVelocityForTime(PR->getTimeCounter() );
        float lengthOfVel = sqrt(vel.x * vel.x + vel.y * vel.y);
		float angle       = atan2(vel.y, vel.x);

        float pct   = getAlphaForLinePosition(PR->getCurrentId(),PR->pts.size());
        glColor4f(strokeBaseVal,strokeBaseVal,strokeBaseVal, strokeBaseAlpha*pct);

        glPushMatrix();

        glTranslatef(PR->getCurrentPoint().x, PR->getCurrentPoint().y, time_time);
		glRotatef(angle * RAD_TO_DEG, 0,0,1);
            //glScalef(1+.05*lengthOfVel,1,1);

            glBegin(GL_TRIANGLES);
                    glVertex3f(-size*.25, size*1.2, 0);
                    glVertex3f(size, 0, 0);
                    glVertex3f(-size*.25, -size*1.2, 0);
            glEnd();

            glColor4f(0,0,0, alphaArrow);
            glLineWidth(outlineBaseThick);

            glBegin(GL_LINE_STRIP);
                    glVertex3f(-size*.25, size*1.2, 0);
                    glVertex3f(size, 0, 0);
                    glVertex3f(-size*.25, -size*1.2, 0);
            glEnd();

        glPopMatrix();

}

void grafVizStroked::calculateStroke(grafTag * PR)
{
    if(leftPts.size() > 0 ) return;

    // reset swap so it always starts false
    bSwap = false;

    // clear points
    leftPts.clear();
    rightPts.clear();

    leftPtsInv.clear();
    rightPtsInv.clear();

    distSlim.clear();

    // calculate the zero point
    calculatePoint(PR->pts[0], (1000*PR->pts[0].time) / PR->z_const, 0, 0, leftPts, rightPts);
    calculatePoint(PR->pts[0], .1+((1000*PR->pts[0].time) / PR->z_const), 0, 0, leftPtsInv, rightPtsInv);

    distSlim.push_back(0);
    // calc rest
    for (int i = PR->getStartPt() + 1; i < PR->getNumPts();i++)//getCurrentId(); i++)
    {
        float dist      = sclLineFat * (PR->pts[i].dist/PR->distMax);
        float angle     = PR->pts[i].angle;
        float time_num  = (1000*PR->pts[i].time) / PR->z_const;

        float deltaA = fabs(PR->pts[i].angle - PR->pts[i-1].angle);
        if( deltaA > (HALF_PI) && TWO_PI-deltaA > HALF_PI ) bSwap = !bSwap;

        float pct  = 1;
        float diff = 1;

        if( i < 5 ) pct = (i / 4.f);
        if( i < 5 )diff = powf( pct,1.1);

        if( i > PR->pts.size()-5 ) pct = ( (PR->pts.size()-i) / 4.f);
        if( i > PR->pts.size()-5 ) diff = powf( pct,1.1);

        if( bSwap ) calculatePoint(PR->pts[i], time_num, dist*diff, angle, leftPts, rightPts);
        else        calculatePoint(PR->pts[i], time_num, dist*diff, angle, rightPts, leftPts);

        diff = 0;
        if( i > PR->pts.size()-5 ) pct = ( ( i-( PR->pts.size()-6 ) ) / 4.f );
        if( i > PR->pts.size()-5)  diff = (PR->distMax-PR->pts[i].dist)*powf( pct,1.1);

        if( i < 10 ) pct = ((i) / 9.f);
        if( i < 10 ) diff = (PR->distMax-PR->pts[i].dist)*powf( 1-pct,1.10);

        //float ptDist = ofxVec2f( (ofxVec2f)PR->pts[i].pos - (ofxVec2f)PR->pts[i-1].pos).length();
        dist = sclLineSlim - (sclLineSlim * (( (PR->pts[i].dist+diff )/PR->distMax)) );
        dist *= fadeLineWidth;
        distSlim.push_back( 1.2* ( 1 - (PR->pts[i].dist/PR->distMax) ) );

        if( bSwap ) calculatePoint(PR->pts[i], time_num+.1, dist, angle, rightPtsInv, leftPtsInv);
        else        calculatePoint(PR->pts[i], time_num+.1, dist, angle, leftPtsInv, rightPtsInv);


    }
}

float grafVizStroked::getAlphaForLinePosition( int pos, int total )
{
    float pct = 1;

    int min = 10;
    int max = total - min;

    if (pos < min ) pct = pos / (float) (min-1);
    if (pos > max ) pct = 1 - ( ( pos-max )  / (float) (min-1) );

    return pct;
}

void grafVizStroked::calculatePoint(timePt pt, float time_num, float dist, float angle, vector<ofPoint>&left, vector<ofPoint>&right)
{
    // limit min dist
    dist = MAX(dist,.002);

    float left_x,left_y,right_x,right_y;

    left_x 	= pt.pos.x-(sin(pt.angle)*dist);
    left_y 	= pt.pos.y+(cos(pt.angle)*dist);
    right_x = pt.pos.x+(sin(pt.angle)*dist);
    right_y = pt.pos.y-(cos(pt.angle)*dist);


    left.push_back( ofPoint(left_x,left_y,time_num) );
    right.push_back( ofPoint(right_x,right_y,time_num) );
}


void grafVizStroked::drawFatStroke(grafTag * PR, vector<ofPoint>&left,vector<ofPoint>&right, float grayVal, float alpha, bool bUseDrawShape, bool bUseLines)
{

    int numToDraw = PR->getCurrentId()-(PR->getStartPt()+1);

    if( numToDraw <= 0 ) return;

    GLfloat * points = new GLfloat[numToDraw * 3 * 4];
    GLfloat * color  = new GLfloat[numToDraw * 4 * 4];

    int ar  = 0;
    int cr  = 0;

    for (int i = PR->getStartPt() + 1; i < PR->getCurrentId(); i++)
    {

        if(i <= 0) continue;

        float distS = MAX(alpha, distSlim[i-1]);
        float pctb  = distS*getAlphaForLinePosition(i-1,PR->pts.size());

        // add prev point
        points[ar]   = left[i-1].x;
        points[++ar] = left[i-1].y;
        points[++ar] = (1000*PR->pts[i-1].time) / PR->z_const;

        color[cr] = grayVal*alphaStroke;
        color[++cr] = grayVal*alphaStroke;
        color[++cr] = grayVal*alphaStroke;
        color[++cr] = pctb;

        points[++ar] = right[i-1].x;
        points[++ar] = right[i-1].y;
        points[++ar] = (1000*PR->pts[i-1].time) / PR->z_const;

        color[++cr] = grayVal*alphaStroke;
        color[++cr] = grayVal*alphaStroke;
        color[++cr] = grayVal*alphaStroke;
        color[++cr] = pctb;


        distS = MAX(alpha, distSlim[i]);
        float pct  = distS*getAlphaForLinePosition(i,PR->pts.size());

        // add prev point
        color[++cr] = grayVal*alphaStroke;
        color[++cr] = grayVal*alphaStroke;
        color[++cr] = grayVal*alphaStroke;
        color[++cr] = pct;

        points[++ar] = right[i].x;
        points[++ar] = right[i].y;
        points[++ar] = (1000*PR->pts[i].time) / PR->z_const;

        color[++cr] = grayVal*alphaStroke;
        color[++cr] = grayVal*alphaStroke;
        color[++cr] = grayVal*alphaStroke;
        color[++cr] = pct;

        points[++ar]  = left[i].x;
        points[++ar] = left[i].y;
        points[++ar] = (1000*PR->pts[i].time) / PR->z_const;

        ar++;
        cr++;

    }

    /*

    */


    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);

    glVertexPointer(3, GL_FLOAT, 0, points);
    glColorPointer(4, GL_FLOAT, 0, color);

    glDrawArrays(GL_QUADS, 0, numToDraw*4);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);


    delete points;
    delete color;


}

void grafVizStroked::drawTimeStroked( grafTag * PR )
{


    //ofEnableSmoothing();
    //ofEnableAlphaBlending();

    // make line calculations
    calculateStroke(PR);

    glEnable(GL_DEPTH_TEST);
    ofFill();

    if(bUseSlimMode)
    {
        // draw line so that the slower the stroke, the fatter the line
        drawFatStroke(PR,leftPtsInv,rightPtsInv, strokeBaseVal, strokeBaseAlpha, false);
        drawFatOutline(PR,leftPtsInv,rightPtsInv);
    }else{

        drawFatStroke(PR,leftPts,rightPts, strokeBaseVal, strokeBaseAlpha, false);
        drawFatOutline(PR,leftPts,rightPts);
    }

    glDisable(GL_DEPTH_TEST);

}

void grafVizStroked::drawFatOutline(grafTag * PR, vector<ofPoint>&left,vector<ofPoint>&right)
{
    float linePoints[6];

    glLineWidth(outlineBaseThick);
    glColor4f(outlineBaseVal,outlineBaseVal,outlineBaseVal,1);

    for( int i=1; i<PR->getCurrentId(); i++)
    {
        linePoints[0] = left[i-1].x;
        linePoints[1] = left[i-1].y;
        linePoints[2] = .1+left[i-1].z;

        linePoints[3] = left[i].x;
        linePoints[4] = left[i].y;
        linePoints[5] = 1+left[i].z;

        float pct = getAlphaForLinePosition(i,PR->pts.size());
        glColor4f(outlineBaseVal,outlineBaseVal,outlineBaseVal,.9*alphaStroke*powf(pct,1.5));

        glEnableClientState(GL_VERTEX_ARRAY);
        glVertexPointer(3, GL_FLOAT, 0, &linePoints[0]);
        glDrawArrays(GL_LINES, 0, 2);

    }

    for( int i=1; i<PR->getCurrentId(); i++)
    {
        linePoints[0] = right[i-1].x;
        linePoints[1] = right[i-1].y;
        linePoints[2] = .1+right[i-1].z;

        linePoints[3] = right[i].x;
        linePoints[4] = right[i].y;
        linePoints[5] = 1+right[i].z;

        float pct = getAlphaForLinePosition(i,PR->pts.size());
        glColor4f(outlineBaseVal,outlineBaseVal,outlineBaseVal,.9*alphaStroke*powf(pct,1.5));

        glEnableClientState(GL_VERTEX_ARRAY);
        glVertexPointer(3, GL_FLOAT, 0, &linePoints[0]);
        glDrawArrays(GL_LINES, 0, 2);

    }

    /// draw outlines around slow stroke

    /*
    ofNoFill();
    glBegin(GL_LINE_STRIP);
        for( int i=0; i<left.size()-1; i++)
         {
            float pct = getAlphaForLinePosition(i,PR->pts.size());
            glColor4f(outlineBaseVal,outlineBaseVal,outlineBaseVal,.9*alphaStroke*powf(pct,1.5));
            glVertex3f(left[i].x, left[i].y, .1+left[i].z);
         }
    glEnd();



   glBegin(GL_LINE_STRIP);
        for( int i=0; i<right.size()-1; i++)
        {
            float pct = getAlphaForLinePosition(i,PR->pts.size());
            glColor4f(outlineBaseVal,outlineBaseVal,outlineBaseVal,.9*alphaStroke*powf(pct,1.5));
            glVertex3f(right[i].x, right[i].y, .1+right[i].z);
         }
    glEnd();

     int i = right.size()-1;
     glBegin(GL_LINES);
        float pct = getAlphaForLinePosition(i,PR->pts.size());
        glColor4f(outlineBaseVal,outlineBaseVal,outlineBaseVal,.9*alphaStroke*powf(pct,1.5));
        glVertex3f(right[i].x, right[i].y, .1+right[i].z);
        glVertex3f(left[i].x, left[i].y, .1+left[i].z);
     glEnd();
*/
    glLineWidth(1.f);
}


void grafVizStroked::alterParam( float val, float & param )
{
    param += val;
    param = MIN( MAX(param,0), 1 );
}

void grafVizStroked::drawTimeLine( grafTag * PR )
{
}

void grafVizStroked::averagePoints(float pct)
{
    float pctSis = (1-pct) * .5f;

    for( int i = 2; i < leftPtsInv.size(); i++)
    {
        leftPtsInv[i-1].x = (pctSis * leftPtsInv[i-2].x) + (pct * leftPtsInv[i-1].x) + (pctSis * leftPtsInv[i].x);
        leftPtsInv[i-1].y = (pctSis * leftPtsInv[i-2].y) + (pct * leftPtsInv[i-1].y) + (pctSis * leftPtsInv[i].y);
    }


    for( int i = 2; i < rightPtsInv.size(); i++)
    {
        rightPtsInv[i-1].x = (pctSis * rightPtsInv[i-2].x) + (pct * rightPtsInv[i-1].x) + (pctSis * rightPtsInv[i].x);
        rightPtsInv[i-1].y = (pctSis * rightPtsInv[i-2].y) + (pct * rightPtsInv[i-1].y) + (pctSis * rightPtsInv[i].y);
    }
}
