/***************************************************************************
 *   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.             *
 ***************************************************************************/
#define _USE_MATH_DEFINES
#include "text.h"
#include <qpixmap.h>
#include <qbrush.h>
#include <qimage.h>
#include <qcolor.h>
#include <qwidget.h>
#include <qfont.h>
//Added by qt3to4:
#include "pointarray.h"
#include <QTextStream>
#include "textproperties.h"
#include "elementlist.h"
#include "path.h"
#include "polygon.h"
#include <ft2build.h>
//#include <freetype2/freetype/config/ftheader.h>
#include FT_FREETYPE_H
#include "general/setup.h"
#include "fileformats/dxf.h"
#include "fileformats/gds.h"
#include "fileformats/oasis.h"
#include "fileformats/cif.h"
#include "fileformats/svg.h"
#include "fileformats/gerber.h"
#include "general/drawingfield.h"

#include <math.h>

textRender txt::render;

txt::txt()
        : element() {
    trans.reset();
}

txt::txt(int l,QPoint p,QString s)
        : element() {
    name=s;
    point=p;
    layerNum=l;
    trans.reset();
    presentation=0;
    width=setup::defaultTextWidth;
}


txt::~txt() {}


void txt::paint(layoutImagePainter *e) {
//printf("text begin\n");
    if (layers::num[layerNum].visible) {
	if (e->available()) {
		int w=width; //höhe in drawingunits
		if (w==0) w=(int)((double)(setup::defaultTextWidth));
		if (w==0) {
			if (e->detailLevel>3) return;
			e->drawPixel(point,layers::layerColor[layerNum]);
			//printf("e1\n");
			return;
		}
		int wp=width;//höhe in pixel
		//int wd;
		if (w<0) {
			wp=abs(w);
			w=(int)((double)(-w)/e->drawTrans.mag/trans.mag);
			//wd=(int)((double)(w));
		}
		else    {
			wp=(int)(e->drawTrans.mag*(w)*trans.mag);
			//wd=(int)((double)(w));
			}
		//int lp=(int)((float)(name.length())*wp*0.7);
		int ld=(int)((double)(name.length())*w*0.7);
		if (wp<4) { //als Linie zeichnen
				if (e->detailLevel>3) return;
				pointArray pa(2);
				 switch (presentation&0x0003) {
        			case 1:
            				pa.setPoint(0,-ld/2,0); 
					pa.setPoint(1,+ld/2,0); 
           			 	break;
        			case 2:
 					pa.setPoint(0,-ld,0); 
					pa.setPoint(1,0,0); 
           			 	break;
				default:
					pa.setPoint(0,0,0); 
					pa.setPoint(1,ld,0); 
            			break;
        			}
				pa.setPoint(0,trans.mapIn(pa.point(0))+point);
				pa.setPoint(1,trans.mapIn(pa.point(1))+point);
				e->drawPolygon(&pa,layers::layerColor[layerNum]);
				//printf("e2\n");
				return;
		}
		//bounding box bestimmen
		QPoint p1,p2,p3,p4;
		switch (presentation&0x0003) {
        		case 1:
				p1.setX(-ld/2);
				p2.setX(-ld/2);
				p3.setX(ld/2);
				p4.setX(ld/2);
            			break;
        		case 2:
				p1.setX(-ld);
				p2.setX(-ld);
				p3.setX(0);
				p4.setX(0);
            			break;
        		default:
				p1.setX(0);
				p2.setX(0);
				p3.setX(ld);
				p4.setX(ld);
			break;
			}
            	switch (presentation&0x000C) {
            		case 8:
				p1.setY((w));
				p2.setY(0);
				p3.setY((w));
				p4.setY(0);
                		break;
            		case 4:
				p1.setY((w/2));
				p2.setY(-(w/2));
				p3.setY((w/2));
				p4.setY(-(w/2));
				break;
			default:
				p1.setY(0);
				p2.setY(-(w));
				p3.setY(0);
				p4.setY(-(w));
                	break;
            		}
		p1=trans.mapIn(p1)+point;
		p2=trans.mapIn(p2)+point;
		p3=trans.mapIn(p3)+point;
		p4=trans.mapIn(p4)+point;
		/*pointArray pa(5);
		pa.setPoint(0,p1);
		pa.setPoint(1,p2);
		pa.setPoint(2,p4);
		pa.setPoint(3,p3);
		pa.setPoint(4,p1);
		e->drawPolygon(&pa,layers::num[layerNum].pen.color().rgb());*/
		
		if ((!e->visibleBox( p1,p3))&&(!e->visibleBox( p1,p4))
			&&(!e->visibleBox( p2,p3))&&(!e->visibleBox( p2,p4))) {
							//printf("e3\n");
							return;
							}
		//printf("%d %d %d\n",wp,width,w);
		if (*e->paintAbort) return;
		if ((wp<20)||(w<20)) {
				//printf("endText1-\n");
				e->drawText(name,wp,p2,trans,layers::layerColor[layerNum]);
				//printf("endText1\n");
				//printf("e4\n");
				return;
			}
		QList<pointArray> l=e->renderText(name,w,presentation,point,trans);
		if (l.size()==0) e->drawPixel(point,layers::layerColor[layerNum]);
		else
			for (int listCount=0;listCount<l.size();listCount++){
				e->drawPolygon(&l[listCount],layers::layerColor[layerNum],layers::num[layerNum].brushStyle);
		}
	}

 	else e->drawPixel(point,layers::layerColor[layerNum]);
        }
   //printf("endText\n");
}

void txt::paintSelect(layoutImagePainter *e) {
    if (layers::num[layerNum].visible) 
      if (select) {
	e->drawPoint(point,setup::selectColor.rgb());
      }
}



void txt::paintSelected(layoutImagePainter *e) {
 if (layers::num[layerNum].visible) 
	e->drawPoint(point,setup::selectColor.rgb());
}


void txt::paintNode(int node, layoutImagePainter *e){
	if (layers::num[layerNum].visible)
	if (getNode()==node) paintSelected(e);
}

void txt::paintHighlighted(QPainter *po,strans trans_ ){
   QPoint p=convert(point,trans_);
   po->setPen(setup::highlightColor);
   QMatrix m=po->matrix();
   QMatrix n(1,0,0,1,0,0);
   po->setMatrix(n);
   int x=p.x();
   int y=p.y();
   po->drawLine(x-3,y-3,x+3,y+3);
   po->drawLine(x+3,y-3,x-3,y+3);
   po->setMatrix(m);

}
void txt::paintHighlighted(QPainter *po,strans trans ,QPoint p_ ) {
 if (p_==point) {paintHighlighted(po,trans);}
}

void txt::paintInfo(QPoint *min,QPoint *max,uint *count)const{
   (*count)++;
        if (point.x()<min->x()) {
            min->setX(point.x());
        }
        if (point.y()<min->y()) {
            min->setY(point.y());
        }
        if (point.x()>max->x()) {
            max->setX(point.x());
        }
        if (point.y()>max->y()) {
            max->setY(point.y());
        }
}

void txt::fSelect(QRect select_) {
    if (layers::num[layerNum].visible) {
        if (pointInRect(point,select_))
            select=true;
    }
}

void txt::fAllSelect(QRect select_) {
    if (layers::num[layerNum].visible) {
        if (pointInRect(point,select_))
            select=true;
    }
}

void txt::fDeselect(QRect select_) {
    if (layers::num[layerNum].visible) {
        if (pointInRect(point,select_))
            select=false;
    }
}

void txt::fAllDeselect(QRect select_) {
    if (layers::num[layerNum].visible) {
        if (pointInRect(point,select_))
            select=false;
    }
}

void txt::pSelect(QRect select_) {
    if (layers::num[layerNum].visible) {
        if (pointInRect(point,select_))
            select=true;
    }
}

void txt::pDeselect(QRect select_) {
    if (layers::num[layerNum].visible) {
        if (pointInRect(point,select_))
            select=false;
    }
}

void txt::minimum(QPoint *pos) {
    if (point.x()<pos->x()) {
        pos->setX(point.x());
    }
    if (point.y()<pos->y()) {
        pos->setY(point.y());
    }
}

void txt::maximum(QPoint *pos) {
    if (point.x()>pos->x()) {
        pos->setX(point.x());
    }
    if (point.y()>pos->y()) {
        pos->setY(point.y());
    }
}

void txt::minimumSelect(QPoint *pos) {
 if (!select) return;
 minimum(pos);
}

void txt::maximumSelect(QPoint *pos) {
 if (!select) return;
 maximum(pos);
}

void txt::saveSOURCE( source *o){
}

void txt::saveGDS(gds *g) {
    //text
    g->write->writeUInt16(4);
    g->write->writeUInt8(0x0C);
    g->write->writeUInt8(0);
    //(*g->streamPtr)<<(quint16) 4<<(quint8) 0x0C<<(quint8)0;  //Text
    //layer
    g->write->writeUInt16(6);
    g->write->writeUInt8(0x0D);
    g->write->writeUInt8(2);
    if (!setup::gdsMapLayer)
    	g->write->writeInt16((qint16)layerNum);
    else
	g->write->writeInt16((qint16)layers::num[layerNum].mapToLayer);
    //datatype
    g->write->writeUInt16(6);
    g->write->writeUInt8(0x16);
    g->write->writeUInt8(2);
    if (!setup::gdsMapLayer)
    	g->write->writeInt16((qint16)datatype);
    else
	g->write->writeInt16((qint16)layers::num[layerNum].getMapToDatatype(datatype));
    //(*g->streamPtr)<<(quint16) 6<<(quint8) 0x0D<<(quint8)2<<(qint16)(layer_nr);  //layer
    //(*g->streamPtr)<<(quint16) 6<<(quint8) 0x16<<(quint8)2<<(qint16)(datatype);  //texttype
    //presentation
    g->write->writeUInt16(6);
    g->write->writeUInt8(0x17);
    g->write->writeUInt8(1);
    g->write->writeInt16((qint16)(presentation));
    //(*g->streamPtr)<<(quint16) 6<<(quint8) 0x17<<(quint8)1<<(qint16)(presentation);  //presentation
     //width
    g->write->writeUInt16(8);
    g->write->writeUInt8(0x0F);
    g->write->writeUInt8(3);
    g->write->writeInt32((qint32)(width));
    //(*g->streamPtr)<<(quint16) 8<<(quint8) 0x0F<<(quint8)3<<(qint32)(width);  //width
    qint16 strans_=0;
    if (trans.mirror_x) {
        strans_|=0x8000;
    };
    //STRANS
    g->write->writeUInt16(6);
    g->write->writeUInt8(0x1A);
    g->write->writeUInt8(1);
    g->write->writeInt16((qint16)(strans_));
    //(*g->streamPtr)<<(quint16) 6<<(quint8) 0x1A<<(quint8)1<<(qint16)(strans_);  //STRANS
    if (trans.mag!=1){
	//mag
	g->write->writeUInt16(12);
	g->write->writeUInt8(0x1B);
	g->write->writeUInt8(5);
	g->write8ByteReal(trans.mag);
	//(*g->streamPtr)<<(quint16) 12<<(quint8) 0x1B<<(quint8)5;  //MAG
    }
    if (trans.angle!=0){
	//angle
	g->write->writeUInt16(12);
	g->write->writeUInt8(0x1C);
	g->write->writeUInt8(5);
	//(*g->streamPtr)<<(quint16) 12<<(quint8) 0x1C<<(quint8)5;  //Angle
	if ((trans.mirror_x)&&(trans.angle!=0)) g->write8ByteReal(360-trans.angle);	
	else  g->write8ByteReal(trans.angle);	
    }
    //xy
    g->write->writeUInt16((1*2*4)+4);
    g->write->writeUInt8(0x10);
    g->write->writeUInt8(3);
    g->write->writeInt32((qint32)point.x());g->write->writeInt32(point.y());
    //(*g->streamPtr)<<(quint16) ((1*2*4)+4)<<(quint8) 0x10<<(quint8)3;  //XY
    //(*g->streamPtr)<<(qint32)(point.x())<<(qint32)(point.y());
    g->writeString(name,0x19);
	//properties
	saveGDSProperty(g);
    // endel
    g->write->writeUInt16(4);
    g->write->writeUInt8(0x11);
    g->write->writeUInt8(0);
    //(*g->streamPtr)<<(quint16) 4<<(quint8) 0x11<<(quint8)0;  //endel
    g->count();
}

void txt::saveSelect(QDataStream *stream){
  if (select){
	save(stream);
  }

}

void txt::save(QDataStream *stream){
	(*stream)<<(qint8)(4);
	(*stream)<<(qint16)(layerNum);
	(*stream)<<(qint16)(datatype);
	(*stream)<<(qint16)(presentation);
	(*stream)<<(qint32)(width);
	(*stream)<<(qint32)(point.x());
	(*stream)<<(qint32)(point.y());
	(*stream)<<(qreal)(trans.angle);
	(*stream)<<(qreal)(trans.mag);
        if (trans.mirror_x) (*stream)<<(qint8)(1);
	else (*stream)<<(qint8)(0);
	(*stream)<<(qint16)name.length();
	if (name.length()!=0) stream->writeRawData (name.toAscii().data(), name.length() );
}

void txt::saveDXF(dxf *d ) {
QString s;
d->writeEntry(0,"TEXT");
d->writeEntry(100,"AcDbText");
//dxf::writeEntry(st,8,layers::num[layer_nr].name);
d->writeLayer(layerNum);
d->writeEntry(10,s.setNum(point.x()*d->df->userunits));
d->writeEntry(20,s.setNum(point.y()*d->df->userunits));
if (presentation!=8){
  d->writeEntry(11,s.setNum(point.x()*d->df->userunits));
  d->writeEntry(21,s.setNum(point.y()*d->df->userunits));
}
d->writeEntry(30,"   0");
d->writeEntry(40,s.setNum(width*d->df->userunits));
d->writeEntry(1,name);
d->writeEntry(50,s.setNum(trans.angle));
int i=0;
if ((presentation&3)==0) i=0;
if ((presentation&3)==1) i=1;
if ((presentation&3)==2) i=2;
d->writeEntry(72,s.setNum(i));
if ((presentation&12)==8) i=1;
if ((presentation&12)==4) i=2;
if ((presentation&12)==0) i=3;
d->writeEntry(73,s.setNum(i));
}

void txt::saveSVG(svg *s){
 *(s->str)<<"<text ";
s->savePos("x",point.x());
s->savePos("y",-point.y());
s->saveValue("fill",layers::num[layerNum].pen.color().name());
if (width>0) s->savePos("font-size",width);
/*if (trans.angle!=0){
	*(s->str)<<"transform=\"rotate(";
	s->saveNum("",-trans.angle);
	*(s->str)<<")\" ";
	}*/
 *(s->str)<<">";
 *(s->str)<<name<<"</text>"<<s->endOfLine;
}

void txt::saveOASIS( oasis *o){
  int oasisDatatype=datatype;
  int oasisLayer=layerNum;
  if (!setup::oasisMapLayer){
	oasisLayer=layers::num[layerNum].mapToLayer;
	oasisDatatype=layers::num[layerNum].getMapToDatatype(datatype);
  }
if (!o->modal.absoluteMode){o->setModalAbsoluteMode();}
  qint8 info_byte=64;  //explicid text
  if (oasisLayer!=o->modal.textlayer) info_byte+=1;
  if (oasisDatatype!=o->modal.texttype) info_byte+=2;
  if (point.x()!=o->modal.text_x) info_byte+=16;
  if (point.y()!=o->modal.text_y) info_byte+=8;
  o->writeUnsignedInteger((uint)19);
  o->writeRaw(info_byte);
  o->writeString(name);
  if (info_byte&1) {
	o->modal.textlayer=oasisLayer;
	o->writeUnsignedInteger((uint)o->modal.textlayer);
	}
  if (info_byte&2) {
	o->modal.texttype=oasisDatatype;
	o->writeUnsignedInteger((uint)oasisDatatype);
	}
  if (info_byte&16) {
	o->modal.text_x=point.x();
	o->writeSignedInteger(o->modal.text_x);
	}
  if (info_byte&8) {
	o->modal.text_y=point.y();
	o->writeSignedInteger(o->modal.text_y);}  
  if (presentation!=setup::defaultTextPresentation)
	 { o->error->addItem("Text presentation can not be saved in OASIS files.",4);}
  if (trans.mag!=1)
	 { o->error->addItem("Scaled text can not be saved in OASIS files.",4);}
  if (trans.angle!=0)
	 { o->error->addItem("Rotated text can not be saved in OASIS files.",4);}
  o->count();
  saveOASISProperty(o);
}

void txt::saveCIF( cif *c){
  QString s;
  QString s1;
  s="4N ";
  s1=name;
  s1.replace(" ","");
  s1.replace(",","");
  s+=s1+" ";
  s+=s1.setNum(point.x())+" ";
  s+=s1.setNum(point.y())+" ";
  s=s.trimmed();
  c->writeEntry(s);
//c->reportSave->addItem("Text can not be save in CIF format.",2,name);
  c->reportSave->addItem("Text is saved as \"4N\" record (signal label)",2,name);
}

void txt::saveGerber( gerber *g){
 if (g->saveLayer!=layerNum) return;
  g->reportSave->addItem("Text element can not be saved in gerber",2,name);
}

void txt::saveODB( odb *g){
#ifdef netlistutility
#endif
}

void txt::saveEPS( eps *e){
}

int txt::saveEPSCount(){
  int count=29+20+11+1*(16+8)+9+7+20+30+name.length();
  return count;
}

void txt::moveSelect(QPoint pos) {
    if (select)
        point=QPoint(point.x()+pos.x(),point.y()+pos.y());
}

void txt::move(QPoint pos) {
    point=QPoint(point.x()+pos.x(),point.y()+pos.y());
}

void txt::setMirrorx() {
    trans.setMirror_x();
}

void txt::clearMirrorx() {
    trans.clearMirror_x();
}

void txt::toggleMirrorx() {
    trans.toggleMirror_x();
}

void txt::rotate(double angle) {
    trans.rotate(-angle);
}

void txt::scale(double scale) {
    trans.scale(scale);
}

double txt::nearestDistance(QPoint p) {
    if (layers::num[layerNum].visible) {
        return distance(point,p);
    } else
        return 1.1E99;
}

double txt::nearestDistance(const QPoint p,QPoint *p_)const {
    if (layers::num[layerNum].visible) {
    	*p_=point;
        return distance(point,p);
    } else
        return 1.1E99;
}


bool txt::showProperties(elementList *el, drawingField *d,bool ) {
    textproperties b(d);
    b.setElement(el->thisElement->getText());
    b.show();
    int i=b.exec();
    b.hide();
    switch (i) {
    case QDialog::Rejected :
        return false;
    case QDialog::Accepted :
        clean();
        return false;
    case 2 :
        return true;
    case 3 : {
            elementList *e;
	    elementList *help;
            e=convertText();
	    if (e==NULL) return false;
	    help=e;
	    while (help->nextElement!=NULL){help=help->nextElement;}
            help->nextElement=el->nextElement;
            el->nextElement=e;
            if (e->thisElement!=NULL)
                return true;
            return false;
        }
    }
    return false;
}

void txt::clean() {
    if (trans.mag==0) {
        trans.mag=1;
    }
}

void txt::map(strans m) {
    trans*=m;
    point=m.matrix.map(point);
}

void txt::mapSelect(strans m) {
    if (select) {
        trans*=m;
        point=m.matrix.map(point);
    }
}

void txt::resize(double size) {
    point*=size;
    trans.scale(size);
}

elementList * txt::convertText() {
    elementList *el=NULL;
	if (render.available()) {
		int w=width;
		if (w<=0) {
			return NULL;
			}
                QList<pointArray> l=render.renderText(name,w,presentation,point,trans);
		for (int listCount=0;listCount<l.size();listCount++){
			elementList *help=el;
	    		el=new elementList();
            		el->thisElement=new polygon(l[listCount],layerNum);
			el->thisElement->datatype=datatype;
    			el->thisElement->property=property;
            		el->nextElement=help;
		}
	}

    return el;

}

bool txt::identicalStructure(element *e){
	if (e->isText()) {
	txt *b=reinterpret_cast<txt(*)>(e);
	if ((*b)%(*this)) return true;
	}
return false;
}

bool operator% (const txt &b1,const txt &b2){
if (b1.layerNum!=b2.layerNum) return false;
if (b1.datatype!=b2.datatype) return false;
if (b1.name!=b2.name) return false;
if (!(b1.trans==b2.trans)) return false;
return true;
}

bool txt::identical(element *e){
if (e->isText()) {
	txt *b=reinterpret_cast<txt(*)>(e);
	if ((*b)==(*this)) return true;
	}
return false;
}

bool operator== (const txt &b1,const txt &b2){
if (b1.layerNum!=b2.layerNum) return false;
if (b1.datatype!=b2.datatype) return false;
if (b1.point!=b2.point) return false;
if (b1.name!=b2.name) return false;
if (!(b1.trans==b2.trans)) return false;
return true;
}













