/***************************************************************************
 *   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 "booleanhandler.h"
#include "elements/elementlist.h"
#include "elements/polygon.h"
#include "booleng.h"
#include "elements/pointarray.h"
#include "defines.h"
#include "layout.h"
#include "general/guiworkthread.h"
#include "elements/strans.h"
#include "elements/cellrefarray.h"

booleanHandler::booleanHandler(drawingField *d,layout *la)
 : QObject()
{
  init();
  l=la;
  drawing=d;
}

booleanHandler::booleanHandler(drawingField *d)
 : QObject()
{
  init();
  l=NULL;
  drawing=d;
  
}

booleanHandler::booleanHandler()
 : QObject(){
   init();
 }
 
 
void booleanHandler::init(){
  l=NULL;
  drawing=NULL;
  validA=false;
  validB=false;
  engine = new Bool_Engine();
  // set some global vals to arm the boolean engine
    double MARGE = 2;   // snap with in this range points to lines in the intersection routines
                          // should always be > DGRID  a  MARGE >= 10*DGRID is oke
                          // this is also used to remove small segments and to decide when
                          // two segments are in line.
    double CORRECTIONFACTOR = 0.1;  // correct the polygons by this number
    double CORRECTIONABER   = 1.0;    // the accuracy for the rounded shapes used in correction
    double ROUNDFACTOR      = 1.5;    // when will we round the correction shape to a circle
    double SMOOTHABER       = 1.0;   // accuracy when smoothing a polygon
    double MAXLINEMERGE     = 1.5; // leave as is, segments of this length in smoothen
    engine->SetMarge( MARGE );
    engine->SetCorrectionFactor( CORRECTIONFACTOR );
    engine->SetCorrectionAber( CORRECTIONABER );
    engine->SetSmoothAber( SMOOTHABER );
    engine->SetMaxlinemerge( MAXLINEMERGE );
    engine->SetRoundfactor( ROUNDFACTOR );
    engine->SetDGrid(1);
    engine->SetGrid(1);
}

booleanHandler::~booleanHandler()
{
delete engine;
}

void booleanHandler::setA(){
elementList *e=drawing->currentCell->firstElement;
polygon *p;
pointArray array;
for (;e!=NULL;e=e->nextElement) {
  if ((e->thisElement!=NULL)) 
  	if (e->thisElement->select){
		p=NULL;
		if (e->thisElement->isBox()) {
                    p=e->thisElement->convertToPolygon()->getPolygon();
		    e->thisElement->select=false;}
		if (e->thisElement->isPath())
		   if (e->thisElement->getWidth()>0) {
                    p=e->thisElement->convertToPolygon()->getPolygon();
		    e->thisElement->select=false;}
		if (e->thisElement->isPolygon()){
		    p=new polygon(e->thisElement->getPolygon());
		    e->thisElement->select=false;}
		if (p!=NULL)
		   add(GROUP_A,&p->pointarray);
		delete p; 
	}
  }
validA=true;
drawing->macroAdd("layout->booleanTool->setA();");
}

void booleanHandler::setB(){
elementList *e=drawing->currentCell->firstElement;
polygon *p;
pointArray array;
for (;e!=NULL;e=e->nextElement) {
  if ((e->thisElement!=NULL)) 
  	if (e->thisElement->select){
		p=NULL;
		if (e->thisElement->isBox()) {
                    p=e->thisElement->convertToPolygon()->getPolygon();
		    e->thisElement->select=false;}
		if (e->thisElement->isPath())
		   if (e->thisElement->getWidth()>0) {
                    p=e->thisElement->convertToPolygon()->getPolygon();
		    e->thisElement->select=false;}
		if (e->thisElement->isPolygon()){
		    p=new polygon(e->thisElement->getPolygon());
		    e->thisElement->select=false;}
		if (p!=NULL)
		      add(GROUP_B,&p->pointarray);
		delete p; 
	}
  }
validB=true;
drawing->macroAdd("layout->booleanTool->setB();");
}

void booleanHandler::add(GroupType g,pointArray *p){

 if (engine->StartPolygonAdd(g))
    {   
        for (int i=0; i<p->size();i++){
        engine->AddPoint(p->point(i).x(),p->point(i).y());
		//printf("p: %d ; %d\n",p->point(i).x()  , p->point(i).y());
		}
	
    }
    engine->EndPolygonAdd(); 
}

void booleanHandler::setA(pointArray *p){
	add(GROUP_A,p);
	validA=true;
}
void booleanHandler::setB(pointArray *p){
	add(GROUP_B,p);
	validB=true;
}

void booleanHandler::result(){
  validA=false;
  validB=false;
  pointArray p;
   while ( engine->StartPolygonGet() )
    {    p.resize(0);
        // foreach point in the polygon
        while ( engine->PolygonHasMorePoints() )
        {   
	    p.resize(p.size()+1);
	    p.setPoint(p.size()-1,(int)engine->GetPolygonXPoint(),(int)engine->GetPolygonYPoint());
          
        }
        engine->EndPolygonGet();
	if (p.size()>0) {
	  p.resize(p.size()+1);
	  p.setPoint(p.size()-1,p.point(0));
	  element *e;
	  e=drawing->currentCell->addPolygon(p,drawing->activeLayer);
	  e->select=true;
	  }
    }
} 

QList<pointArray> booleanHandler::resultingPointArrays(){
  QList<pointArray> list;
  validA=false;
  validB=false;
  pointArray p;
   while ( engine->StartPolygonGet() )
    {    p.resize(0);
        // foreach point in the polygon
        while ( engine->PolygonHasMorePoints() )
        {   
	    p.resize(p.size()+1);
	    p.setPoint(p.size()-1,(int)engine->GetPolygonXPoint(),(int)engine->GetPolygonYPoint());
          
        }
        engine->EndPolygonGet();
	if (p.size()>0) {
	  p.resize(p.size()+1);
	  p.setPoint(p.size()-1,p.point(0));
	  list<<p;
	  }
    }
  return list;
} 

/*pointArray booleanHandler::getAMinusB(){
  engine->Do_Operation(BOOL_A_SUB_B);
  validA=false;
  validB=false;
  pointArray p;
  while ( engine->StartPolygonGet() )
    {    p.resize(0);
        // foreach point in the polygon
        while ( engine->PolygonHasMorePoints() )
        {   
	    p.resize(p.size()+1);
	    p.setPoint(p.size()-1,(int)engine->GetPolygonXPoint(),(int)engine->GetPolygonYPoint());
          
        }
        engine->EndPolygonGet();
	if (p.size()>0) {
		p.resize(p.size()+1);
	    p.setPoint(p.size()-1,p.point(0));
		return p;
	  }
    }
  p.resize(2);
  p.setPoint(0,0,0);
  p.setPoint(1,0,0);
  return p;
}*/
	
QList<pointArray> booleanHandler::getAMinusB(){
  engine->Do_Operation(BOOL_A_SUB_B);
  return resultingPointArrays();
}

QList<pointArray> booleanHandler::getBMinusA(){
  engine->Do_Operation(BOOL_B_SUB_A);
  return resultingPointArrays();
}

QList<pointArray> booleanHandler::getAPlusB(){
  engine->Do_Operation(BOOL_OR);
  return resultingPointArrays();
}

QList<pointArray> booleanHandler::getAMultiB(){
  engine->Do_Operation(BOOL_AND);
  return resultingPointArrays();
}

QList<pointArray> booleanHandler::getAExorB(){
  engine->Do_Operation(BOOL_EXOR);
  return resultingPointArrays();
}

void booleanHandler::aPlusB(){
  if (!check()) return; 
  engine->Do_Operation(BOOL_OR);
  result();
  drawing->macroAdd("layout->booleanTool->aPlusB();");
}

void booleanHandler::aMinusB(){
  if (!check()) return; 
  engine->Do_Operation(BOOL_A_SUB_B);
  result();
  drawing->macroAdd("layout->booleanTool->aMinusB();");
}

void booleanHandler::bMinusA(){
  if (!check()) return;
  engine->Do_Operation(BOOL_B_SUB_A);
  result();
  drawing->macroAdd("layout->booleanTool->bMinusA();");
}

void booleanHandler::aMultiB(){
  if (!check()) return;
  engine->Do_Operation(BOOL_AND);
  result();
  drawing->macroAdd("layout->booleanTool->aMultiB();");
}
void booleanHandler::aExorB(){
  if (!check()) return;
  engine->Do_Operation(BOOL_EXOR);
  result();
  drawing->macroAdd("layout->booleanTool->aExorB();");
}

bool booleanHandler::check(){
//  engine->Activate();
  drawing->prepareUndo();
  if (validA&&validB) return true;
  return false;
}


void booleanHandler::setAGui(){
if (drawing->mutexReadGuiTryLock()) {
	setA();
	drawing->mutexReadUnlock();
	drawing->recountSelect();
	drawing->paint();
	}
}

void booleanHandler::setBGui(){
if (drawing->mutexReadGuiTryLock()) {
	setB();
	drawing->mutexReadUnlock();
	drawing->recountSelect();
	drawing->paint();
	}
}

void booleanHandler::aPlusBGui(){
#ifdef multithread
 if (l!=NULL) l->workThread->startOperation("boolAPlusB","");
 else
#else
   if ( drawing->mutexAddGuiTryLock()){
	aPlusB();
	drawing->mutexAddUnlock();
	drawing->paint();
	}
#endif
 ;
}

void booleanHandler::aMinusBGui(){
#ifdef multithread
if (l!=NULL) l->workThread->startOperation("boolAMinusB","");
 else
#else
  if ( drawing->mutexAddGuiTryLock()){
	aMinusB();
	drawing->mutexAddUnlock();
	drawing->paint();
	}
 
#endif
  ;
}

void booleanHandler::bMinusAGui(){
#ifdef multithread
if (l!=NULL) l->workThread->startOperation("boolBMinusA","");
 else
#else
if ( drawing->mutexAddGuiTryLock()){
	bMinusA();
	drawing->mutexAddUnlock();
	drawing->paint();
	}
#endif
 ;
}

void booleanHandler::aMultiBGui(){
#ifdef multithread
if (l!=NULL) l->workThread->startOperation("boolAMultiB","");
 else
#else
if ( drawing->mutexAddGuiTryLock()){
	aMultiB();
	drawing->mutexAddUnlock();
	drawing->paint();
	}
#endif
 ;
}

void booleanHandler::aExorBGui(){
#ifdef multithread
if (l!=NULL) l->workThread->startOperation("boolAExorB","");
 else
#else
if ( drawing->mutexAddGuiTryLock()){
	aExorB();
	drawing->mutexAddUnlock();
	drawing->paint();
	}
#endif
 ;
}

void booleanHandler::mergeSelectToActiveLayer(){
#ifdef multithread
if (l!=NULL) l->workThread->startOperation("boolMergeSelect","");
 else
#else
if (drawing->mutexAddGuiTryLock()) {
	mergeSelect(drawing->activeLayer);
	drawing->mutexAddUnlock();
	drawing->recountSelect();
	drawing->paint();
	}
#endif
 ;
}

void booleanHandler::mergeSelect(int layer){
// set elements
	{elementList *e=drawing->currentCell->firstElement;
	polygon *p;
	pointArray array;
	for (;e!=NULL;e=e->nextElement) {
	if ((e->thisElement!=NULL)) 
		if (e->thisElement->select){
			p=NULL;
			if (e->thisElement->isBox()) {
			p=e->thisElement->convertToPolygon()->getPolygon();
			e->thisElement->select=false;}
			if (e->thisElement->isPath())
			if (e->thisElement->getWidth()>0) {
			p=e->thisElement->convertToPolygon()->getPolygon();
			e->thisElement->select=false;}
			if (e->thisElement->isPolygon()){
			p=new polygon(e->thisElement->getPolygon());
			e->thisElement->select=false;}
			if (p!=NULL)
			add(GROUP_A,&p->pointarray);
			delete p; 
		}
	}}
//do operation
  engine->Do_Operation(BOOL_OR);
//get result
  pointArray p;
   while ( engine->StartPolygonGet() )
    {    p.resize(0);
        // foreach point in the polygon
        while ( engine->PolygonHasMorePoints() )
        {   
	    p.resize(p.size()+1);
	    p.setPoint(p.size()-1,(int)engine->GetPolygonXPoint(),(int)engine->GetPolygonYPoint());
          
        }
        engine->EndPolygonGet();
	if (p.size()>0) {
	  p.resize(p.size()+1);
	  p.setPoint(p.size()-1,p.point(0));
	  element *e;
	  e=drawing->currentCell->addPolygon(p,layer);
	  e->select=true;
	  }
    }
  QString s;
  s.setNum(layer);
  drawing->macroAdd("layout->booleanTool->mergeSelect("+s+");");
}

void booleanHandler::performAPlusB(){
  engine->Do_Operation(BOOL_OR);
  validA=false;
  validB=false;
}

void booleanHandler::performAMinusB(){
   engine->Do_Operation(BOOL_A_SUB_B);
  validA=false;
  validB=false;
}

void booleanHandler::performBMinusA(){
  engine->Do_Operation(BOOL_B_SUB_A);
  validA=false;
  validB=false;
}

void booleanHandler::performAMultiB(){
  engine->Do_Operation(BOOL_AND);
  validA=false;
  validB=false;
}

void booleanHandler::performAEorB(){
  engine->Do_Operation(BOOL_EXOR);
  validA=false;
  validB=false;
}

pointArray booleanHandler::getResultingPolygon(){
  validA=false;
  validB=false;
  pointArray p;
  p.resize(0);
  if ( engine->StartPolygonGet() )
    {    p.resize(0);
        // foreach point in the polygon
        while ( engine->PolygonHasMorePoints() )
        {   
	    p.resize(p.size()+1);
	    p.setPoint(p.size()-1,(int)engine->GetPolygonXPoint(),(int)engine->GetPolygonYPoint());
          
        }
        engine->EndPolygonGet();
	if (p.size()>0) {
	  p.resize(p.size()+1);
	  p.setPoint(p.size()-1,p.point(0));
	  }
    }
  return p;
}

void booleanHandler::setResultToA(){
 validA=true;
 QList<pointArray> list=resultingPointArrays();
 for (int i=0;i<list.size();i++){
	addA(list.at(i));
	}
}

void booleanHandler::setResultToB(){
 validB=true;
 validA=true;
 QList<pointArray> list=resultingPointArrays();
 for (int i=0;i<list.size();i++){
	addB(list.at(i));
	}
}

void booleanHandler::addA(pointArray pointarray){
 validA=true;
 add(GROUP_A,&pointarray);
}

void booleanHandler::addB(pointArray pointarray){
 validB=true;
 add(GROUP_B,&pointarray);
}


void booleanHandler::addLayerA(int layer){
validA=true;
strans trans;
trans.reset();
add(GROUP_A,trans,drawing->currentCell,layer);
}

void booleanHandler::addLayerB(int layer){
validB=true;
strans trans;
trans.reset();
add(GROUP_B,trans,drawing->currentCell,layer);
}

void booleanHandler::add(GroupType g, strans trans, cell *c, int layer){
elementList *e=c->firstElement;
polygon *p=NULL;
pointArray array;
for (;e!=NULL;e=e->nextElement) {
  if ((e->thisElement!=NULL)) {
	if (e->thisElement->isBox()){
		if (e->thisElement->layerNum==layer) {
		p=e->thisElement->convertToPolygon()->getPolygon();
		array=p->getPoints();
		array.map(trans);
		add(g,&array);
		delete p;
		}
	}
	else if (e->thisElement->isPath()) {
		if (e->thisElement->layerNum==layer) 
		if (e->thisElement->getWidth()>0){
		p=e->thisElement->convertToPolygon()->getPolygon();
		array=p->getPoints();
		array.map(trans);
		add(g,&array);
		delete p;
		}
	}
	else if (e->thisElement->isPolygon()){
		if (e->thisElement->layerNum==layer) {
			array=e->thisElement->getPoints();
			array.map(trans);
			add(g,&array);
			}
	}
	else if (e->thisElement->isCellref()){
		cellref *cr=e->thisElement->getCellref();
		strans matrix=trans;
		matrix.translate(cr->point.x(),cr->point.y());
		if (cr->trans.mirror_x){matrix.toggleMirror_x();};
		matrix.rotate(cr->trans.angle);
		matrix.scale(cr->trans.mag);
		add(g,matrix,cr->cell_ref,layer);
	}
	else if (e->thisElement->isCellrefArray()){
		cellrefArray *cr=e->thisElement->getCellrefArray();
		for (int x=0;x<cr->anz_x;x++) {
            		for (int y=0;y<cr->anz_y;y++) {
                		QPoint point=cr->point+cr->space_x*x+cr->space_y*y;
				strans matrix=trans;
				matrix.translate(point.x(),point.y());
				if (cr->trans.mirror_x){matrix.toggleMirror_x();};
				matrix.rotate(cr->trans.angle);
				matrix.scale(cr->trans.mag);
				add(g,matrix,cr->cell_ref,layer);
			}
		}
	}
	}
  }
}

void booleanHandler::setLayerAGui(){
if (drawing->mutexReadGuiTryLock()) {
	addLayerA(drawing->activeLayer);
	drawing->mutexReadUnlock();
	drawing->paint();
	}
drawing->macroAdd("layout->booleanTool->addLayerA("+drawing->str(drawing->activeLayer)+");");
}

void booleanHandler::setLayerBGui(){
if (drawing->mutexReadGuiTryLock()) {
	addLayerB(drawing->activeLayer);
	drawing->mutexReadUnlock();
	drawing->paint();
	}
drawing->macroAdd("layout->booleanTool->addLayerB("+drawing->str(drawing->activeLayer)+");");
}
