/***************************************************************************
 *   Copyright (C) 2004 by Juergen Thies                                   *
 *   layout@juergenthies.de                                                *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "textrender.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include "general/setup.h"
#include <QFile>
#include <QResource>

QStringList textRender::internalFonts;

int textRender::fontNum=0;

textRender::textRender(){
		currentFontNum=-1;
		libraryInitDone=false;
//printf("constru\n");
}
textRender::~textRender(){
	if (libraryInitDone){
		//printf("start delete\n");
		FT_Done_Face(face);
		FT_Done_FreeType(library);
		//printf("ende text Render\n");
	}
}

void textRender::reset(){
 fontNum++;
 textRender::internalFonts.clear();
 textRender::internalFonts<<":/font0"<<":/font1"<<":/font2";
}

bool textRender::available(){
	if (currentFontNum==fontNum) return true;
    initFace();
    if (currentFontNum!=fontNum) return false;
	return true;
}

bool textRender::libraryInit(){
	//printf("libinit\n");
	if (!libraryInitDone) {
				//FT_Done_FreeType(library);
				int error = FT_Init_FreeType( &library );
					if ( error ) {
					 return false;
					 }
				libraryInitDone=true;
	}
	return true;
}

bool textRender::initFace(){
	//printf("faceinit %d %d\n",currentFontNum,fontNum);
	if (currentFontNum>=0) FT_Done_Face(face);
	//printf("faceinit1 %d\n",currentFontNum);
	if (!libraryInitDone) libraryInit();
	//printf("faceinit2 %d (%s)\n",internalFonts.size(),setup::font.toAscii().data());
	//FT_Done_Face(face);
	bool initDone=false;
	int error=1;
	if (setup::font.length()>1)
	    error = FT_New_Face( library, setup::font.toLatin1(), 0, &face );
	if (error) {
			QString source=internalFonts.at(0);
			//printf("try internal font (%s)\n",source.toAscii().data());
			int fontPos=setup::font.toInt();
			if ((fontPos>0)&&(fontPos<internalFonts.size()))source=internalFonts.at(fontPos);
			QResource r(source);
			//QFile f(":/font");
			//f.open(QIODevice::ReadOnly);
			//QDataStream ds(&f);
			//char a[130000];
			FT_Byte *b;
			const uchar *c;
			c=reinterpret_cast<const uchar *>(r.data());
			b=(FT_Byte *)c;
			//b=(FT_Byte *)&a;
			//c=(char *)&a;
			//int readed=ds.readRawData(c,120000);
			int readed=r.size();
			error = FT_New_Memory_Face( library,
                              b,    /* first byte in memory */
                              readed,      /* size in bytes        */
                              0,         /* face_index           */
                              &face );
			//f.close();
	}
    if ( error == FT_Err_Unknown_File_Format ) {
        return initDone;
    } else if ( error ) {
        return initDone;
    }
    initDone=true;
	currentFontNum=fontNum;
	//printf("faceinit3 %d\n",currentFontNum);
    return initDone;
}

QList<pointArray> textRender::renderText(QString name, int width, int presentation, QPoint point, strans trans ){
	if (currentFontNum!=fontNum) initFace();
    QList<pointArray> list;
    if (currentFontNum!=fontNum) return list;
    QPoint offset=QPoint(0,0);
    double gr=(1.09*(double)width/face->ascender*trans.mag);
    int fontheight=(int)(gr*(int)face->ascender);  //units_per_EM;
    if (fontheight<20) return list;

    //printf("font height %d asc %d max %d desc %d \n",face->height,face->ascender,face->max_advance_height,face->descender);
    for (int anz=0;anz<name.length();anz++) {
        int glyph_index = FT_Get_Char_Index( face,  name.at(anz).unicode() );
        int error;
	error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE|FT_LOAD_NO_BITMAP );
        if (face->glyph->format==FT_GLYPH_FORMAT_OUTLINE) {
            //printf("anz %d\n",anz);
           // printf("%d\n",face->glyph->outline.n_points);
            //printf("%d\n",face->glyph->outline.n_contours);
            pointArray array[100];
            int lastend=0;
            if (face->glyph->outline.n_contours!=0){
            for (int j=0;j<face->glyph->outline.n_contours;j++) {
                array[j].resize(0);
                for (int i=lastend;i<=face->glyph->outline.contours[j];i++) {
                    if (face->glyph->outline.tags[i]==FT_Curve_Tag_On) {
                        FT_Vector *v=&face->glyph->outline.points[i];
                        array[j].resize(array[j].size()+1);
                        array[j].setPoint(array[j].size()-1,(int)(v->x*gr),(int)(v->y*gr));
                    } else if (face->glyph->outline.tags[i+1]==FT_Curve_Tag_Cubic) {
                        FT_Vector *v0=&face->glyph->outline.points[i-1];
                        FT_Vector *v1=&face->glyph->outline.points[i];
                        FT_Vector *v2=&face->glyph->outline.points[i+1];
                        FT_Vector *v3=&face->glyph->outline.points[i+2];
                        if ((i+2)>face->glyph->outline.contours[j]) {
                            v3=&face->glyph->outline.points[lastend];
                        }
                        bezier3((double)(v0->x*gr),(double)(v0->y*gr),
                                (double)(v1->x*gr),(double)(v1->y*gr),
                                (double)(v2->x*gr),(double)(v2->y*gr),
                                (double)(v3->x*gr),(double)(v3->y*gr),
                                0,&array[j],setup::fontIteration);
                        i++;
                    } else  if(face->glyph->outline.tags[i]==FT_Curve_Tag_Conic) {
                        FT_Vector *v1=&face->glyph->outline.points[i-1];
                        FT_Vector *v2=&face->glyph->outline.points[i];
                        FT_Vector *v3=&face->glyph->outline.points[i+1];
                        if ((i+1)>face->glyph->outline.contours[j]) {
                            v3=&face->glyph->outline.points[lastend];
                        }
                        double v1x,v2x,v3x,v1y,v2y,v3y;
                        if (face->glyph->outline.tags[i-1]==FT_Curve_Tag_On) {
                            v1x=(double)v1->x*gr;
                            v1y=(double)v1->y*gr;
                        } else {
                            v1x=(double)((v1->x)+(v2->x))/2.0*gr;
                            v1y=(double)((v1->y)+(v2->y))/2.0*gr;
                        }
                        if ((face->glyph->outline.tags[i+1]==FT_Curve_Tag_On)||(((i+1)>face->glyph->outline.contours[j]))) {
                            v3x=(double)v3->x*gr;
                            v3y=(double)v3->y*gr;
                        } else {
                            v3x=(double)((v3->x)+(v2->x))/2.0*gr;
                            v3y=(double)((v3->y)+(v2->y))/2.0*gr;
                        }
                        v2x=(double)v2->x*gr;
                        v2y=(double)v2->y*gr;
                        bezier2(v1x,v1y,v2x,v2y,v3x,v3y,0,&array[j],setup::fontIteration);

                    }
                    //printf("1a: %d %d\n",array[j].size()-1,j);
                    // printf("p %d %d\n",array[j].point(array[j].size()-1).x(),array[j].point(array[j].size()-1).y());
                }
                lastend=face->glyph->outline.contours[j]+1;
                //printf("lastend %d size %d\n",lastend,array[j].size());

            }
            array[0].resize(array[0].size()+1);
            array[0].setPoint(array[0].size()-1,array[0].point(0));
	    array[0].cleanSimple();
	    int firstOfChar=list.size();
	    list<<array[0];
//	    elementlist *help=elist;
//	    elementlist *l=new elementlist();
//           l->this_element=new polygon(array[0],layer_nr);
//            l->next_element=elist;
//            elist=l;
	    //pass 1
	    //printf("testToPolygon %d pointarray of %s\n",face->glyph->outline.n_contours,name.mid(anz,1).toLatin1().data());
            for (int j=1;j<face->glyph->outline.n_contours;j++) {
	       array[j].resize(array[j].size()+1);
               array[j].setPoint(array[j].size()-1,array[j].point(0));
	       array[j].cleanSimple();
	       bool b=false;
	       b=list[firstOfChar].add(&array[j]);
	       if (b) {array[j].resize(0);}
	    }
	    //pass 2
            for (int j=1;j<face->glyph->outline.n_contours;j++) 
	       if (array[j].size()>0){
	       bool b=false;
	       int listCount=firstOfChar;
	       while (b==false&&(listCount!=list.size())){
	       		b=list[listCount].add(&array[j]);
			listCount++;
			}
	       if (b==false) {
			list<<array[j];
			}
	    	}
	    int listCount=firstOfChar;
	    while (listCount!=list.size()){
	       		list[listCount].move(offset);
			listCount++;
			}
        }
        int x=offset.x();
        int y=offset.y();
        x+=(int)((face->glyph->advance.x)*gr);
        y+=(int)((face->glyph->advance.y)*gr);
        offset=QPoint(x,y);
    }
    else {
        fprintf(stderr,"Glyph is not in outline format. Cannot dump.\n");
        return list;
    }
    }
    QPoint off=QPoint(0,0);
    switch (presentation&0x0003) {
        case 1:
            off.setX(-offset.x()/2);
            break;
        case 2:
            off.setX(-offset.x());
            break;
        }
    switch (presentation&0x000C) {
            case 0:
                off.setY(-fontheight);
                break;
            case 4:
                off.setY(-fontheight/2);
                break;
            }
    strans m;
    m.reset();
    m.translate(point.x(),point.y());    
    if (trans.mirror_x){m.toggleMirror_x();} 
    m.rotate(trans.angle);
    m.translate(off.x(),off.y());
    for (int listCount=0;listCount<list.size();listCount++){
	list[listCount].map(m);
    }
    return list;
}



void textRender::bezier2(                            // Zeichne Bezierkurve 2. Grades
    double px0, double py0,    // anhand des Stuetzpunktes p0
    double px1, double py1,    // anhand des Stuetzpunktes p1
    double px2, double py2,    // anhand des Stuetzpunktes p2
    int depth,                 // aktuelle Rekursionstiefe
    pointArray *a,	       //punktarray
    int maxDepth)		
{
    double qx01,qy01,qx12,qy12,qx012,qy012;

    if (depth > maxDepth)   {                    // Iterationstiefe erreicht
        a->resize(a->size()+1);
        a->setPoint(a->size()-1,(int)px0,(int)py0);
    } else {
        depth++;
        qx01   = (px0+px1)/2;
        qy01   = (py0+py1)/2;
        qx12   = (px1+px2)/2;
        qy12   = (py1+py2)/2;
        qx012  = (qx01+qx12)/2;
        qy012  = (qy01+qy12)/2;
        bezier2 (px0,py0,qx01,qy01,qx012,qy012,depth,a,maxDepth);
        bezier2 (qx012,qy012,qx12,qy12,px2,py2,depth,a,maxDepth);
    }
}

void textRender::bezier3(                            // Zeichne Bezierkurve 3. Grades
    double px0, double py0,    // anhand des Stuetzpunktes p0
    double px1, double py1,    // anhand des Stuetzpunktes p1
    double px2, double py2,    // anhand des Stuetzpunktes p2
    double px3, double py3,    // anhand des Stuetzpunktes p3
    int depth,                 // aktuelle Rekursionstiefe
    pointArray *a,		//punktarray
    int maxDepth)
{
    double qx01,qy01,qx12,qy12,qx23,qy23,qx012,qy012,qx123,qy123,qx0123,qy0123;
    if (depth > maxDepth)   {                    // Iterationstiefe erreicht
        a->resize(a->size()+1);
        a->setPoint(a->size()-1,(int)px0,(int)py0);
    } else {
        depth++;
        qx01   = (px0+px1)/2;
        qy01   = (py0+py1)/2;
        qx12   = (px1+px2)/2;
        qy12   = (py1+py2)/2;
        qx23   = (px2+px3)/2;
        qy23   = (py2+py3)/2;
        qx012  = (qx01+qx12)/2;
        qy012  = (qy01+qy12)/2;
        qx123  = (qx12+qx23)/2;
        qy123  = (qy12+qy23)/2;
        qx0123 = (qx012+qx123)/2;
        qy0123 = (qy012+qy123)/2;
        bezier3 (px0,py0,qx01,qy01,qx012,qy012,qx0123,qy0123,depth,a,maxDepth);
        bezier3 (qx0123,qy0123,qx123,qy123,qx23,qy23,px3,py3,depth,a,maxDepth);
    }
}
