/***************************************************************************
 *   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 "def.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"
#include "net/netlist.h"

drawingField *def::df=NULL;

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


def::~def()
{
}


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

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

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

void def::load(QString fileName,drawingField *d,fileOpenType typ){
  if (typ==fileImport) {
  	report.setTitle(tr("Import of DEF-File \"")+fileName+"\"");}
  else if (typ==fileOpen){
	report.setTitle(tr("Open of DEF-File \"")+fileName+"\"");
	report.addItem(tr("The DEF file format is strong connected with the corresponding LEF file. For a correct display please open the corresponding LEF file first and then import or attach the DEF file."),4);
	}
  else if (typ==fileUpdate){
	report.setTitle(tr("Update with DEF-File \"")+fileName+"\"");}
try { 
  QFile f( fileName );
  if ( !f.open( QIODevice::ReadOnly ) )
	throw QString(tr("Can not open File."));
  QTextStream ts( &f );
  stream=&ts;
report.addItem(tr("DEF import is still in beta development status. Please use with care!"),2);
  for (int i=0; i<layersMax;i++){
  	 layerused[i]=false;
  	 if ((typ==fileImport)||(typ==fileUpdate)) {
		if (d->firstCell->useLayer(i)) layerused[i]=true;
  }
}

#ifdef printtime
  setup::centralTimer.start();
#endif
  QString s="";
  cellList *cell_list;
  cell *cell_;
  cell_=NULL;
  cell_list=NULL; 
  cell_list=d->addCell();
  cell_=cell_list->thisCell;
  cell_->cellName="DEF-Cell";
  d->setCell("DEF-Cell");
  QString sectionType="";
  double scale=1;
  int layer=0;
  viaMap.clear();
  viaContent.clear();
  netList netlist;
  netlist.type=netListLoaded;
  QRegExp seper=QRegExp("[ \t]");
  QStringList externalNodes;
  while (!ts.atEnd()&&(s!="END DESIGN")){
  	s=ts.readLine().trimmed();
	if (layout::debug) printf("process line: %s\n",s.toAscii().data());
	if (s.left(10)=="COMPONENTS"){
		sectionType="COMPONENTS";
		if (layout::debug)printf("section %s\n",s.toAscii().data());
	}
	else if (s.left(11)=="SPECIALNETS"){
		sectionType="NETS";
		if (layout::debug)printf("section %s\n",s.toAscii().data());
	}
	else if (s.left(4)=="NETS"){
		sectionType="NETS";
		if (layout::debug)printf("section %s\n",s.toAscii().data());
	}
	else if (s.left(4)=="VIAS"){
		sectionType="VIAS";
		if (layout::debug)printf("section %s\n",s.toAscii().data());
	}
	else if (s.left(4)=="PINS"){
		sectionType="PINS";
		if (layout::debug)printf("section %s\n",s.toAscii().data());
	}	
	else if (s.left(6)=="DESIGN"){
		QStringList sl=s.split(seper,QString::SkipEmptyParts);
		if (sl.size()>1){
			cell_->cellName=sl[1];
			netlist.cellname=sl[1];
			if (layout::debug) printf("DESIGN: %s\n",cell_->cellName.toAscii().data());
		}
	}
	else if (s.left(5)=="UNITS"){
		QStringList sl=s.split(seper,QString::SkipEmptyParts);
		//printf("scale: %s\n",s.toAscii().data());
		if (sl.size()>4){
			double datab=sl[3].toDouble();
				//printf("%f\n",datab);
				if ((datab!=0)&&(typ==fileOpen)){
					d->databaseunits=0.000001/datab;
					d->userunits=1.0/datab;
				} else if (datab!=0)
				{
				scale=0.000001/datab/d->databaseunits;
				//printf("scale: %f\n",scale);
				}
			if (layout::debug) printf("UNITS: %s\n",sl[3].toAscii().data());
		}
	}
	else if (s.left(3)=="END") {
		sectionType="";
		if (layout::debug) printf("end\n");
	}
	else if (s.left(1)=="-"){
		QStringList sl=s.split(seper,QString::SkipEmptyParts);
		if (sectionType=="COMPONENTS"){
		if (layout::debug) printf("component process: %s\n",s.toAscii().data());
		 if (sl.size()>2){
			element *e=cell_->addCellref();
			e->setName(sl[2]);
			cell *cHelp=d->findCell(sl[2]);
			e->setCellRef(cHelp);
			e->setDeviceName(sl[1]);
			QString command="";
			netListDevice nld;
			nld.devicename=sl[1];
			nld.cellname=sl[2];
			if (cHelp!=NULL) nld.status=netListDeviceFound;
			else nld.status=netListDeviceNotFound;
			for (int k=3;k<sl.size();k++){
				if ((command=="new")&&((sl[k]=="PLACED")||(sl[k]=="FIXED"))){
					if (sl.size()>k+4){
					nld.status=netListDevicePlaced;
					QPoint p=QPoint(element::runden(sl[k+2].toDouble()*scale), element::runden(sl[k+3].toDouble()*scale));
					QPoint max=QPoint(0,0);
					QPoint min=QPoint(0,0);
					if (cHelp!=NULL){
						cHelp->deselectAll();
						cHelp->selectLayer(setup::lefCellBoundaryLayer);
						max=QPoint(0,0);
						min=QPoint(0,0);
						cHelp->minimumSelect(&min);
						cHelp->maximumSelect(&max);
						}
					e->setPos(p);
					if (sl[k+5]=="N"){
						e->move(-min);
						}
					else if (sl[k+5]=="E"){
						e->rotate(270);
						e->move(QPoint(-min.y(),max.x()));
						}
					else if (sl[k+5]=="W"){
						e->rotate(90);
						e->move(QPoint(max.y(),-min.x()));
						}
					else if (sl[k+5]=="S"){
						e->rotate(180);
						e->move(QPoint(max.x(),max.y()));
						}
					else if (sl[k+5]=="FN"){
						e->rotate(180);
						e->setMirrorx();
						e->move(QPoint(max.x(),-min.y()));
						}
					else if (sl[k+5]=="FS"){
						e->setMirrorx();
						e->move(QPoint(-min.x(),max.y()));
						}
					else if (sl[k+5]=="FW"){
						e->rotate(270);
						e->setMirrorx();
						e->move(QPoint(-min.y(),min.x()));
						}
					else if (sl[k+5]=="FE"){
						e->rotate(90);
						e->setMirrorx();
						e->move(QPoint(max.y(),max.x()));
						}
					}
				}
				else if (sl[k]=="+") command="new";
				else command="";
			}
			netlist.devices<<nld;
			}
		}
		else if (sectionType=="PINS"){
			s=readAll(&ts,s);
			if (layout::debug) printf("pins process: %s\n",s.toAscii().data());
			QStringList sl=s.split(seper,QString::SkipEmptyParts);
			if (sl.size()>1)
				externalNodes<<sl[1];
		}
		else if (sectionType=="NETS"){
			s=readAll(&ts,s);
			if (layout::debug) printf("nets process: %s\n",s.toAscii().data());
			QStringList sl=s.split(seper,QString::SkipEmptyParts);
			QString mode="net";
			pointArray pa;
			QPoint p,pLast;
			pLast=QPoint(0,0);
			width=-1;
			QPoint addPoint=QPoint(0,0);
			int node=-1;
			int newLayer=0;
			if (sl.size()>1) {
				node=netlist.addNode(sl[1]);
				if (externalNodes.contains(sl[1]))
					netlist.setNodeExternal(sl[1]);
				}
					
			for (int k=2;k<sl.size();){
			int slSize=sl.size();
			//printf("process %s (mode %s)\n",sl[k].toAscii().data(),mode.toAscii().data());
			if (sl[k]=="+") {
				if (k+1>=slSize) throw QString("File corrupt!");
				//printf("process + %s (layer %d)\n",sl[k+1].toAscii().data(),layer);
				endElement(cell_,mode,pa,layer);
				pa.resize(0);
				if ((sl[k+1]=="ROUTED")||(sl[k+1]=="FIXED")||(sl[k+1]=="COVER")) {
					if (k+2>=slSize) throw QString("File corrupt!");
					mode="route";
					layer=getLayer(sl[k+2]);
					k+=3;
					bool ok;
					width=element::runden(sl[k].toInt(&ok)*scale);
					if (ok) k++; 
					else width=-1;
					}
				else if (sl[k+1]=="SHAPE"){
					if (k+2>=slSize) throw QString("File corrupt!");
					if ((sl[k+2]=="STRIPE")||(sl[k+2]=="RING")|| (sl[k+2]=="PADRING")||(sl[k+2]=="BLOCKWIRE")|| (sl[k+2]=="FOLLOWPIN")||(sl[k+2]=="IOWIRE")|| (sl[k+2]=="FILLWIRE")||(sl[k+2]=="BLOCKAGEWIRE")|| (sl[k+2]=="DRCWIRE")) {
						k+=3;
						mode="route";
						}
					else k++;
				}
				else if (sl[k+1]=="RECT"){
					if (k+2>=slSize) throw QString("File corrupt!");
					layer=getLayer(sl[k+2]);
					k+=3;
					mode="rect";
				}
				else if (sl[k+1]=="PATTERNNAME"){
					if (k+2>=slSize) throw QString("File corrupt!");
					k+=3;
					mode="ignore";
				}
				else if (sl[k+1]=="POLYGON"){
					if (k+2>=slSize) throw QString("File corrupt!");
					layer=getLayer(sl[k+2]);
					k+=3;
					mode="polygon";
				}
				else k++;
			}
			else if (sl[k]=="CLEARADDPOINT") {
				pa.resize(0);
				pa<<addPoint;
				pLast=addPoint;
				addPoint=QPoint(0,0);
				//printf("newLayer %d->%d\n",layer,newLayer);
				layer=newLayer;
				mode="route";
				k++; 
			}
			else if (sl[k]=="NEW") {
				if (k+1>=slSize) throw QString("File corrupt!");
				endElement(cell_,mode,pa,layer);
				mode="route";
				pa.resize(0);
				pLast=addPoint;
				layer=getLayer(sl[k+1]);
				k+=2;
				bool ok;
				width=element::runden(sl[k].toInt(&ok)*scale);
				if (ok) k++; 
				else width=-1;
			}
			else if (sl[k]==";") {
				endElement(cell_,mode,pa,layer);
				k++;
			}
			else if (sl[k]=="(") {
				if (k+3>=slSize) throw QString("File corrupt!");
				if ((mode=="route")||(mode=="rect")||(mode=="polygon")){
				p=pLast;
				if (sl[k+1]!="*") p.setX(element::runden(sl[k+1].toDouble()*scale)+addPoint.x());
				if (sl[k+2]!="*") p.setY(element::runden(sl[k+2].toDouble()*scale)+addPoint.y());
				pa<<p;
				pLast=p;
				k+=4;
				}
				else if (mode=="net"){
					int deviceNum=netlist.getDeviceNum(sl[k+1]);
					if (deviceNum>=0){
						netlist.devices[deviceNum].addConnection(sl[k+2],node);
					}
					k+=4;
					
				}
				else k++;
			}
			else {
				if (mode=="route"){
				cell *cHelp=d->findCell(sl[k]);
				if (cHelp!=NULL) {
					endElement(cell_,mode,pa,layer);
					pa.resize(0);
					pa<<pLast;
					cell_->addCellref(cHelp,pLast);
					QList<int> layerList=viaMap.values(sl[k]);
					int oldLayer=layer;
					int pos=0;
					while ((oldLayer==layer)&&(pos<layerList.size())){
						layer=layerList.at(pos);
						pos++;
						//printf("check %d %d %d\n",oldLayer,layer,layerList.size());
						}
					//printf("%s %d %d\n",sl[k].toAscii().data(),oldLayer,layer);
					}
				else {
					QStringList sl2=viaContent.value(sl[k]);
					addPoint=pLast;
					for (int j=0;j<sl2.size();j++){
						sl.insert(k+1+j,sl2[j]);
						//printf("insert %s\n",sl2[j].toAscii().data());
						}
					sl.insert(k+1+sl2.size(),"CLEARADDPOINT");
					//printf("%d insert\n",layer );
					QList<int> layerList=viaMap.values(sl[k]);
					newLayer=layer;
					int pos=0;
					while ((newLayer==layer)&&(pos<layerList.size())){
						newLayer=layerList.at(pos);
						pos++;
						//printf("check %d %d %d\n",oldLayer,layer,layerList.size());
						}
					
					}
			 	k++;
				}
				else k++;
			}
			}
		}
		else if (sectionType=="VIAS"){
			s=readAll(&ts,s);
			if (layout::debug) printf("vias process: %s\n",s.toAscii().data());
			QStringList sl=s.split(seper,QString::SkipEmptyParts);
			
			for (int k=2;k<sl.size()-2;k++){
				if (sl[k]=="+") {
					if ((sl[k+1]=="RECT")||(sl[k+1]=="POLYGON")||(sl[k+1]=="PATH")){
						int layer=getLayer(sl[k+2]);
						if (layer>0) 
							if (layers::num[layer].layerType==layerTypeConductor){
							viaMap.insertMulti(sl[1],layer);
							//printf("maps via: %s %d \n",sl[1].toAscii().data(),layer);
							}
						}
					}
				}
			QString cellname=sl[1];
			sl.removeLast();
			sl.removeFirst();
			sl.removeFirst();
			viaContent.insert(cellname,sl);
		}
		else report.addItem(tr("can not process entry"),2,s);
	}
  }
  f.close();

  if (d->currentCell==NULL) d->findTopCell();
   // resolving brocken cellrefs
 if (layout::debug)
printf("def end main\n");
  		for (elementList *e=cell_->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){
				 	//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;}
			
		}
		cell_->clean();
// update remove double cell
if (typ==fileUpdate){
	QString cn=cell_->cellName;
	QString base=cn;
	cell_->cellName="NEW-DEF-UPDATE-LAYOUTEDiTOR";
	cell *c=d->findCell(cn);
	cellList *cl;
	if (c!=NULL) {
		for (cl=d->firstCell;cl!=NULL;cl=cl->nextCell){
			if ((cl->thisCell!=cell_)&&(cl->thisCell!=c))
				cl->thisCell->relink(c,cell_);
		}
		d->deleteCell(c);
		}
	netlist.cellname=cn;
	cell_->cellName=cn;
}
else if (typ==fileImport){
	QString cn=cell_->cellName;
	QString base=cn;
	int count=0;
	cell_->cellName="NEW-DEF-IMPORT-LAYOUTEDITOR";
	if (d->existCellname(cn)){
		do {
			count++;
			QString s;
			s.setNum(count);
			cn=base+s;
			}
		while (d->existCellname(cn));
		}
	netlist.cellname=cn;
	cell_->cellName=cn;
	}
 if (layout::debug)
printf("def end\n");
  if (d->currentCell==NULL) d->currentCell=d->firstCell->thisCell;

#ifdef printtime
  printf("def load: %d ms\n", setup::centralTimer.elapsed());
#endif
#ifdef netlistutility
  emit d->setNetlist(netlist);
#endif
  }
catch (QString s){
  report.addItem(tr("Aborted. "),0);
  report.addItem(s,1);
 }
  //report.showReport(); 
  QString s=report.getReport();
  d->showReport(s,report.getLastRang());
}


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

}

int def::getLayer(QString s){

int layer=layers::findLayer(s);
if (layer==-1) {
	layer=0;
	//printf("unknown layer %s\n",s.toAscii().data());
	}
return layer;
   
/*
			if (layer==-1) {
					int layerNum=1;
					while ( layerused[layerNum]) layerNum++;
					if (layerNum<layer_max){
						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;
			}
	*/	
	
}

int def::getWidth(int layer){
if (width>-1) return width;
if (layers::num[layer].layerType==layerTypeConductor)
	return layers::num[layer].getTypeParameter(1);
return setup::defaultPathWidth;
}

QString def::readAll(QTextStream *ts,QString s){
	while (s.at(s.size()-1)!=';'){
		s+=" "+ts->readLine().trimmed();
		}
return s;
}

void def::endElement(cell *cell_,QString type, pointArray pa, int layer){
if (type=="route"){
	if (pa.size()>1) {
		element *e=cell_->addPath(pa,layer);
		e->setWidth(getWidth(layer));
		e->setCap(2);
		}
	}
if (type=="rect"){
	if (pa.size()>1) {
		cell_->addBox(pa.point(0).x(),pa.point(0).y(), pa.point(1).x()-pa.point(0).x(),pa.point(1).y()-pa.point(0).y(),layer);
		}
	}
if (type=="polygon"){
	if (pa.size()>1) {
		cell_->addPolygon(pa,layer);
		}
	}
}

