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

drawingField *lef::df=NULL;

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


lef::~lef()
{
}


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

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

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

void lef::load(QString fileName,drawingField *d,fileOpenType typ){
  if (typ==fileImport) {
  	report.setTitle(tr("Import of LEF-File \"")+fileName+"\"");}
  else if (typ==fileOpen){
	report.setTitle(tr("Open of LEF-File \"")+fileName+"\"");}
  else if (typ==fileUpdate){
	report.setTitle(tr("Update with LEF-File \"")+fileName+"\"");}
try { 
#ifdef printtime
  setup::centralTimer.start();
#endif
  cellList *firstcellhelp=d->firstCell;
  double databaseunitshelp=d->databaseunits;
//  double userunitshelp=d->userunits;
  QFile f( fileName );
  if ( !f.open( QIODevice::ReadOnly ) )
	throw QString(tr("Can not open File."));
  QTextStream ts( &f );
  stream=&ts;
  if ((typ==fileImport)||(typ==fileUpdate)) {
	d->firstCell=NULL;
  }else {
	//remove all technologie layer
	for (int i=0;i<layersMax;i++){
		layers::num[i].setType(layerTypeUnknown);
	}
	}
  for (int i=0; i<layersMax;i++){
  	 layerused[i]=false;
  	 if ((typ==fileImport)||(typ==fileUpdate)) {
		if (firstcellhelp->useLayer(i)) layerused[i]=true;
  }
} layerused[setup::lefCellBoundaryLayer]=true;

  QString s="";
  cellList *cell_list;
  cell *cell_;
  cell_=NULL;
  cell_list=NULL; 
  
  QString sectionType="";
  QString sectionName="";
  QString pinName="";
  QPoint origin=QPoint(0,0);
  bool pinNamePlaced=true;
  int layer=0;
  int width=0;
  QRegExp seper=QRegExp("[ \t]");
  while (!ts.atEnd()&&(s!="EOF")){
  	s=ts.readLine().trimmed();
	bool endSemi=false;
	if (s.size()>0) endSemi=(s.at(s.size()-1)==';');
	if (s.left(1)=="#") {
		//commend
	}
	else if ((!endSemi)&&(s.left(3)=="VIA")){
		QStringList sl=s.split(seper,QString::SkipEmptyParts);
		sectionType="VIA-CELL";
		if (sl.size()>1) sectionName=sl[1];
		else sectionName="unnamed";
		cell_list=d->addCell();
		cell_=cell_list->thisCell;
		cell_->cellName=sectionName;
		}
	else if (s.left(5)=="MACRO"){
		sectionType="MACRO";
		sectionName=s.mid(6).trimmed();
		cell_list=d->addCell();
		cell_=cell_list->thisCell;
		cell_->cellName=sectionName;
		origin=QPoint(0,0);
	}
	else if (s.left(5)=="UNITS"){
		sectionType="UNITS";
		sectionName="UNITS";
	}
	else if (s.left(8)=="DATABASE"){
		if (sectionType=="UNITS"){
			QStringList sl=s.split(seper,QString::SkipEmptyParts);
			if (sl.size()>2){
				double datab=sl[2].toDouble();
				//printf("%f\n",datab);
				if (datab!=0){
					d->databaseunits=0.000001/datab;
					d->userunits=1.0/datab;
				}
				}
			}
	}
	else if (s.left(6)=="ORIGIN") {
		QStringList sl=s.split(seper,QString::SkipEmptyParts);
		if (sl.size()>2){
			origin.setX(element::runden(sl[1].toDouble()/d->userunits));
			origin.setY(element::runden(sl[2].toDouble()/d->userunits));
		}
		}
	else if (s.left(4)=="SIZE") {
		QStringList sl=s.split(seper,QString::SkipEmptyParts);
		if (sl.size()>3){
			QPoint pSize;
			pSize.setX(element::runden(sl[1].toDouble()/d->userunits));
			pSize.setY(element::runden(sl[3].toDouble()/d->userunits));
			if (cell_!=NULL) cell_->addBox(origin.x(),origin.y(),pSize.x(),pSize.y(),setup::lefCellBoundaryLayer);
		}
		}
	else if (s.left(3)=="END"){
		QStringList sl=s.split(seper,QString::SkipEmptyParts);
		if (sl.size()>1){
			if (sl[1]==sectionName) sectionType="";
			else if (sl[1]==pinName) {pinName=""; pinNamePlaced=true;}
		}
	}
	else if (s.left(3)=="PIN"){
		QStringList sl=s.split(seper,QString::SkipEmptyParts);
		if (sl.size()>1){
		pinName=sl[1];
		pinNamePlaced=false;
		}
	}
	else if ((!endSemi)&&(s.left(5)=="LAYER")){
		sectionType="LAYER";
		sectionName=s.mid(6).trimmed();
		layer=layers::findLayer(sectionName);
		if (layer==-1) {
				int layerNum=1;
				while ( layerused[layerNum]) layerNum++;
				if (layerNum<layersMax){
					layers::num[layerNum].name=sectionName;
					layerused[layerNum]=true;
					report.addItem(tr("Add layer"),4,sectionName);
					layer=layerNum;
					}
				else {
					layer=1;
					report.addItem(tr("unknow layer"),2,sectionName);
					}
				}
		else layerused[layer]=true;
		}
	else if (s.left(5)=="LAYER"){
		QStringList sl=s.split(seper,QString::SkipEmptyParts);
		if (sl.size()>1){
			layer=layers::findLayer(sl[1]);
			if (layer==-1) {
					int layerNum=1;
					while ( layerused[layerNum]) layerNum++;
					if (layerNum<layersMax){
						layers::num[layerNum].name=sl[1];
						layerused[layerNum]=true;
						report.addItem(tr("Add layer"),4,sl[1]);
						layer=layerNum;}
					else {
						layer=1;
						report.addItem(tr("unknow layer"),2,sl[1]);
						}
					}
			else layerused[layer]=true;
			if (layers::num[layer].layerType==layerTypeConductor)
					width=layers::num[layer].getTypeParameter(1);
			}
		else report.addItem(tr("can not process LAYER information"),2,s);
	}
	else if (s.left(4)=="RECT"){
		QStringList sl=s.split(seper,QString::SkipEmptyParts);
		if ((sl.size()>4)&&(cell_!=NULL)){
			QRect r;
			r.setLeft(element::runden(sl[1].toDouble()/d->userunits));
			r.setBottom(element::runden(sl[2].toDouble()/d->userunits));
			r.setRight(element::runden(sl[3].toDouble()/d->userunits));
			r.setTop(element::runden(sl[4].toDouble()/d->userunits));
			if (!pinNamePlaced) {
				cell_->addText(layer,QPoint(r.left(),r.top()),pinName);
				pinNamePlaced=true;
				}
			cell_->addBox(r,layer);
		}
		else report.addItem(tr("can not process RECT information"),2,s);
	}
	else if (s.left(7)=="POLYGON"){
		QStringList sl=s.split(seper,QString::SkipEmptyParts);
		if ((cell_!=NULL)&&(sl.size()>2)){
			pointArray pa;
			for (int k=1;k<sl.size()-1;k+=2){
				QPoint p;
				p.setX(element::runden(sl[k].toDouble()/d->userunits));
				p.setY(element::runden(sl[k+1].toDouble()/d->userunits));
				pa<<p;
			}
			if (!pinNamePlaced) {
				cell_->addText(layer,pa.point(0),pinName);
				pinNamePlaced=true;
				}
			cell_->addPolygon(pa,layer);
		}
		else report.addItem(tr("can not process POLYGON information"),2,s);
	}
	else if (s.left(3)=="VIA"){
		QStringList sl=s.split(seper,QString::SkipEmptyParts);
		if ((cell_!=NULL)&&(sl.size()>4)){
			if (sl[1]!="ITERATE"){
				QPoint p;
				p.setX(element::runden(sl[1].toDouble()/d->userunits));
				p.setY(element::runden(sl[2].toDouble()/d->userunits));
				element *e=cell_->addCellref();
				e->setName(sl[3]);
				e->setCellRef(d->findCell(sl[3]));
				e->setPos(p);
			}
		}
	}
	else if (s.left(4)=="PATH"){
		QStringList sl=s.split(seper,QString::SkipEmptyParts);
		if ((cell_!=NULL)&&(sl.size()>2)){
			pointArray pa;
			for (int k=1;k<sl.size()-1;k+=2){
				QPoint p;
				p.setX(element::runden(sl[k].toDouble()/d->userunits));
				p.setY(element::runden(sl[k+1].toDouble()/d->userunits));
				pa<<p;
			}
			if (!pinNamePlaced) {
				cell_->addText(layer,pa.point(0),pinName);
				pinNamePlaced=true;
				}
			element *e=cell_->addPath(pa,layer);
			e->setWidth(width);
			e->setCap(2);
		}
		else report.addItem(tr("can not process PATH information"),2,s);
	}
	else if (s.left(5)=="WIDTH"){
		QStringList sl=s.split(seper,QString::SkipEmptyParts);
		if (sl.size()>1){
			width=element::runden(sl[1].toDouble()/d->userunits);
			
		}
		if (sectionType=="LAYER"){
				if (layers::num[layer].layerType==layerTypeConductor)
					layers::num[layer].setTypeParameter(1,width);
				else if (layers::num[layer].layerType==layerTypeVia)
					layers::num[layer].setTypeParameter(1,width);
				//printf("width %d %d %s\n",layer,width,sl[1].toAscii().data());
				}
	}
	else if (s.left(4)=="TYPE"){
	    if (sectionType=="LAYER"){
		QStringList sl=s.split(seper,QString::SkipEmptyParts);
		if ((sl.size()>1)){
			if (sl[1]=="ROUTING")
			layers::num[layer].setType(layerTypeConductor);
			else if (sl[1]=="CUT")
			layers::num[layer].setType(layerTypeVia);
			if ((layers::num[layer].layerType==layerTypeConductor)|| (layers::num[layer].layerType==layerTypeVia))
			layers::num[layer].setTypeParameter(0,layers::maxLevel()+1);
			layers::checkLayer();
		}
	    }
	}
	else if (s.left(7)=="SPACING"){
	    if (sectionType=="LAYER"){
		QStringList sl=s.split(seper,QString::SkipEmptyParts);
		if ((sl.size()>1)){
			int space=element::runden(sl[1].toDouble()/d->userunits);
			if (layers::num[layer].layerType==layerTypeConductor)
					layers::num[layer].setTypeParameter(2,space);
				else if (layers::num[layer].layerType==layerTypeVia) {
					layers::num[layer].setTypeParameter(3,space);
					layers::num[layer].setTypeParameter(2,space);
					layers::num[layer].setTypeParameter(4,space);
					}
				};
		}
	}
  }
  f.close();
//printf("0\n");
  if (d->currentCell==NULL) d->findTopCell();
   // resolving brocken cellrefs
//printf("1\n");

  for (cellList *f =d->firstCell; f!=NULL;f=f->nextCell) {
  	if (f->this_cell!=NULL){
		//f->this_cell->clean();
  		for (elementList *e=f->thisCell->firstElement;e!=NULL;e=e->nextElement){
			if (e->thisElement->isCellref()||e->thisElement->isCellrefArray()) {
				 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->thisCell->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();
  	}
  }
//printf("2\n");
  //  
  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;}
		}
//printf("3\n");
  if (typ==fileImport) filegeneral::import(&report,d,firstcellhelp);
  if (typ==fileUpdate) filegeneral::update(&report,d,firstcellhelp);
//printf("4\n");
#ifdef printtime
  printf("lef 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());
//printf("5\n");
}


void lef::save(QString ,drawingField *){

}


