#include "font.h"


FT_Library library;
void init(){
    int error = FT_Init_FreeType( &library );
    if ( error ) {  }

}

bezier::bezier(){

}
bezier::bezier(std::vector<bezier::point> p){
    points = p;
}
bezier::bezier(bezier::point* p, int len){
    for( int i = 0; i < len; ++i){
        points.push_back( p[i] );
    }
}
bezier::bezier(bool usingpoints, int len, ...){
    va_list a;
    va_start( a, len );
    //printf("\n\n\n");
    for( int i = 0; i < len; ++i ){
        if( usingpoints )
            points.push_back( va_arg( a, point ) );
        else
            points.push_back( {va_arg( a, double ),va_arg( a, double )} );
        //printf("%f, %f\n", points[points.size()-1].x,points[points.size()-1].y);
    }
    //printf("\n\n\n");
    va_end(a);
}
void bezier::push( double x, double y ){
    points.push_back( {x, y} );
    //printf("%f, %f\n", points[points.size()-1].x,points[points.size()-1].y);
}
void bezier::push( bezier::point p ){
    points.push_back( p );
    //printf("%f, %f\n", points[points.size()-1].x,points[points.size()-1].y);
}
bezier::point bezier::get( double t ){
    if( points.size() < 1 ) return {0,0};
    if( points.size() < 2 ) return points[0];
    bezier a;
    for( int i = 0; i < points.size() - 1; ++i ){
        double px = points[i].x + (points[i+1].x-points[i].x) * t;
        double py = points[i].y + (points[i+1].y-points[i].y) * t;
        if( points.size() <= 2 ){ /*printf( "%f\t%f\n", px, py ); */return { px, py }; }
        a.push( px, py );
    }

    return a.get( t );
}





font::glyph::glyph(){
    penx = peny = 0;
    tmp = 0;
}
void font::glyph::finalize(){
    if( tmp != 0 ) {
        strokes.push_back( tmp );
        //delete tmp;
        tmp = 0;
    }
}
void font::glyph::moveto( double x, double y ){
    if( tmp != 0 ) {
            strokes.push_back( tmp );
            //delete tmp;
            tmp = new stroke();

    }

    penx = x; peny = y;
    //printf("%f\t%f\n", x, y );
}
void font::glyph::lineto( double x, double y ){

    if(tmp == 0) {tmp = new stroke(); tmp->points.push_back({penx, peny, 0, 0, 0}); }
    tmp->points.push_back( { x, y, 255, 0, 0 } );
    //printf("%f\t%f\n", x, y );
    penx = x; peny = y;
}
void font::glyph::conicto( double cx, double cy, double x, double y ){
    //printf("%f\t%f\t-\t%f\t%f\t-\t%f\t%f\n", penx, peny, cx, cy, x, y);

    if(tmp == 0) {tmp = new stroke(); tmp->points.push_back({penx, peny, 0, 255, 0}); }
    bezier a;//(false, 3, penx, peny, cx, cy, x, y );
    a.push( penx, peny );
    a.push( cx, cy );
    a.push( x, y );

    for( double i = 0; i < 1; i += .5){
        bezier::point p = a.get( i );
        //tmp->points.push_back( {rand()%300, rand()%300});
        tmp->points.push_back( { p.x, p.y, 0, 255, 0 } );
        //printf("%f\t%f\n", tmp->points[tmp->points.size()-1].x, tmp->points[tmp->points.size()-1].y );
        //printf("HELLO %f %f\t", p.x, p.y);
    }
    tmp->points.push_back( { x, y, 0, 255, 0 } );
    penx = x; peny = y;
    //printf("%f\t%f\n", x, y );
}
void font::glyph::cubicto( double c1x, double c1y, double c2x, double c2y, double x, double y ){

    if(tmp == 0) {tmp = new stroke(); tmp->points.push_back({penx, peny, 0, 0, 255}); }
    bezier a;//(false, 3, penx, peny, c1x, c1y, c2x, c2y, x, y );
    a.push( penx, peny );
    a.push( c1x, c1y );
    a.push( c2x, c2y );
    a.push( x, y );
//printf("CUBIC");
    for( double i = 0; i < 1; i += .5){
        bezier::point p = a.get( i );
        tmp->points.push_back( { p.x, p.y, 0, 0, 255 } );
    }
    tmp->points.push_back( { x, y, 0, 0, 255 } );
    penx = x; peny = y;
    //printf("%f\t%f\n", x, y );
}
int a = 0;
int b = 0;
int font::process_glyph_moveto( const FT_Vector*  to, void* user ){
    while( b ){}b = 1;++a; b=0;((glyph*)user)->moveto( ((double)(to->x))/pow( 2, 16 ), ((double)(to->y))/pow( 2, 16 ) );
    return 0;
}
int font::process_glyph_lineto( const FT_Vector*  to, void* user ){
    while( b ){}b = 1;++a; b=0;((glyph*)user)->lineto( ((double)(to->x))/pow( 2, 16 ), ((double)(to->y))/pow( 2, 16 ) );
    return 0;
}
int font::process_glyph_conicto( const FT_Vector*  control, const FT_Vector*  to, void* user ){
    while( b ){}b = 1;++a; b=0; ((glyph*)user)->conicto( ((double)(control->x))/pow( 2, 16 ), ((double)(control->y))/pow( 2, 16 ), ((double)(to->x))/pow( 2, 16 ), ((double)(to->y))/pow( 2, 16 ) );
    return 0;
}
int font::process_glyph_cubicto( const FT_Vector*  control1, const FT_Vector*  control2, const FT_Vector*  to, void* user ){
    while( b ){}b = 1;++a; b=0;((glyph*)user)->cubicto( ((double)(control1->x))/pow( 2, 16 ), ((double)(control1->y))/pow( 2, 16 ), ((double)(control2->x))/pow( 2, 16 ), ((double)(control2->y))/pow( 2, 16 ), ((double)(to->x))/pow( 2, 16 ), ((double)(to->y))/pow( 2, 16 ) );
    return 0;
}

font::glyph* font::process_glyph(int index){
    //glyph_index = FT_Get_Char_Index( face, charcode );
    FT_Load_Glyph( face, index,  FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT );

    FT_Outline_Funcs aa;
    aa.move_to = process_glyph_moveto;
    aa.line_to = process_glyph_lineto;
    aa.conic_to = process_glyph_conicto;
    aa.cubic_to = process_glyph_cubicto;
    aa.shift = 16;
    aa.delta = 0;

    glyph* val = new glyph();
    //if( face == 0 ){ val->rightbearing = 0;val->botbearing = 0;return val; }
    val->rightbearing = ((double)(face->glyph->advance.x));
    val->botbearing = ((double)(face->glyph->advance.y));
//printf("lol" );
    int ap = a;
    FT_Outline_Decompose( &(face->glyph->outline), &aa, (void*) val );
    //printf("\n\n%i\n", a - ap );
    val->finalize();

        /*int* contourlens = (int*)malloc(val->strokes.size()*sizeof(int));
        int tc = 0;
        for( int i = 0; i < val->strokes.size(); ++i ){ for( int k = 0; k < val->strokes[i]->points.size(); ++k ){ ++tc; } }
        tc+=2;
        double** contours = (double**)malloc(tc*sizeof(double*));
        double* contoursbuf = (double*)malloc(tc*sizeof(double)*2);
        contours+=1;
        int** tris = (int**)malloc(tc*3*sizeof(int*));
        int* trisbuf = (int*)malloc(tc*3*sizeof(int)*3);

        int pos = 0;
        int pos2 = 0;

        for( int i = 0; i < val->strokes.size(); ++i ){
            contourlens[i] = val->strokes[i]->points.size();
            for( int k = 0; k < val->strokes[i]->points.size(); ++k ){
                    contours[pos2] = contoursbuf+(pos2)*2;
                    pos2++;
                    //if( k >= 4 ) break;
                    //v.push_back( {rand()%1000,rand()%1000} );
                    contoursbuf[pos++] = val->strokes[i]->points[k].x;
                    contoursbuf[pos++] = val->strokes[i]->points[k].y;


                //v.push_back( {val->strokes[i]->points[k].x, val->strokes[i]->points[k].y} );
            }
        }
        for( int i = 0; i < tc*3; ++i )
            tris[i] = trisbuf+i*3;

        //Triangulate::Process( v, out );
        triangulate_polygon( val->strokes.size(), contourlens, contours, tris );
        polygon p;
        //printf("%i\t", out.size()/3);
        for( int k = 0; k < val->strokes.size(); k+=1 ){
            triangle t;
            t.points[0].x = tris[k*2][0];
            t.points[0].y = tris[k*2+1][0];
            t.points[1].x = tris[k*2][1];
            t.points[1].y = tris[k*2+1][1];
            t.points[2].x = tris[k*2][2];
            t.points[2].y = tris[k*2+1][2];

            //printf( "%f\t%f\n", t.points[0].x, t.points[0].y);

            p.triangles.push_back(t);
        }
        val->polys.push_back(p);
        contours -= 1;
        free( contourlens );
        free( contours );
        free( contoursbuf );
        free( tris );
        free( trisbuf );*/


    return val;
}
font::font( const char* fname ){
    int error = FT_New_Face( library, fname, 0, &face );
    if( error != 0 ) return;
    charset.resize( 256, 0 );

}
font::font( ){

}
void font::load( const char* fname ){
    int error = FT_New_Face( library, fname, 0, &face );
    if( error != 0 ) return;
    charset.resize( 256, 0 );

}
font::font( const FT_Byte* v, int len ){
    int error = FT_New_Memory_Face( library, v, len, 0, &face );
    if( error != 0 ) return;
    charset.resize( 256, 0 );

}
font::~font(){
    for( int i = 0; i < charset.size(); ++i ){
        if( charset[i] != 0 )
            delete charset[i];
    }
}
font::glyph* font::request_glyph( int sym ){
    if( charset.size() <= sym ){
        charset.resize( sym + 100, 0 );
    }
    if( charset[sym] == 0 ){

        int id = FT_Get_Char_Index( face, sym );
        //printf("%i\n", id);
        //if( )
        //printf("\n\n%c\n\n", sym );
        //if( sym == 'o' )getc(stdin);
        charset[sym] = process_glyph( id );
    }
    return charset[sym];
}
font::glyph* font::render_glyph( int sym ){
    glyph* a = request_glyph( sym );
    //printf("\n%c:\t%i\t\n\n", sym, a->strokes.size() );
    for( int i = 0; i < a->strokes.size(); ++i ){
        glBegin( GL_LINE_LOOP );
        for( int k = 0; k < a->strokes[i]->points.size(); ++k ){
            //glColor3ub(a->strokes[i]->points[k].r, a->strokes[i]->points[k].g, a->strokes[i]->points[k].b );
            glVertex2d( a->strokes[i]->points[k].x/1100., a->strokes[i]->points[k].y/1100. );
            //printf("%f\t%f\n", a->strokes[i]->points[k].x/100, a->strokes[i]->points[k].y/100);
        }
        glEnd();
    }
    /*for( int i = 0; i < a->polys.size(); ++i ){
        glBegin( GL_TRIANGLES );
        for( int k = 0; k < a->polys[i].triangles.size(); ++k ){
                //glBegin( GL_LINE_LOOP );
            //printf("hi");
            glVertex2d( a->polys[i].triangles[k].points[0].x/1100, a->polys[i].triangles[k].points[0].y/1100 );
            glVertex2d( a->polys[i].triangles[k].points[1].x/1100, a->polys[i].triangles[k].points[1].y/1100 );
            glVertex2d( a->polys[i].triangles[k].points[2].x/1100, a->polys[i].triangles[k].points[2].y/1100 );
        //glEnd();
            //printf("%f\t%f\n", a->strokes[i]->points[k].x/100, a->strokes[i]->points[k].y/100);
        }
        glEnd();
    }*/

    return a;
}
void font::render_string( const char* str, double* w ){
    double xpos = 0;
    if( w != 0 ) *w = 0;
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    for( int i = 0; str[i] != 0; ++i ){
        glTranslated( xpos, 0, 0 );
        glyph* a = render_glyph( str[i] );
        //printf("%f\n", a->rightbearing);
        xpos = a->rightbearing/1100.;
        if( w != 0 ) *w += xpos;
    }
    glPopMatrix();
}
double font::string_width( const char* str ){
    double xpos = 0;
    if( str == 0 ) return 0;
    if( str[0] == 0 ) return 0;
    for( int i = 0; str[i] != 0; ++i ){
        glyph* a = request_glyph( str[i] );
        //printf("%f\n", a->rightbearing);
        xpos += a->rightbearing/1100.;
    }
    return xpos;
}

void font::process_set( int mi, int ma ){
    for( int i = mi; i < ma; i ++ ){
        request_glyph( i );
    }
    //printf("\n\n\n%i\n\n\n", a);
}
