/***************************************************************************
 *   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.             *
 ***************************************************************************/
#ifndef CELL_H
#define CELL_H


#include <qstring.h>
#include <QStringList>
#include "elements/pointarray.h"
#include <QTextStream>
#include "elementlist.h"
#include "general/drawpixmap.h"
#include <qdatetime.h>
#include "widgets/selectwidget.h"
#include "layertranslator.h"
#include "propertylist.h"
#ifdef USE_3d
#include "3d/dxf3d.h"
#endif
/**
@author Juergen Thies
*/
//#define first_element firstElement
class oasis;
class cif;
class gds;
class cellList;
class svg;
class csv;
class odb;
class eps;
class dxf;
class source;
class drawingField;

//! a single cell
/*!
This class holds all information relevant for a single cell.
*/
class cell {
private:
	QPoint paintInfoMin,paintInfoMax;
	char statusPaintInfo;
	uint paintInfoElements;
	uint paintInfoCellElements;
	qint16 defaultLayer;
	qint16 getDefaultLayer(){
		if (defaultLayer>-2) return defaultLayer;
		if (firstElement!=NULL) defaultLayer=firstElement->getDefaultLayer();
		else defaultLayer=-1;
		return defaultLayer;
		}
	void paintInfoCalc();
	void paintInfoMinMax();
public:
	void paintInfoClear(){statusPaintInfo=0;}
	void paintInfoGet(QPoint *,QPoint *,uint *);
public:
    cell();
    bool saved, marker;
    QStringList markerList;
    QList<int> markerInt;
    QList<propertyItem> cellData;
    ~cell();
    int countElements();
    void markDepend();
    void removeDepend(QHash<cell*,bool> *l);
    void removeDependSelectMultilevel(QHash<cell*,bool> *l);
    void removeDependMultilevel(QHash<cell*,bool> *l);
    void countPrepare(){markerInt.clear();}
    int countDepend();
    void listDepend();
//    void listDependMultilevel(cellList *,QStringList *);
//    void listDependMultilevelSelect(cellList *,QStringList *);
//! list of all element
/*!
This is a list of all elements in this cell.
*/
    elementList *firstElement;
    QDate date_modification,date_access;
    QTime time_modification,time_access;
//! cell name
/*!
The name of this cell.
*/
    QString cellName;
    void paint(layoutImagePainter *e, bool mainCell=false);
    void paintSelect(layoutImagePainter *e);
    void paintSelected(layoutImagePainter *e);
    void paintBoundingRec(layoutImagePainter *e,QRgb color);
	void paintNode(int node, layoutImagePainter *e);
	void paintDevice(QString device, layoutImagePainter *e);
	void paintDeviceNode(QString device,int node, layoutImagePainter *e);
    void paintHighlighted(QPainter *po,strans trans_);
    void paintHighlightedBoundingRec(QPainter *po,strans trans_);
//! form select
/*!
Selects a complete element, if one point is inside rect.
*/ 
    void fSelect(QRect select);
//! form select layer
/*!
Selects a complete element, if one point is inside rect and it is on layer
*/
    void fSelectLayer(QRect select, int layer);
//! form all select
/*!
Selects a complete element, if all points are inside rect.
*/ 
    void fAllSelect(QRect select);
//! point select
/*!
Selects point in the rect.
*/ 
    void pSelect(QRect select);
//! cell select
/*!
Selects a cell, if the base point is in the rect.
*/ 
    void cSelect(QRect select);
//! form deselect
/*!
Deselects a complete element, if one point is inside rect.
*/
    void fDeselect(QRect select);
//! form deselect layer
/*!
Deselects a complete element, if one point is inside rect and it is on layer
*/
    void fDeselectLayer(QRect select, int layer);
//! form all deselect
/*!
Deselects a complete element, if all points are inside rect.
*/
    void fAllDeselect(QRect select);
//! point deselect
/*!
Deselects point in the rect.
*/
    void pDeselect(QRect select);
//! cell deselect
/*!
Deselects a cell, if the base point is in the rect.
*/ 
    void cDeselect(QRect select);
//!  select all
/*!
All elements are selected.
*/ 
    void selectAll();
//! invert select
/*!
The selection of this cell is inverted.
*/
    void invertSelect();
//! select visible
/*!
All visible elements are selected.
*/ 
    void selectVisible();
//! deselect all
/*!
All elements are deselected.
*/ 
    void deselectAll();
//! path select
/*!
All visible path elements are selected.
*/
void pathSelectVisible();
//! path deselect
/*!
All path elements are deselected.
*/
void pathDeselect() ;
//! box select
/*!
All visible boxes are selected.
*/
void boxSelectVisible() ;
//! box deselect
/*!
All boxes are deselected.
*/
void boxDeselect() ;
//! polygon select
/*!
All visible polygons are selected.
*/
void polygonSelectVisible() ;
//! polygon deselect
/*!
All polygons are deselected.
*/
void polygonDeselect();
//! select text 
/*!
All text elements with 'text' are selected.
*/
void selectText(QString text);
//! deselect text 
/*!
All text elements with 'text' are deselected.
*/
void deselectText(QString text);
//! text select
/*!
All visible text elements are selected.
*/
void textSelectVisible();
//! text deselect
/*!
All text elements are deselected.
*/
void textDeselect();
//! select cellref
/*!
All cellref to cells named cellname are selected
*/
void selectCellref(QString cellname);
//! select Layer
/*!
All elements on layer are selected.
*/
void selectLayer(int layer);
//! deselect layer
/*!
All elements on layer are deselected.
*/
void deselectLayer(int layer);
//! select node
/*!
All elements connected with node are selected.
*/
void selectNode(int node);
//! deselect node
/*!
All elements connected with node are deselected.
*/
void deselectNode(int node);
//! select node on layer
/*!
All elements on layer connected with node are selected
*/
void selectNodeOnLayer(int node,int layer);
//! deselect node on layer
/*!
All elements on layer connected with node are sdeelected
*/
void deselectNodeOnLayer(int node,int layer);
//! map layer
/*!
all elements in this cell are maped with layerTranslator t.
*/
 void mapLayer(layerTranslator *t);
 //! rotate select elements
/*!
Selected elements are rotated.
@param angle	angle counterclock
@param pos	center of rotation
*/
    void rotateSelect(double angle,QPoint pos);
//! mirror select elements
/*!
Selected elements are mirror at a Line made by p1 and p2.
If p1==p2, it is mirrored at this single point.
*/
    void mirrorSelect(QPoint p1,QPoint p2);
//! scale select elements
/*!
Selected elements are scaled.
p2 is scale to p3.
@param p1	orgin
@param p2	old point
@param p3	new point
*/
    void scaleSelect(QPoint p1,QPoint p2,QPoint p3);
//! move to layer
/*!
Selected elements are moved to the layer
@param layer	new layer
*/
    void moveToLayerSelect(int layer);
//! copy select
/*!
Selected elements are copyed and move by p
@param p	movement
*/
    void copySelect(QPoint p=QPoint(0,0));
//! delete references
/*!
Cellrefs and cellrefarrays on the cell c are deleted.
@param *c	cell
*/
    void deleteRefs(cell *c);
//! flatten
/*!
Selected cellrefs and cellrefarrays are flatten.
*/
    void flatSelect();
//! flatten multilevel
/*!
Selected cellrefs and cellrefarrays are flatten. If the the cellrefs contain more cellrefs, they are flatten as well.
*/
    void flatAllSelect();
//! flatten cellrefarray
/*!
all cellrefarrays are flatten.
*/
    void flatCellrefArray();
//! delete select
/*!
Selected element are deleted.
*/
    void deleteSelect();
//! move select
/*!
Selected element are moved by pos
*/
    void moveSelect(QPoint pos);
//! move
/*!
All element are moved by pos
*/
    void move(QPoint pos);
//! cut
/*!
Cut selected elements between p1 and p2.
*/
    void cutSelect(QPoint p1, QPoint p2);
//! adjust size
/*!
Selected elements grow/shrink with value.
*/ 
    void sizeadjustSelect(int value);
//! adjust size
/*!
Selected elements grow/shrink with different values in x and y.
*/ 
    void sizeadjustSelect(int valueX, int valueY);
//! modify corners
/*!
All corners of selected elements will be modified. A addition area will be added/substracted in the corner region. The amount of the area will be calculation in dependence with the corner angles. sharp corner will add more area than small angles. The first parameter will give the squart of amount for a 90° angle. The second parameter will modify the shape of the added area.
*/ 
    void modifyCornersSelect(double value1,double value2);
//! crop with selection
/*!
Selected elements are used to crop the complete cell.
*/ 
    void cropWithSelection();
//! convert to mesh
/*!
Selected elements are converted to a mesh of path elements.
*/ 
    void toMeshSelect(int width,int spaceing);
//! merge
/*!
Selected elements on the same layer are merged.
*/    
    void mergeSelect();
//! round
/*!
Selected elements/point are rounded to a multiply of i.
*/
    void roundSelect(int i);
//! snap 
/*!
Selected elements/point will snap to near elements with a search radius of i.
*/
    void snapShapeSelect(int i);
//! set datatype select
/*!
The datatype of selected element is set to i
*/
    void setDatatypeSelect(int i);
//! select datatype
/*!
All element with the datatype i are selected.
*/
    void selectDatatype(int i);
//! remove edges
/*!
Edges smaller than i are removed.
*/
    void edgeRemoveSelect(int i);
//! crop sharp angles
/*!
Angles smaler than 90° are removed by inserting additional points
*/
    void cropSharpAnglesSelect(int i);
//! area
/*!
The area of all selected elements is calculated.
*/
    double areaSelect();
//! area
/*!
The circumference of all selected elements is calculated.
*/
    double circumferenceSelect();
//! area
/*!
The circumference of all elements is calculated.
*/
    double circumferenceSelected();
//! area
/*!
The area of all elements is calculated.
*/
    double areaSelected();
//! nearest element
/*!
@return the nearest element to the point p.
*/
    elementList* nearestElement(QPoint p);
	elementList* nearestElement(QPoint p,QPoint *point);
    elementList* nearestForm(QPoint p);
    elementList* nearestCell(QPoint p);
    elementList* nearestCell(QPoint p,QList<element*> exclude);
    elementList* identicalPoint(elementList *elem,QPoint point);
    elementList* identicalElementPoint(elementList *elem,QPoint point);
    elementList* nearestPointVirtual(QPoint p,QPoint *point);
    elementList* nearestPoint(QPoint p,QPoint *point);
    QPoint nearestPoint(QPoint p, int searchradius);
    QPoint nearestLine(QPoint p, int searchradius);
    QPoint nearestMiddleLine(QPoint p, int searchradius);
    QPoint nearestCenter(QPoint p, int searchradius);
    QPoint nearestIntersection(QPoint p, int searchradius);
    
//! copy
/*!
@return a copy of this cell
*/
    cell * copy();
    element* addElement(element *);
    element* addBox(QRect r,int layer);
//! add box
/*!
A box is added to this cell. The box is defined by one of the corner
points and the width and the height.  For positive width and height
the point will be the lower left corner of the box.
@param x	X value of the anchor point.
@param y	Y value of the anchor point.
@param b	width of the box.
@param h	width of the box.
@param layer	the layer number which will be used for the box.
@return a pointer to the new element is returned.
*/
    element* addBox(int x,int y,int b, int h,int layer);
//! add rounded box
/*!
A rectangular polygon with rounded corners is added to this cell. The polygon is defined by one of the corner points and the width and the height.  For positive width and height
the point will be the lower left corner of the box.
@param x	X value of the anchor point.
@param y	Y value of the anchor point.
@param b	width of the box.
@param h	width of the box.
@param radius	radius of the corners
@param layer	the layer number which will be used for the box.
@return a pointer to the new element is returned.
*/
    element* addRoundedBox(int x,int y,int b, int h,int radius, int layer);
//! add box
/*!
A box is added to this cell. The box is defined by an array of 5
points. The first and the last point have to be the same. If the box
is rotated, a polygon is added instead of a box.
@param points	An array of 5 points.
@param layer	the layer number which will be used for the box.
@return a pointer to the new element is returned.
*/
    element* addBox(pointArray points,int layer);
//! add polygon
/*!
A polygon is added to this cell.
*/
    element* addPolygon(pointArray points,int layer);
//! add path
/*!
A path is added to this cell.
*/
    element* addPath(pointArray points,int layer);
//! add cellref
/*!
A cellref is added to this cell.
*/
    element* addCellref(cell *c,QPoint pos);
//! add cellrefarray
/*!
A cellrefarray is added to this cell.
*/
    element* addCellrefArray(cell *c,pointArray array,int anzx,int anzy);
//! add cellrefarray
/*!
A cellrefarray is added to this cell.
*/
    element* addCellrefArray(cell *c,QPoint pos1,QPoint pos2,int anzx,int anzy);
    element* addCellrefArray(cell *c,QPoint pos1,QPoint pos2,int anzx,int anzy,strans trans);
    element* addCellref();
    element* addCellrefArray();
//! add text
/*!
A test is added to this cell.
*/
    element* addText(int layer,QPoint pos, QString text);
//! add circle
/*!
A circle is added to this cell.
*/
    element* addCircle(int layer,QPoint center,int radius);
//! add ellipse
/*!
A ellipse is added to this cell.
*/
    element* addEllipse(int layer,QPoint center,int rx,int ry);
//! add circle
/*!
A circle is added to this cell. p1 and p2 are the edges of the bounding box.
*/
    element* addCircleBox(QPoint p1, QPoint p2,int layer);
//! group
/*!
Selected elements are moved to cell_
*/
    void group(cell *cell_);
//! group structure
/*!
Searchs structures identical to cell_ and replace it with a cellref to this cell. The number of replaced structures is returned.
*/
	int groupStructure(cell *cell_);
//! minimum
/*!
The minimum coordinates is returned, if less than pos.
*/
    void minimum(QPoint *pos);
//! maximum
/*!
The maximum coordinates is returned, if bigger than pos.
*/
    void maximum(QPoint *pos);
//! minimum select
/*!
The minimum coordinates of all selected elements is returned, if bigger than pos..
*/
    void minimumSelect(QPoint *pos);
//! maximum select
/*!
The maximum coordinates of all selected elements is returned, if less than pos.
*/
    void maximumSelect(QPoint *pos);
/*
    int minSize();
    int minSizeSelect();*/
//! depend on
/*!
@return true if this cell or its cellrefs has a cellref to Cell
*/
    bool depend(cell*Cell);
//! use cell
/*!
@return true if this cell has a cellref to Cell
*/
    bool useCell(cell*Cell);
    bool dependNotSaved();
//! relink
/*!
Cellreferences to Cellold are relink to Cellnew.
*/
    void relink(cell*Cellold,cell*Cellnew);
//! relink select
/*!
Selected cellref are relinked to Cellnew.
*/
    void relinkSelect(cell*Cellnew);
//! identical
/*!
@return true if this cell is identical to Cell; Non identical elements will be selected.
*/
    bool identical(cell*Cell);
//! compare
/*!
Tests, if this cell is the same as Cell. Non identical elements will be selected in both cells. All other elements will be deselected. 
*/
    void compare(cell*Cell);
    void mapSelect(strans t);
    void moveOrigin(cell *c,QPoint pos);
    void saveGDS(gds *);
    void saveDXF(dxf *);
#ifdef USE_3d
    void saveDXF3d(dxf3d*);
#endif
    void saveOASIS(oasis *);
    void saveCIF( cif *);
    void saveSVG(svg *);
    void saveCSV(csv *);
    void saveSelect(QDataStream *);
    void save(QDataStream *);
    void saveGerber(gerber *);
    void saveODB(odb *);
    void saveSOURCE(source *);
    void saveEPS(eps *,bool mainCell=false);
//! resize
/*!
Multiply all element with the factor scale.
*/
    void resize(double scale);
    void resize(double scale,int mod, bool *b); // data lost ?
//! delete element
/*!
Delete the element e.
*/
    void deleteElement(element *e);
//! swap layer
/*!
This function swap the layers i and k in the this cell.
*/
    void swapLayer(int i,int k);
//! swap layer select
/*!
This function swap the layers i and k of the selected elements in the this cell.
*/
    void swapLayerSelect(int i,int k);
//! select to box
/*!
Converts selected element to a box element if possible.
*/
    void toBoxSelect();
//! select to polygon
/*!
Converts selected element to a polygon if possible.
*/
    void toPolygonSelect();
//! to box
/*!
Converts element to a box element if possible.
*/
    void toBox();
//! to polygon
/*!
Converts element to a polygon if possible.
*/
    void toPolygon();
//! to circle select
/*!
Converts selected element polygon and box element to a circle. The circle is fitted in all existing points of the element.
*/
    void toCircleSelect();
//! close to polygon
/*!
Closes selected path elements to a polygon.
*/
   void closeToPolygonSelect();
//! convert to polygon if the path element is closed
/*!
Converts selected path elements to a polygon, if the first point is identical with the last point..
*/
void convertToPolygonIfClosedSelect();
//! close to polygon
/*!
Closes path elements to a polygon.
*/
   void closeToPolygon();
//! convert to polygon if the path element is closed
/*!
Converts all path elements to polygons, if the first point is identical with the last point..
*/
void convertToPolygonIfClosed();
//! set width
/*!
In selected path and text elements the width is set to w.
*/
    void setWidthSelect(int w);
//! set cap
/*!
In selected path elements the caps is set to w. (0 for no cap, 1 for round caps, 2 for square caps)
*/
    void setCapSelect(int w);
//! strip identical elements
/*!
If a cell contains two identical elements at the same position, it is impossible to see it. This function will detect this and remove one of the element.
*/
    void stripIdenticalElements();
//! clear properties
/*!
All properties in this cell are removed.
*/
    void clearProperties();
//! replace text
/*!
If text element are named text1, it is replaced with text2.
@return number of replacements
*/
    int replaceText(QString text1,QString text2);
//! flat layer
/*!
All element on layer are flatten. They are not removed in the cell reference and double exists afterwards (in the cell and in the cell reference):
*/
    void flatLayer(int layer);
//! triangulate select
/*!
Selected polygon are triangulated. The result is added to layer.
*/
    void triangulateSelect(int layer);
    void removeNotOrthogonalCellref(QHash<QString,cell*>,drawingField*);
    void removeScaledCellref(QHash<QString,cell*>,drawingField*);
    void clearNode();
    void clean();
    void updateCellref(cell *oldCell, cell* newCell);
    elementCount countSelect();
    elementList* addElement();
	int findEdge(QPoint p1,QPoint p2,int layer,char dir);
    void lineDistanceLayer(QPoint p1,QPoint p2, int *left,int *right,int layer);
    bool pointOnLayer(QPoint p,int layer);
    QStringList usedCells();
    //debug
public:
    void nextPoint();
    void cleanElements();
    bool useLayer(int layer);
    void useLayer(QBitArray *);
    void clearNodes();
    void buildConnect();
 friend class elementList;
 friend class cellList;
 friend class cellrefArray;
};

#endif
