/***************************************************************************
 *   Copyright (C) 2009 by Juergen Thies                                   *
 *   layout@juergenthies.de                                                *
 *                                                                         *
 ***************************************************************************/
#define _USE_MATH_DEFINES
#include "odb.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 <QFileInfo>
#include "general/errorreport.h"
#include "layout.h"
#include "macro/macro.h"
#include "filedialog.h"
#include <QDir>


odb::odb(QObject *parent, const char *)
 : QObject(parent)
{

}


odb::~odb()
{
}


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


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

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

void odb::load(QString fileName,drawingField *d, fileOpenType typ){
 
  if (typ==fileImport) {
  	report.setTitle(tr("Import of ODB++-File \"")+fileName+"\"");}
  else  if (typ==fileOpen){
	report.setTitle(tr("Open of ODB++-File \"")+fileName+"\"");}
  else  if (typ==fileUpdate){
	report.setTitle(tr("Update with ODB++-File \"")+fileName+"\"");}
try { 
 report.addItem(tr("ODB++ import is still in beta development status. Please use with care!"),2);
#ifdef printtime
  setup::centralTimer.start();
#endif
  cellList *firstcellhelp=d->firstCell;
  if (typ==fileOpen){
	d->databaseunits=0.000000001;
 	d->userunits=0.001;}
  QFile f( fileName );
  if ( !f.open( QIODevice::ReadOnly ) )
	  throw QString(tr("Can not open File: %1").arg(fileName));
  if ((typ==fileImport)||(typ==fileUpdate)) {
	d->firstCell=NULL;
  }
  QStringList layer;
  QStringList cells;
  QTextStream ts( &f );
  QString s="";
  int type=-1;
  QString name="";
  int row=0;
  while (!ts.atEnd()){
    s=ts.readLine().trimmed();
    if (s.left(4)=="STEP"){
	type=1;
	row=0;
	name="noname";
	}
    else if (s.left(5)=="LAYER"){
	type=2;
	row=0;
	name="noname";
	}
    else if (s.left(3)=="ROW"){
	int i=s.indexOf("=");
	if (i>0) row=s.mid(i+1).trimmed().toInt();
	}
    else if (s.left(4)=="NAME"){
	int i=s.indexOf("=");
	if (i>0) name=s.mid(i+1).trimmed();
	}
     else if (s.indexOf("}")>=0){
	if (type==1){
		cells<<name;
	}
	else if (type==2){
		QString l;
		l.setNum(row);
		if ((row>=0)&&(row<layersMax)){
			layers::num[row].name=name;
			layer<<name;
		}
		else
		report.addItem(tr("Illegal layer row."),1,l);
	}
	type=-1;
	}
  }

  f.close();

  QString fn=fileName.left(fileName.length()-14);

  for (int ic=0;ic<cells.size();ic++){
	cellList *cell_list=NULL;
  	cell *cell_=NULL;
	cell_list=d->addCell();
	cell_=cell_list->this_cell;
	cell_->cellName=cells[ic];
  	for (int il=0;il<layer.size();il++){
		QString fileName=fn+"/steps/"+cells[ic]+"/layers/"+layer[il]+"/features";
		QFile f( fileName );
		if (layout::debug) printf("load %s\n",fileName.toAscii().data());
		if ( !f.open( QIODevice::ReadOnly ) ) {
			report.addItem(tr("Can not open File."),1,fileName);
			break;
			}
		int activeLayer=layers::findLayer(layer[il]);
		if (activeLayer<0) {
			activeLayer=0;
			report.addItem(tr("Unknown layer"),1,layer[il]);
			}
		QTextStream ts( &f );
		QString s="";
		pointArray pa;
		double scale=(double)(1.0)/d->databaseunits/100*2.54;
  		QRegExp seper=QRegExp("[ \t]");
		QMap<QString,QString> aperturtes;
		while (!ts.atEnd()){
			s=ts.readLine().trimmed();
			if (s.left(1)=="#"){ //ignore comment
			}
			else if(s.left(1)=="U"){
				QStringList sl=s.split(seper,QString::SkipEmptyParts);
				if (sl.size()>1) 
					if (sl[1].trimmed()=="MM")scale=(double)(1.0)/d->databaseunits/1000;
				
			}
			else if(s.left(1)=="$"){
				QStringList sl=s.mid(1).split(seper,QString::SkipEmptyParts);
				if (sl.size()>1) aperturtes.insert(sl[0],sl[1]);
			}
			else if(s.left(1)=="S"){//ignore
			}
			else if(s.left(1)=="L"){
				QStringList sl=s.mid(1).split(seper,QString::SkipEmptyParts);
				if (sl.size()>5){
					pa.resize(2);
					pa.setPoint(0,getPos(sl[0],scale),getPos(sl[1],scale));
					pa.setPoint(1,getPos(sl[2],scale),getPos(sl[3],scale));
					QString aper=aperturtes.value(sl[4]);
					if (aper.left(1)=="r"){
						int width= element::runden(aper.mid(1).toDouble()*scale/1000);
						element *e=cell_->addPath(pa,activeLayer);
						e->setWidth(width);
						e->setCap(1);
					}					
					else if (aper.left(1)=="s"){
						int width= element::runden(aper.mid(1).toDouble()*scale/1000);
						element *e=cell_->addPath(pa,activeLayer);
						e->setWidth(width);
						e->setCap(2);
					}else report.addItem(tr("unsupported apertur"),2,s);
				}
			}
			else if(s.left(1)=="P"){
				QStringList sl=s.mid(1).split(seper,QString::SkipEmptyParts);
				if (sl.size()>3){
					QPoint p=QPoint(getPos(sl[0],scale),getPos(sl[1],scale));
					QString aper=aperturtes.value(sl[4]);
					if (aper.left(1)=="r"){
						int width= element::runden(aper.mid(1).toDouble()*scale/1000);
						cell_->addCircle(activeLayer,p,width/2);
					}else report.addItem(tr("unsupported apertur"),2,s);
				}
			}
			else if(s.left(1)=="T"){
				QStringList sl=s.split(seper,QString::SkipEmptyParts);
				int i=s.indexOf("'");
				int k=s.indexOf("'",i+1);
				QString text="";
				if ((i>0)&&(k>0)) text=s.mid(i+1,k-i-1);
				if ((sl.size()>2)&&(text!=""))
					cell_->addText(activeLayer,QPoint(getPos(sl[1],scale),getPos(sl[2],scale)),text);
				
			}
			else if(s.left(2)=="OB"){
				pa.resize(1);
				QStringList sl=s.split(seper,QString::SkipEmptyParts);
				if (sl.size()>=4){
					pa.setPoint(0,getPos(sl[1],scale),getPos(sl[2],scale));
					if (sl[3].trimmed()!="I") report.addItem(tr("unsupported polygon type"),2,sl[3]);
					}
				else report.addItem(tr("wrong format"),2,s);
			}
			else if(s.left(2)=="OS"){
				QStringList sl=s.split(seper,QString::SkipEmptyParts);
				if (sl.size()>=3){
					pa.resize(pa.size()+1);
					pa.setPoint(pa.size()-1,getPos(sl[1],scale),getPos(sl[2],scale));
				}
				else report.addItem(tr("wrong format"),2,s);
			}
			else if(s.left(24)=="OE"){
				cell_->addPolygon(pa,activeLayer);
			}
			else report.addItem(tr("Unkown entry"),2,s);
		
		}
		f.close();
		}
	}



  // 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){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;
					//printf("addCell %s\n",e->thisElement->getName().toAscii().data());
  					c=d->addCell();
  					c->this_cell->cellName=e->thisElement->getName();
					e->thisElement->setCellRef(d->findCell(e->thisElement->getName()));
					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("odb++ 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 odb::getPos(QString s,double scale){
double d=s.toDouble()*scale;
//printf("%s -> %d\n",s.toAscii().data(),element::runden(d));
return element::runden(d);
}



void odb::save(QString fileName,drawingField *d){
errorreport report;
report.setTitle(tr("Save of odb++ -File \"")+fileName+"\"");
try { 
  QString fn=fileName.left(fileName.length()-14);
  QDir dir=QDir(fn);
  //printf("fn/filename %s----%s\n",fn.toAscii().data(),fileName.toAscii().data());
  if (!dir.exists()) {
	int pos=fn.lastIndexOf("/"); 
	dir.setPath(fn.left(pos));
	//printf("create dir %s---%s\n",fn.mid(pos+1).toAscii().data(),fn.left(pos).toAscii().data());
	dir.mkdir(fn.mid(pos+1));
	dir.setPath(fn);
	}
  QDir dir2=QDir(fn+"/matrix");
  if (!dir2.exists()) {
	dir.mkdir("matrix");
	//printf("create dir matrix\n");
	}
  dir2=QDir(fn+"/steps");
  if (!dir2.exists()) {
	dir.mkdir("steps");
	//printf("create dir steps\n");
	}
  d->prepareUndo();
  d->currentCell->selectAll();
  elementCount ec1= d->currentCell->countSelect();
  d->currentCell->flatAllSelect();
  d->currentCell->deselectAll();
  if (ec1.cellref+ec1.cellrefarray!=0) {
	report.addItem(tr("No hierachy in odb++, current cell is flatten"),2,"");
	d->recountSelect();
	}
  QFile f( fn+"/matrix/matrix" );
  if ( !f.open( QIODevice::WriteOnly ) ) {
	throw QString(tr("Can not open File."));
  }
  QTextStream stream( &f );
  QString cellname=odb::convert(d->currentCell->cellName);
  stream<<"STEP {\n";
  stream<<"COL=1\n";
  stream<<"NAME="<<cellname<<"\n";
  stream<<"}\n";
  dir=QDir(fn+"/steps");
  dir2=QDir(fn+"/steps/"+cellname);
  if (!dir2.exists()) {
	dir.mkdir(cellname);
	}
  dir=QDir(fn+"/steps/"+cellname+"/layers");
  if (!dir.exists()) {
	dir2.mkdir("layers");
	}
  int numLayer=0;
  bool layerused[layersMax];
  for (int i=0; i<layersMax;i++){
  	 if ((d->useLayer(i))&&(layers::num[i].use==true)) {
	 	numLayer++;
		layerused[i]=true;
		QString srow;
		srow.setNum(i);
		stream<<"LAYER {\n";
  		stream<<"ROW="<<srow<<"\n";
		if ((i==4)||(i==5)||((i>=20)&&(i<=67)))
			stream<<"CONTEXT=BOARD\n";
		else
			stream<<"CONTEXT=MISC\n";
		switch (i){
		case 28: case 30: case 32: case 34: case 36: case 38: case 40: case 42: case 44: case 46: case 48: case 50: case 52: case 54: case 56: case 58: case 60:
			stream<<"TYPE=SIGNAL\n";
			break;
		case 20: case 67: 
			stream<<"TYPE=SILK_SCREEN\n";
			break;
		case 27: case 29: case 31: case 33: case 35: case 37: case 39: case 41: case 43: case 45: case 47: case 49: case 51: case 53: case 55: case 57: case 59:
			stream<<"TYPE=DRILL\n";
			break;
		default:
			stream<<"TYPE=MASK\n";
		}
  		stream<<"NAME="<<convert(layers::num[i].name)<<"\n";
		stream<<"POLARITY=POSITIVE\n";
		switch (i){
		case 27: 
			stream<<"START_NAME=top\n";
			stream<<"END_NAME=bottom\n";
			break;
		case 29: 
			stream<<"START_NAME=top\n";
			stream<<"END_NAME=layer1\n";
			break;
		case 31: 
			stream<<"START_NAME=layer1\n";
			stream<<"END_NAME=layer2\n";
			break;
		case 33: 
			stream<<"START_NAME=layer2\n";
			stream<<"END_NAME=bottom\n";
			break;
		default:
			;
		}
  		stream<<"}\n";  
		}
	 else layerused[i]=false;
	 }

  f.close();

  f.setFileName( fn+"/steps/"+cellname+"/stephdr");
  if ( !f.open( QIODevice::WriteOnly ) ) {
	throw QString(tr("Can not open File."));
  }
 {
  QTextStream stream( &f );
  stream<<"TOP_ACTIVE=1\n";
  stream<<"BOTTOM_ACTIVE=1\n";
  stream<<"RIGHT_ACTIVE=1\n";
  stream<<"LEFT_ACTIVE=1\n";
	}
  f.close();

  //printf("%s\n",QDir::currentPath().toAscii().data());
  for (int i=0; i<layersMax;i++){
  	 if (layerused[i]==true){
		QString layername=odb::convert(layers::num[i].name);
		dir2=QDir(fn+"/steps/"+cellname+"/layers/"+layername);
  		if (!dir2.exists()) {
			dir.mkdir(layername);
		}
		f.setFileName( fn+"/steps/"+cellname+"/layers/"+layername+"/features");
		if ( !f.open( QIODevice::WriteOnly ) ) {
			throw QString(tr("Can not open File."));
		}
		QTextStream stream( &f );
		odb odbClass;
		odbClass.save(&stream,d,&report,i);
		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());

}

QString odb::convert(QString s){
s=s.toLower();
s.remove(" ");
return s;
}


void odb::save(QTextStream *streamPtr, drawingField *d , errorreport *e ,int layer){
	/*	if (com=="MOIN") unit="inch";
		else if (com=="MOMM") unit="mm";*/
  *streamPtr<<"# ODB++ file exported by LayoutEditor (http://www.layouteditor.net)\r\n";
  symbols.clear();
  reportSave=e;
  saveLayer=layer;
  stream=streamPtr;
  scale=d->databaseunits*100/2.54;
  d->currentCell->saveODB(this);
}

void odb::writePointArray(pointArray pa){
if (pa.size()<2) return;
*stream<<"S P 0\n";
*stream<<"OB ";
writePoint(pa.point(0));
*stream<<"I\n";
for (int i=1;i<pa.size();i++){
	*stream<<"OS ";
	writePoint(pa.point(i));
	*stream<<"\n";
}
*stream<<"OE\n";
*stream<<"SE\n";
}

void odb::writePoint(QPoint p ){
 QString sx,sy;
 sx.setNum(scale*p.x(),'f');
 sy.setNum(scale*p.y(),'f');
 *stream<<sx<<QString(" ")<<sy<<QString(" ");
}
int odb::searchSymbol(QString s){
if (symbols.contains(s)){
	return symbols.indexOf(s);
	}
symbols<<s;
QString s1;
s1.setNum(symbols.size()-1);
*stream<<"$"<<s1<<" "<<s<<"\n";
return symbols.size()-1;
}

int odb::getSymbolCircle(int width){
QString s,sx;
sx.setNum(scale*width*1000,'f');
s="r"+sx;
return searchSymbol(s);
}

int odb::getSymbolRect(int width){
QString s,sx;
sx.setNum(scale*width*1000,'f');
s="s"+sx;
return searchSymbol(s);
}
