/***************************************************************************
 *   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 "cif.h"
#include <qfile.h>
#include <qstring.h>
#include <qtextstream.h>
#include "general/drawingfield.h"
//#include <q3pointarray.h>
#include "general/layers.h"
#include "elements/celllist.h"
#include "elements/cell.h"
#include "elements/path.h"
#include "elements/polygon.h"
#include "elements/strans.h"
#include <qmessagebox.h>
#include "general/setup.h"
#include "elements/element.h"
#include <qstringlist.h>
#include "layout.h"
#include <math.h>
#include "general/errorreport.h"


cif::cif(QObject *parent, const char *)
 : QObject(parent)
{
}


cif::~cif()
{
}


void cif::open(QString fileName,drawingField *d){
  cif cifClass;
  cifClass.load(fileName,d,fileOpen);
}

void cif::import(QString fileName,drawingField *d){
  cif cifClass;
  cifClass.load(fileName,d,fileImport);
}

void cif::update(QString fileName,drawingField *d){
  cif cifClass;
  cifClass.load(fileName,d,fileUpdate);
}

void cif::load(QString fileName,drawingField *d, fileOpenType typ){
  if (typ==fileImport) {
  	report.setTitle(tr("Import of CIF-File \"")+fileName+"\"");}
  else  if (typ==fileOpen){
	report.setTitle(tr("Open of CIF-File \"")+fileName+"\"");}
  else  if (typ==fileUpdate){
	report.setTitle(tr("Update with CIF-File \"")+fileName+"\"");}
try { 
#ifdef printtime
  setup::centralTimer.start();
#endif
  QTime cifTimer;
  cifTimer.start();
  int recordCount=0;
  cellList *firstcellhelp=d->firstCell;
  double databaseunitshelp=d->databaseunits;
  QFile f( fileName );
  if ( !f.open( QIODevice::ReadOnly ) )
	throw QString(tr("Can not open File."));
  QTextStream ts( &f );
  stream=&ts;
  puffer="";
  if ((typ==fileImport)||(typ==fileUpdate)) {
	d->firstCell=NULL;
  }
  for (int i=0; i<layersMax;i++){
  	 layerused[i]=false;
  	 if ((typ==fileImport)||(typ==fileUpdate)) {
		if (firstcellhelp->useLayer(i)) layerused[i]=true;
  }}
  QString s="";
  QString s1;
  cellList *cell_list=NULL;;
  cell *cell_=NULL;
  cell_list=NULL; 
  element *element_;
  int pos;
  int layer=1;
  double factor=1;
  d->fileType="CIF";
  s=readString();
  while (!ts.atEnd()&&(s[0]!=QChar('E'))){
          recordCount++;
	  if (s[0]==QChar('B')){
		if (cell_==NULL) {
			report.addItem(tr("Box outside cell. Element ignored."),3,s);
			}
		else {
		pos=0;
		if (s.left(3)=="BOX") pos=2;
		if (s.left(3)=="Box") pos=2;
		int length=element::runden((double)getInt(s,&pos)*factor);
		int width=element::runden((double)getInt(s,&pos)*factor);
		QPoint center=getPoint(s,&pos);
		QString s2=s.mid(pos);
		if (s2.trimmed().length()<3){
			center=QPoint(element::runden(factor*(double)center.x()),element::runden(factor*(double)center.y()));
			element_ = cell_->addBox(center.x()-(length/2),center.y()-(width/2),length,width,layer);
			if (layout::debug) printf("Box %d %d %d %d\n",length,width,center.x(),center.y());
		}
		else
		{
			QPoint dir=getPoint(s,&pos);
			if (length==0) break;
			int w2=(width/2);
			int l2=(length/2);
			double length2=element::length(dir);
			double x=1.0/length2*dir.x();
			double y=1.0/length2*dir.y();
			pointArray array;
			array.resize(5);
			array.setPoint(0,(int)(center.x()-(x*l2)+(y*w2)),(int)(center.y()-(x*w2)-(y*l2)));
			array.setPoint(1,(int)(center.x()+(x*l2)+(y*w2)),(int)(center.y()-(x*w2)+(y*l2)));
			array.setPoint(2,(int)(center.x()+(x*l2)-(y*w2)),(int)(center.y()+(x*w2)+(y*l2)));
			array.setPoint(3,(int)(center.x()-(x*l2)-(y*w2)),(int)(center.y()+(x*w2)-(y*l2)));
			array.setPoint(4,(int)(center.x()-(x*l2)+(y*w2)),(int)(center.y()-(x*w2)-(y*l2)));
			element_=cell_->addPolygon(array,layer);
			if (layout::debug) printf("Rotated Box\n");
		}
		}
	  }
    else if (s[0]==QChar('P')){
		if (cell_==NULL) {
			report.addItem(tr("Polygon outside cell. Element ignored."),3,s);
			}
		else {
		pos=0;
		pointArray array;
		array.resize(0);
		while ((pos+3)<(int)s.length()){
			array.resize(array.size()+1);
			QPoint p=getPoint(s,&pos);
			array.setPoint(array.size()-1,element::runden(factor*(double)p.x()),element::runden(factor*(double)p.y()));
		}
		array.resize(array.size()+1);
		array.setPoint(array.size()-1,array.point(0));
		element_=cell_->addPolygon(array,layer);
		if (layout::debug) printf("Polygon\n");
		}
	  }
    else if (s[0]==QChar('C')){
  		if (cell_==NULL) {
			report.addItem(tr("Cell reference outside cell. Element ignored."),4,s);
			}
		else {
		pos=0;
		int num=getInt(s,&pos);
		element_ = cell_->addCellref();
		element_ ->setName(s1.setNum(num));
		QPoint ptrans=QPoint(0,0);
		bool mirror=false;
		//printf("org: (%s) \n",s.toAscii().data());
			
		while ((pos+2)<(int)s.length()){
			//printf("(%s)%d ->",s.toAscii().data(),(pos));
			s1=getString(s,&pos).trimmed();
			//printf("read: (%s) pos: %d\n",s1.toAscii().data(),(pos));
			strans matrix;
			matrix.reset();
			if (s1=="T") {
				int x=element::runden((double)getInt(s,&pos)*factor);
				int y=element::runden((double)getInt(s,&pos)*factor);
				ptrans=QPoint(x,y);
				//printf("xy %d %d\n",x,y);
				matrix.translate(x,y);
				}
			else if (s1=="MX") {matrix.toggleMirror_x();matrix.rotate(180);mirror=!mirror;}
			else if (s1=="MY") {matrix.toggleMirror_x();mirror=!mirror;}
			else if (s1=="R") {
				int x=getInt(s,&pos);
				int y=getInt(s,&pos);
				double angle;
				if (x==0) {if (y>0) angle=90; else angle=270;}
				else {
				  angle=atan((double)y/(double)x)/2/M_PI*360;
				  if (angle<0) angle=-angle;
				  
				  if ((x<0)&&(y<0)) {angle+=180;}
				  else if ((y<0)) {angle=360-angle;}
				  else if ((x<0)) {angle=180-angle;}
				  
				}
				//printf("R %d %d %f \n",x,y,angle);
				matrix.rotate(angle);}
			else if (s1.left(1)=="T"){
				int p2=0;
				s1=" "+s1.mid(1);
				int x=element::runden((double)getInt(s1,&p2)*factor);
				int y;
				if ((s1.length()>(p2+1))&&(p2>0))
				   y=element::runden((double)getInt(s1,&p2)*factor);
				else y=element::runden((double)getInt(s,&pos)*factor);
				ptrans=QPoint(x,y);
				//printf("-xy %d %d \n",x,y);
				matrix.translate(x,y);
				//printf("--(%s) (%d/%d)\n",s1.toAscii().data(),getInt(s1,&p2),getInt(s1,&p2));
				}
			else if (s1.left(1)=="R"){
				int p2=0;
				s1=" "+s1.mid(1);
				int x=element::runden((double)getInt(s1,&p2)*factor);
				int y;
				//printf(" %d %d %s \n",s1.length(),p2,s1.toAscii().data());
				if ((s1.length()>(p2+1))&&(p2>0))
				   y=element::runden((double)getInt(s1,&p2)*factor);
				else y=element::runden((double)getInt(s,&pos)*factor);
				double angle;
				if (x==0) {if (y>0) angle=90; else angle=270;}
				else {
				  angle=atan((double)y/(double)x)/2/M_PI*360;
				  if (angle<0) angle=-angle;
				  //printf("%d %d %f ",x,y,angle);
				  if ((x<0)&&(y<0)) {angle+=180;}
				  else if ((y<0)) {angle=360-angle;}
				  else if ((x<0)) {angle=180-angle;}
				  
				}
				//printf("-R %d %d %f \n",x,y,angle);
				matrix.rotate(angle);
			}
			else if (s1.trimmed()!="") report.addItem(tr("Unknow transformation"),3,s1);
			element_->map(matrix);
		}
		if (layout::debug) printf("Cellref\n");
		}
	  }
	  else if (s[0]==QChar('L')){
	    pos=0;
		s1=getString(s,&pos).trimmed();
		layer=layers::findLayer(s1);
		if (layer==-1) {
				 int layerNum=1;
				 while ( layerused[layerNum]) layerNum++;
				 if (layerNum<layersMax){
				 	layers::num[layerNum].name=s1;
				 	layerused[layerNum]=true;
				 	report.addItem(tr("Add layer"),4,s1);
				 	layer=layerNum;}
				 else {
				 	layer=1;
					report.addItem(tr("unknow layer"),2,s1);
				 	}
				}
		else layerused[layer]=true;
		if (layout::debug) printf("Layer %d\n",layer);
	  }
  	  else if (s[0]==QChar('R')){
  		if (cell_==NULL) {
			report.addItem(tr("Circle outside cell. Element ignored."),3,s);
			}
		else {
		pos=0;
		int radius=element::runden((double)getInt(s,&pos)*factor*0.5);
		QPoint center=getPoint(s,&pos);
		center=QPoint(element::runden(factor*(double)center.x()),element::runden(factor*(double)center.y()));
		element_ = cell_->addCircle(layer,center,radius);
		if (layout::debug) printf("Circle/Flash\n");
		}
	  }
  	  else if (s[0]==QChar('W')){
  		if (cell_==NULL) {
			report.addItem(tr("Path outside cell. Element ignored."),3,s);
			}
		else {
		pos=0;
		int width=element::runden((double)getInt(s,&pos)*factor);
		pointArray array;
		array.resize(0);
		while ((pos+3)<(int)s.length()){
			array.resize(array.size()+1);
			QPoint p=getPoint(s,&pos);
			array.setPoint(array.size()-1,element::runden(factor*(double)p.x()),element::runden(factor*(double)p.y()));
		}
		element_=cell_->addPath(array,layer);
		element_->setWidth(width);
		element_->setCap(1);
		if (layout::debug) printf("Wire/Path\n");
		}
	  }
	  else if ((s[0]==QChar('D'))&&(s[1]==QChar('S'))){
		cell_list=d->addCell();
		cell_=cell_list->thisCell;
		pos=1;
		cellNum=getInt(s,&pos);
		s1=s1.setNum(cellNum);
		cell_->cellName=s1;
		int a=getInt(s,&pos);
		int b=getInt(s,&pos);
		factor=(double)a/(double)b/(double)100000000/databaseunitshelp;
		if (layout::debug) printf("Cell %d %d %d %e\n",cellNum,a,b,factor);
	  }
  	  else if ((s[0]==QChar('D'))&&(s[1]==QChar('F'))){
		if (layout::debug) printf("Cell end\n");
		cell_=NULL;
	  }
	  else if (s[0]==QChar('9')){
	    if (s[1]==QChar(' ')){
			pos=1;
			if (cell_!=NULL) {
				cell_->cellName=getString(s,&pos).trimmed();
				cellsList.insert(cellNum,cell_);
				if (layout::debug) printf("Cell Name: %s\n",cell_->cellName.toAscii().data());
				}
		}else
		{report.addItem(tr("Unknow entry"),3,s);}
	  }
	  else if (s[0]==QChar('4')){
		  if (s[1]==QChar('A'))
		      report.addItem(tr("Entry not used"),3,tr("\"4A\" Declare cell boundary"));
		  else if (s[1]==QChar('B'))
		      report.addItem(tr("Entry not used"),3,tr("\"4B\" Attach instance name to cell"));
		  else if (s[1]==QChar('N')){
			  pos=2;
			  QString text=getString(s,&pos).trimmed();
			  QPoint center=getPoint(s,&pos);
              center=QPoint(element::runden(factor*(double)center.x()),element::runden(factor*(double)center.y()));
		      if (cell_!=NULL) 
				cell_->addText(layer,center,text);
			  //report.addItem(tr("Entry not used"),3,tr("\"4N\" Labels a signal at a location"));
		  }
			  else if (s[1]==QChar('X')){
			    pos=2;
				QString text=getString(s,&pos).trimmed();
				getInt(s,&pos);
				QPoint center=getPoint(s,&pos);
				center=QPoint(element::runden(factor*(double)center.x()),element::runden(factor*(double)center.y()));
				int width=element::runden((double)getInt(s,&pos)*factor);
				if (cell_!=NULL) {
					element *e=cell_->addText(layer,center,text);
					e->setWidth(width);
				}
		      //report.addItem(tr("Entry not used"),3,tr("\"4X\" Labels a signal at a location"));
			  }
		  else 
		      report.addItem(tr("Entry not used"),3,tr("\"4\" not known"));
	  }
	  else {
		  report.addItem(tr("Unknow entry"),3,s);
	  }
/*

      (void)fprintf( File, "4X %s %ld %ld %ld %ld %s;\n",
                     Name, Index,
                     RDS_TO_CIF_UNIT(  X ) + RDS_TO_CIF_UNIT( Dx ) / 2,
                     RDS_TO_CIF_UNIT(  Y ) + RDS_TO_CIF_UNIT( Dy ) / 2,
                     RDS_TO_CIF_UNIT( Width ), Name );
    }
    else
    {
        (void)fprintf( File, "94 %s %ld %ld %s;\n",
                       Name,
                       RDS_TO_CIF_UNIT(  X ) + RDS_TO_CIF_UNIT( Dx ) / 2,
                       RDS_TO_CIF_UNIT(  Y ) + RDS_TO_CIF_UNIT( Dy ) / 2,
                       Layer );
    }

        (void)fprintf( File, "4N %s %ld %ld;\n",
                       rdstocifname( Name ),
                       RDS_TO_CIF_UNIT(  X ) + RDS_TO_CIF_UNIT( Dx ) / 2,
                       RDS_TO_CIF_UNIT(  Y ) + RDS_TO_CIF_UNIT( Dy ) / 2);
      }
      else
      {
      (void)fprintf( File, "95 %s %ld %ld %ld %ld %s;\n",
                     rdstocifname( Name ), 
                     RDS_TO_CIF_UNIT( Dx ), RDS_TO_CIF_UNIT( Dy ),
                     RDS_TO_CIF_UNIT(  X ) + RDS_TO_CIF_UNIT( Dx ) / 2,
                     RDS_TO_CIF_UNIT(  Y ) + RDS_TO_CIF_UNIT( Dy ) / 2,
                     Layer );
*/

  	s=readString();
	if (((recordCount%1000)==0) ){
		QString sh;
		sh.setNum(recordCount);
		if ((cifTimer.elapsed()>200)){
			d->showMessage(tr("%1 records loaded.").arg(sh));
			cifTimer.start();
			}
		}
  }
  f.close();
  {
    QString sh;
    sh.setNum(recordCount);
    d->showMessage(tr("%1 records loaded.").arg(sh));
			
  }
  // resolving cellrefs
  for (cellList *f =d->firstCell; f!=NULL;f=f->nextCell) {
  	if (f->thisCell!=NULL){
  		for (elementList *e=f->thisCell->firstElement;e!=NULL;e=e->nextElement){
			if (e->thisElement->isCellref()||e->thisElement->isCellrefArray()) {
				 if (e->thisElement->depend()==NULL){
				   int num=e->thisElement->getName().toInt();
				   e->thisElement->setCellRef(cellsList.value(num));}
				 if (e->thisElement->depend()==NULL){e->thisElement->setCellRef(d->findCell(e->thisElement->getName()));}
				 if ((e->thisElement->depend()==NULL)&&(typ==fileImport)){  //refs to before existing cells
						cellList *c=d->firstCell;
						d->firstCell=firstcellhelp;
				 		e->thisElement->setCellRef(d->findCell(e->thisElement->getName()));
						d->firstCell=c;}
				 if (e->thisElement->depend()==NULL){
				 	//add empty cells
					cellList *c;
  					c=d->addCell();
  					c->this_cell->cellName=e->thisElement->getName();
					e->thisElement->setCellRef(c->thisCell);
				 	//f->this_cell->deleteElement(e->this_element);
				 	//e=f->this_cell->first_element;
					report.addItem(tr("Cellref(s) can not be resolved. Empty cell added."),1,e->thisElement->getName());}
			}
			if (e==0) {break;}
			
		}
	f->thisCell->clean();
  	}
  }
  d->currentCell=d->findTopCell();
  if (d->currentCell==NULL) d->currentCell=d->firstCell->thisCell;
  if ((typ==fileImport)||(typ==fileUpdate)) {
	if (d->databaseunits!=databaseunitshelp) {
		report.addItem(tr("Databaseunits are different.Import is fitted."),3,s.setNum(d->databaseunits,'g',10));
		for (cellList *f =d->firstCell; f!=NULL;f=f->nextCell) {f->thisCell->resize(d->databaseunits/databaseunitshelp);}
		d->databaseunits=databaseunitshelp;}
		}
  if (typ==fileImport) filegeneral::import(&report,d,firstcellhelp);
  if (typ==fileUpdate) filegeneral::update(&report,d,firstcellhelp);
#ifdef printtime
  printf("cif load: %d ms\n", setup::centralTimer.elapsed());
#endif
  }
catch (QString s){
  report.addItem(tr("Aborted. "),0);
  report.addItem(s,1);
 }
  //report.showReport(); 
  QString  s=report.getReport();
  d->showReport(s,report.getLastRang());
}

int cif::getInt(QString s, int *pos){
  //printf("-(%s)%d\n",s.toAscii().data(),(*pos));
  while (s.mid(*pos+1,1)==" ") (*pos)++;
  int i=s.indexOf(" ",*pos+1);
  if (i<0) i=s.indexOf(",",*pos+1);
  QString help=s.mid(*pos+1,i-(*pos)-1);
  *pos=i;
  //printf("(%s)%d\n",help.toAscii().data(),(*pos));
  return help.toInt();
}

QPoint cif::getPoint(QString s, int *pos){
  while (s.mid(*pos+1,1)==" ") (*pos)++;
  int i=s.indexOf(",",*pos+1);
  if (i<0) i=s.indexOf(" ",*pos+1);
  QString x=s.mid(*pos+1,i-(*pos)-1);
  *pos=i;
  while (s.mid(*pos+1,1)==" ") (*pos)++;
  i=s.indexOf(" ",*pos+1);
  QString y=s.mid(*pos+1,i-(*pos)-1);
  *pos=i;
  //printf("Point: (x,y)(%s,%s)\n",x.toAscii().data(),y.toAscii().data());
  return QPoint(x.toInt(),y.toInt());
}

QString cif::getString(QString s, int *pos){
  while (s.mid(*pos+1,1)==" ") (*pos)++;
  int i=s.indexOf(" ",*pos+1);
  QString help=s.mid(*pos+1,i-(*pos)-1);
  *pos=i;
  return help;
}


QString cif::readString (){
  QString s=puffer;
  while ((s.lastIndexOf(";")==-1)&&(s[0]!=QChar('E'))) {
  	s+=" "+stream->readLine();
	s=s.trimmed();
  }
  int i=s.indexOf(";");
  if ((i!=-1)&&(i<(int)s.length())){
  	puffer=s.mid(i+1);
	s=s.left(i+1);
	}
  else puffer="";
  s=s.trimmed();
  if (s[0]!=QChar('E')) s=s.left(s.length()-1);
  if (s[0]==QChar('(')) return readString();
  //s.replace("   "," ");
  //s.replace("  "," ");
  //printf("%s\n",s.toAscii().data());
  return s+" ";
}

void cif::save(QString fileName,drawingField *d){
errorreport report;
report.setTitle(tr("Save of CIF-File \"")+fileName+"\"");
try { 
  QFile f( fileName );
  if ( !f.open( QIODevice::WriteOnly ) ) {
	throw QString(tr("Can not open File."));
  }
 
  QTextStream stream( &f );
  cif cifClass;
  cifClass.save(&stream,d,&report);
  f.close();
}
catch (QString s){
  report.addItem(tr("Aborted. "),0);
  report.addItem(s,1);
 }
  //report.showReport();   
  QString s=report.getReport();
  d->showReport(s,report.getLastRang());

}
void cif::save(QTextStream *streamPtr,drawingField *d, errorreport *error){
  
  stream = streamPtr;
  reportSave = error;
  double factor=d->databaseunits*(double)100000000;
  a=1;
  b=1;
  if (factor>1) a=element::runden(factor);
  else b=element::runden(1/factor);
  cellNum=1;
  cellList *e;
  for(e=d->firstCell;e!=NULL;e=e->nextCell){
	e->thisCell->saved=false;}
  bool saved=false;
  while (!saved){
	saved=true;
	for(e=d->firstCell;e!=NULL;e=e->nextCell){
		if (e->thisCell->saved==false){
			if (!e->thisCell->dependNotSaved()){
			  //printf("save %s\n",e->thisCell->cellName.toAscii().data());
			  e->thisCell->saveCIF(this);}
			else{saved=false;}};
		}}
 if (setup::cifTopCell){
   QString s,s1;
   cell *c=d->findTopCell();
   if (c!=NULL){
    s1=s1.setNum(hashList.value(c->cellName));
    s="C "+s1+" ";
    writeEntry(s);
   }
 }
 *stream<<(QString)("E\r\n");
}
void cif::writeEntry(QString wert){
  while (wert.length()>58){
	int i=wert.indexOf(" ",48);
	if (i==-1) break;
	QString s=wert.left(i);
	wert=wert.mid(i);
	*stream<<s<<(QString)("\r\n");
  }
  *stream<<wert<<(QString)(";\r\n");
}


