package framePackage;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;

import printPackage.MyFont;
import assistPackage.Datatype;
import assistPackage.Lang2;
import assistPackage.Lang3;
import assistPackage.Parameters;
import assistPackage.Preferences;
import assistPackage.Selectie;
import assistPackage.VGM;
import assistPackage.Zoom;
import backupPackage.Edit;
import basicPackage.FlowType;
import basicPackage.VPoint2D;
import basicPackage.VPoint3D;
import basicPackage.Ventilatie;

import componentPackage.BComponent;
import componentPackage.ColComponent;
import componentPackage.EComponent;
import componentPackage.ExtraComponent;
import componentPackage.VComponent;
import componentPackage.VComponentType;
import componentPackage.VLine3D;

import dialogPackage.CollectorDialog;
import dialogPackage.EndComponentDialog;

/**
 * Het paneel waarin de tekening van de toevoer en afvoer komt
 * @author Jan Van den Bossche
 *
 */
public class GridPanel extends JPanel implements MouseListener, MouseMotionListener{
	private Dimension _preferredSize = new Dimension(1000, 800);    
//	private boolean _repaintSelectie, _resizePanel, _repaintAll;
	private float _zoomFactor;
	private FlowPanel _parent;
	private ArrayList<Selectie> _selectieList, _selectieListCopy, _selectieListTemp;
	private Selectie _currentSelectie, _pressSelectie;
	private Point _pressPoint2D, _panPoint;
	private VPoint2D _end2D, _previousEnd2D, _ventilatorPoint2D, _breakPoint2D;//_start, 
	/**
	 * 3D punten, maten in meter
	 */
	private VPoint3D _releasePoint3D, _startMovePoint3D, _startCopyPoint3D;
	/**
	 * maten in huidige pixels
	 */
	private float _colomnWidth, _rowHeight, _margeLeft, _margeUp, _snapWidth, _snapHeight, _tolerance;//_panelHeight,_panelWidth;
	private int _columnCount, _rowCount, _viewMode, _breakPointSnap;
	private boolean _background, _allPressures;
	private JPopupMenu _popupMenu;
	private JMenuItem _delete, _addRoom, _break, _copy, _cut, _paste, _move, 
		_selectSpecial, _demper, _collector;
	private boolean _breakingProcess, _movingProcess, _panProcess, _popupVisible, _dragging; 
	private ActionListener _popupMenuListener;
	private static final int SNAP = 4;
	public static final int NORMAL = 0, SELECTED = 1, BACKGROUND = 2;
	private int _drawMode;
	private MyFont _currentFont;
	private static final int SELECTION = 1, POINT = 2, DRAG = 4, BREAKPOINT = 8, 
			SELECT_POINT = 3, SELECT_POINT_DRAG = 7, SELECT_BREAK = 9, SELECT_POINT_BREAK = 11;

	public GridPanel(FlowPanel parent){
		super();
		_zoomFactor = 1.0f;
		_parent = parent;
		_selectieList = new ArrayList<Selectie>();
		_selectieListCopy = new ArrayList<Selectie>();
		_selectieListTemp = new ArrayList<Selectie>();
		_viewMode = FlowPanel.TOTAL_VIEW;//HORIZONTAL_XY;
		_popupMenuListener = new PopupMenuListener();
		_popupMenu = new JPopupMenu();
		_delete = new JMenuItem(Lang2.getString("Grid.popup_deleteComp")); //$NON-NLS-1$
		_delete.addActionListener(_popupMenuListener);
		_move = new JMenuItem(Lang2.getString("Grid.popup_move")); //$NON-NLS-1$
		_move.addActionListener(_popupMenuListener);
		_addRoom = new JMenuItem(Lang2.getString("Grid.popup_addEnd")); //$NON-NLS-1$
		_addRoom.addActionListener(_popupMenuListener);
		_break = new JMenuItem(Lang2.getString("Grid.popup_break")); //$NON-NLS-1$
		_break.addActionListener(_popupMenuListener);
		_selectSpecial = new JMenuItem(Lang2.getString("Grid.popup_selectTrace")); //$NON-NLS-1$
		_selectSpecial.addActionListener(_popupMenuListener);
		_cut = new JMenuItem(Lang2.getString("Grid.popup_cut")); //$NON-NLS-1$
		_cut.addActionListener(_popupMenuListener);
		_copy = new JMenuItem(Lang2.getString("Grid.popup_copy")); //$NON-NLS-1$
		_copy.addActionListener(_popupMenuListener);
		_paste = new JMenuItem(Lang2.getString("Grid.popup_paste")); //$NON-NLS-1$
		_paste.addActionListener(_popupMenuListener);
		_demper = new JMenuItem(Lang2.getString("Grid.popup_demper")); //$NON-NLS-1$
		_demper.addActionListener(_popupMenuListener);
		_collector = new JMenuItem(Lang2.getString("Grid.popup_collector")); //$NON-NLS-1$
		_collector.addActionListener(_popupMenuListener);
		_popupMenu.add(_delete);
		_popupMenu.add(_copy);
		_popupMenu.add(_cut);
		_popupMenu.add(_paste);
		_popupMenu.add(_move);
		_popupMenu.add(_selectSpecial);
		_popupMenu.add(_break);
		_popupMenu.add(_addRoom);
		_popupMenu.add(_demper);
		_popupMenu.add(_collector);
		
		this.setOpaque(true); //niet doorschijnend, moet dan geen onderliggende componenten tekenen?
		this.addMouseListener(this);
		this.addMouseMotionListener(this);
		this.addMouseWheelListener(new MouseWheelListener() {
			public void mouseWheelMoved(MouseWheelEvent e) {
				//wheelrotation = 1 (uitzoomen) of -1 (inzoomen)
				setZoom(e.getWheelRotation(), e.getPoint());
			}
		});
		setGridValues();
		_drawMode = 0;
	}

	@Override
	public void paintComponent(Graphics g){
		super.paintComponent(g);
		Graphics2D g2 = (Graphics2D)g;
		g2.setFont(_currentFont);
		g2.setPaint(Color.WHITE);
		g2.fillRect(0, 0, _preferredSize.width, _preferredSize.height);
		g2.setStroke(Program.preferences.getGridStroke(1.0f));
		drawGrid(g2);
		g2.setStroke(Program.preferences.getGenericStroke(1.0f));
		if (_background) drawBackgroundComponents(g2);
//		g2.setXORMode(Color.WHITE);
		drawComponents(g2);
//		g2.setPaintMode();
		if ((_drawMode & SELECTION) == SELECTION){
			drawSelecties(g2);
		}
		if ((_drawMode & DRAG) == DRAG){
			drawDragLine(g2);
		}
		if ((_drawMode & BREAKPOINT) == BREAKPOINT){
			drawBreakPoint(g2);
		}
		if ((_drawMode & POINT) == POINT){
			drawSelectiePoint(g2);
		}
	}

	private void repaintClip(int drawMode){
		_drawMode = drawMode;
		Point point = _parent.getViewport().getViewPosition();
		Rectangle rect = _parent.getViewport().getVisibleRect();
		repaint(point.x, point.y, rect.width, rect.height);
	}
	
	/**
	 * Tekent het raster en de componenten
	 * @param selectie: indien true worden ook de selecties getekend
	 */
	public void draw(boolean selectie){
		_drawMode = 0;
		if (selectie) _drawMode = SELECTION;
		repaintClip(_drawMode);
	}
	@Override
	public Dimension getPreferredSize() {
		return _preferredSize;
	}

	/**
	 * Here is an explanation: the point p is the location of the mouse relative to the panel. 
	 * Since you are scaling the size of the panel, the location of p (relative to the size of the panel) 
	 * will scale by the same factor. 
	 * By subtracting the current location from the scaled location, you get how much the point 'shifts' 
	 * when the panel is resized. Then it is simply a matter of shifting the panel location in the 
	 * scroll pane by the same amount in the opposite direction to put p back under the mouse cursor.
	 * @param n: number of mousewheel-points (bij aanroep door muiswiel: 1 of -1 via menu 2 of -2)
	 * @param p: point where the cursor is
	 */
	private void setZoom(int n, Point p) {
		// voor 1.08 geldt: als n = 1 => d = 0.926   als n = -1 => d = 1.08
		float max = 6.0f, min = 0.3f;
		float factor = (float) n * 1.05f;
		if (n > 0) factor = 1/factor;
		else factor = -factor;
		boolean doZoom = false;
		
		if (_zoomFactor <= max && _zoomFactor >= min) doZoom = true;
		else if (_zoomFactor > max && factor < 1) 	doZoom = true;
		else if (_zoomFactor < min && factor > 1) doZoom = true;

		if (doZoom){
			_zoomFactor *= factor;
			//bijgevoegd om probleem te verhelpen?
			if (_zoomFactor > max) _zoomFactor = max;
			else if (_zoomFactor < min) _zoomFactor = min;
			setGridValues();
			int offX = (int)(p.x * factor) - p.x;
			int offY = (int)(p.y * factor) - p.y;
			setLocation(getLocation().x - offX, getLocation().y - offY);
			getParent().doLayout();//validate();//
//			LogPanel.line("zoomfactor=" + _zoomFactor + ", locatie tekening t.o.v view: x=" + 
//					getLocation().x + ", y=" + getLocation().y);
		}
	}
	/**
	 * Luistert naar de menu's Inzoomen en Uitzoomen in Beeld
	 * Wijzigt de zoomfactor van het gridPanel
	 * @param zoomIn: -2 = inzoomen, 2 = uitzoomen, 0 = standaard zoom
	 * @return de zoomfactor
	 */
	public float doZoom(int zoomIn){
		Point mousePoint = this.getMousePosition();
		if (mousePoint == null) {
			Rectangle rect = _parent.getViewport().getViewRect();
			int x = rect.x + rect.width/2;
			int y = rect.y + rect.height/2;
			mousePoint = new Point(x, y);
		}
		if (zoomIn == 0) {
			_zoomFactor = 1.0f;
			setGridValues();
			_parent.setVentilatorInViewport();
//			getParent().doLayout();
		}
		else setZoom(-zoomIn, mousePoint);
		return _zoomFactor;
	}
	private void setPan(Point start, Point end){
//		System.out.println("GridPanel.setPan(): "+ getLocation());
//		System.out.println("start: "+start);
//		System.out.println("eind: "+end);
		int locX = getLocation().x, locY = getLocation().y;
		int panX = end.x - start.x, panY = end.y - start.y;
		Rectangle rect = _parent.getViewport().getViewRect();
		
		if (locX > 0 && panX > 0) panX = 0;
		else if (_preferredSize.width + locX < rect.getWidth() && panX < 0) panX = 0;
		if (locY > 0 && panY > 0) panY = 0;
		else if (_preferredSize.height + locY < rect.getHeight() && panY < 0) panY = 0;
		setLocation(locX + panX, locY + panY);
	}
	/**
	 * Creëert het Graphics object, door alle tekenfuncties aan te roepen
	 * @param zoom: de gekozen schaal voor de tekening
	 * @return
	 */
//	private void drawBasic(Graphics2D g2){
//		g2.setPaint(Color.WHITE);
//		g2.fillRect(0, 0, _preferredSize.width, _preferredSize.height);
//		drawGrid(g2);
//		if (_background) drawBackgroundComponents(g2);
//		drawComponents(g2);
//	}

	/**
	 * Haalt de waarde voor kolombreedte, aantal kolommen, rijhoogte en aantal rijen op.
	 * Stelt breedte en hoogte van het panel in, berekent het ventilatorpunt.
	 */
	public void setGridValues(){
		// breedte van de kolom is 1 m schuin (links-rechts, voor-achter)
		_colomnWidth = Program.parameters.getColumnWidth() * _zoomFactor;
		_columnCount = Program.parameters.getColumnCount();

		// hoogte van een rij is 1 m vertikaal
		_rowHeight = Program.parameters.getRowHeight() * _zoomFactor;
		_rowCount = Program.parameters.getRowCount();
		
		_margeLeft = Parameters.MARGE * _zoomFactor;
		_margeUp = Parameters.MARGE * _zoomFactor;

		_preferredSize.width = (int)(_columnCount * _colomnWidth + _margeLeft * 2);//_panelWidth
		_preferredSize.height = (int)(_rowCount * _rowHeight + _margeUp * 2);//_panelHeight

		_snapWidth = _colomnWidth / SNAP;
		_snapHeight = _rowHeight / SNAP;
		_tolerance = (float) (Parameters.TOLERANCE * Math.sqrt(_zoomFactor));
		
		Program.parameters.computeVentilatorPixelPoint2D();
		_ventilatorPoint2D = Program.parameters.getVentilatorGridPoint();
	}
	/**
	 * Tekent het raster, alle lijnen of 2 van de 3 richtingen, 
	 * afhankelijk van de view (TOTAL_VIEW, HORIZONTAL, LEFT_RIGHT, BEFORE_BEHIND)
	 * @param g2
	 */
	private void drawGrid(Graphics2D g2){
		g2.setColor(Program.preferences.getColorGrid());
		
		for (int c = 0; c < _columnCount; c++) {
			if (_viewMode != FlowPanel.HORIZONTAL_XY){
				//vertikale lijn links van de kolom
				Line2D lijn2D = new Line2D.Float(	
						_margeLeft + c * _colomnWidth,
						_margeUp,
						_margeLeft + c * _colomnWidth,
						_margeUp + _rowCount * _rowHeight);	
				g2.draw(lijn2D);			
				//laatste vertikale lijn
				if (c + 1 == _columnCount){
					lijn2D = new Line2D.Float(	
							_margeLeft + (c + 1) * _colomnWidth,
							_margeUp,
							_margeLeft + (c + 1) * _colomnWidth,
							_margeUp + _rowCount * _rowHeight);	
					g2.draw(lijn2D);			
				}
			}
			for (float r = 0; r < _rowCount; r++) {
				float unpair = 0.5f;
				if (c % 2 == 0) unpair = 0;
				if (_viewMode != FlowPanel.BEFORE_BEHIND_YZ){
				//schuine lijn van links boven naar rechts onder
				Line2D lijn2D = new Line2D.Float(
						_margeLeft + c * _colomnWidth,
						_margeUp + (r + unpair) * _rowHeight,
						_margeLeft + (c + 1) * _colomnWidth,
						_margeUp + (r + unpair + 0.5f) * _rowHeight);	
				g2.draw(lijn2D);
				}
				if (_viewMode != FlowPanel.LEFT_RIGHT_XZ){
				//schuine lijn van rechts boven naar links onder
				Line2D lijn2D = new Line2D.Float(
						_margeLeft + (c + 1) * _colomnWidth,
						_margeUp + (r - unpair + 0.5f) * _rowHeight,
						_margeLeft + c * _colomnWidth,
						_margeUp + (r - unpair + 1) * _rowHeight);	
				g2.draw(lijn2D);
				}
			}
		}
	}
	/**
	 * Tekent de componenten op het paneel
	 * @param g2
	 */
	private void drawComponents(Graphics2D g2){
		for (VComponent component : _parent.getComponentList()) {
			try {
				component.draw(g2, _zoomFactor, NORMAL, _allPressures);
			} 
			catch (Exception e) {
				LogPanel.exception(Lang2.getString("Grid.log_drawComp") + component.toString() + " (" + e.toString() + ")"); //$NON-NLS-1$
			}
		}
	}
	/**
	 * Tekent de componenten op het paneel
	 * @param g2
	 */
	private void drawBackgroundComponents(Graphics2D g2){
		if (_parent.getFlowType() == FlowType.IN){
			for (VComponent component : Program.getVentilatie().getComponentList(FlowType.OUT)) {
				try {
					component.draw(g2, _zoomFactor, BACKGROUND, false);
				} 
				catch (Exception e) {
					LogPanel.exception(Lang2.getString("Grid.log_drawComp") + component.toString() + " (" + e.toString() + ")"); //$NON-NLS-1$
				}
			}
		}
		else if (_parent.getFlowType() == FlowType.OUT){
			for (VComponent component : Program.getVentilatie().getComponentList(FlowType.IN)) {
				try {
					component.draw(g2, _zoomFactor, BACKGROUND, false);
				} 
				catch (Exception e) {
					LogPanel.exception(Lang2.getString("Grid.log_drawComp") + component.toString() + " (" + e.toString() + ")"); //$NON-NLS-1$
				}
			}
		}
	}
	/**
	 * Tekent de geselecteerde componenten op het paneel
	 * @param g2
	 */
	private void drawSelecties(Graphics2D g2){
		for (Selectie selectie : _selectieList) {
			selectie.draw(g2, _zoomFactor, _allPressures);
		}
	}
	
	/**
	 * Tekent het geselecteerde punt van de huidige selectie
	 * @param g2
	 */
	private void drawSelectiePoint(Graphics2D g2){
		if (_currentSelectie != null){
			g2.setPaint(Program.preferences.getColorSelectie());
			_currentSelectie.drawSelectedPoint(g2, _zoomFactor);
		}
	}
	/**
	 * Tekent de voorlopige verbindingslijn tussen het muisindrukpunt en het muissleeppunt
	 */
	private void drawDragLine(Graphics2D g2){
		if (_currentSelectie != null && _end2D != null){
			VPoint2D start = _currentSelectie.getPoint().getIsometricPosition(_zoomFactor);
			g2.setColor(Program.preferences.getColorSelectie());
			Line2D lijn2D = new Line2D.Double(
					start.X, start.Y,
					_margeLeft + _end2D.X * _colomnWidth,
					_margeUp + _end2D.Y * _rowHeight);	
			g2.draw(lijn2D);
		}
	}
	/**
	 * Hertekent de voorlopige breekpunten
	 */
	private void drawBreakPoint(Graphics2D g2){//, VPoint2D point){
		if (_breakPoint2D != null){
			g2.setColor(Program.preferences.getColorSelectie());
			_currentSelectie.draw(g2, _zoomFactor, false);
			_breakPoint2D.draw(g2, Program.preferences.getDiameterEnd());
		}
	}
	/**
	 * Controleert in welke element van de ventilatie zich de muispositie bevindt en stelt de selectie in
	 * Eerst controle van componenten, die geen kanaal zijn, dan op startpunt, dan op midden en eindpunt
	 * @param mouseX: de X-waarde van de muispositie
	 * @param mouseY: de Y-waarde van de muispositie
	 * @param takeNext: indien reeds geselecteerd, zoek een volgende
	 * @return selectie van een component (pointer) of null
	 */
	private Selectie getSelectedComponent(int mouseX, int mouseY, boolean takeNext){
		Selectie selectie = null;
		selectie = getSelectedComponent(mouseX, mouseY, takeNext, VLine3D.START, false);
		if (selectie != null) return selectie;
		selectie = getSelectedComponent(mouseX, mouseY, takeNext, VLine3D.START, true);
		if (selectie != null) return selectie;
		selectie = getSelectedComponent(mouseX, mouseY, takeNext, VLine3D.MID, true);
		if (selectie != null) return selectie;
		return getSelectedComponent(mouseX, mouseY, takeNext, VLine3D.END, true);

//		for (VComponent component : _parent.getComponentList()) {
//			if (!component.isTubeSimilar()){
//				VPoint2D[] p = component.getIsometricPosition(_zoomFactor);
//				if ((p[VLine3D.START].X + _tolerance) > mouseX && (p[VLine3D.START].X - _tolerance) < mouseX && 
//						(p[VLine3D.START].Y + _tolerance) > mouseY && (p[VLine3D.START].Y - _tolerance) < mouseY){
//					return new Selectie(component, VLine3D.START);
//				}
//			}
//		}
//		for (VComponent component : _parent.getComponentList()) {
//			VPoint2D[] p = component.getIsometricPosition(_zoomFactor);
//			if ((p[VLine3D.START].X + _tolerance) > mouseX && (p[VLine3D.START].X - _tolerance) < mouseX && 
//					(p[VLine3D.START].Y + _tolerance) > mouseY && (p[VLine3D.START].Y - _tolerance) < mouseY){
//				return new Selectie(component, VLine3D.START);
//			}
//		}
//		for (VComponent component : _parent.getComponentList()) {
//			VPoint2D[] p = component.getIsometricPosition(_zoomFactor);
//			if ((p[VLine3D.MID].X + _tolerance) > mouseX && (p[VLine3D.MID].X - _tolerance) < mouseX && 
//					(p[VLine3D.MID].Y + _tolerance) > mouseY && (p[VLine3D.MID].Y - _tolerance) < mouseY){
//				return new Selectie(component, VLine3D.MID);
//			}
//			else if ((p[VLine3D.END].X + _tolerance) > mouseX && (p[VLine3D.END].X - _tolerance) < mouseX && 
//					(p[VLine3D.END].Y + _tolerance) > mouseY && (p[VLine3D.END].Y - _tolerance) < mouseY){
//				return new Selectie(component, VLine3D.END);
//			}			
//		}
//		return null;
	}
	/**
	 * Controleert in welke element van de ventilatie zich de muispositie bevindt,
	 * indien 'takeNext' en reeds geselecteerd wordt deze gedeselecteerd en zoekt 
	 * een volgende component, en stelt de selectie in
	 * @param mouseX: de X-waarde van de muispositie
	 * @param mouseY: de Y-waarde van de muispositie
	 * @param takeNext: indien reeds geselecteerd, zoek een volgende
	 * @param point: VLine3D.START of MID of END
	 * @param tube: indien false wordt niet naar buizen, reducties, verwijdingen gezocht
	 * @return
	 */
	private Selectie getSelectedComponent(int mouseX, int mouseY, boolean takeNext, int point, boolean tube){
		for (VComponent component : _parent.getComponentList()) {
			if (tube && component.isTubeSimilar() || !tube && !component.isTubeSimilar()){
				VPoint2D[] p = component.getIsometricPosition(_zoomFactor);
				if ((p[point].X + _tolerance) > mouseX && (p[point].X - _tolerance) < mouseX && 
						(p[point].Y + _tolerance) > mouseY && (p[point].Y - _tolerance) < mouseY){
					if (takeNext) {
						if (!removeFromSelectieList(component)) 
							return new Selectie(component, point);
					}
					else return new Selectie(component, point);
				}
//				else if (point == VLine3D.MID &&
//						(p[VLine3D.END].X + _tolerance) > mouseX && (p[VLine3D.END].X - _tolerance) < mouseX && 
//						(p[VLine3D.END].Y + _tolerance) > mouseY && (p[VLine3D.END].Y - _tolerance) < mouseY){
//					if (takeNext) {
//						if (!removeFromSelectieList(component)) 
//							return new Selectie(component, point);
//					}
//					else	return new Selectie(component, VLine3D.END);
//				}		
			}
		}
		return null;
	}
	/**
	 * Controleert in welke elementen van de ventilatie zich de muispositie bevindt,
	 * indien meerdere wordt een volgende in de lijst geretourneerd 
	 * een volgende component, en stelt de selectie in
	 * @param mouseX: de X-waarde van de muispositie
	 * @param mouseY: de Y-waarde van de muispositie
	 * @return
	 */
	private Selectie getSelectedComponentSwitch(int mouseX, int mouseY){
		boolean noChange = false;
		// 0 = START, 1 = MID, 2 = END
		for (int i = 0; i < 3; i++) {
			for (VComponent component : _parent.getComponentList()) {
				VPoint2D[] p = component.getIsometricPosition(_zoomFactor);
				if ((p[i].X + _tolerance) > mouseX && (p[i].X - _tolerance) < mouseX && 
						(p[i].Y + _tolerance) > mouseY && (p[i].Y - _tolerance) < mouseY){
					noChange = addToSelectieListTemp(new Selectie(component, i), noChange);
				}
			}
		}
//		String info = "Selectielijst=";
//		for (Selectie selectie : _selectieListTemp) {
//			info += selectie.getComponent().toString() + ",";
//		}
//		System.out.println(info);
		if (_selectieListTemp.size() > 0) return _selectieListTemp.get(0);
		else return null;
	}
	/**
	 * Gaat na in welke rij en welke kolom de muisklik zich bevindt
	 * Rijen en kolommen worden onderverdeeld (SNAP = aantal ondeverdelingen van een rij)
	 * @param mouseX
	 * @param mouseY
	 * @return: VPoint2D(kolom, rij)  
	 */
	private VPoint2D getGridPoint(int mouseX, int mouseY){
		float snapCol = 0, snapRow = 0;
		float x = ((float)mouseX - _margeLeft) / _snapWidth;	//_margeLeftView)
		if (x < _columnCount * SNAP && Math.round(x) >= 0){
			snapCol = Math.round(x);
		}
		else return null;
		
		float y = ((float)mouseY - _margeUp) / _snapHeight;	//_margeUpView
		if (y < _rowCount * SNAP && Math.round(y) >= 0){
			snapRow = Math.round(y);
			if (snapCol % 2 == 1) snapRow = (float)(Math.round(y + 0.5f)) - 0.5f;
			VPoint2D gridPoint = new VPoint2D((float)snapCol / SNAP, (float)snapRow / SNAP);
//			System.out.println("GridPanel.getGridPoint()" + gridPoint.toString());
			return gridPoint;
		}
		return null;
	}

	/**
	 * Gaat na op welke as de muisklik zich bevindt
	 * Rijen en kolommen worden onderverdeeld (SNAP = aantal onderverdelingen)
	 * @param mouseX
	 * @param mouseY
	 * @return: VPoint2D(kolom, rij)  
	 */
	private VPoint2D getGridPointOnAxis(int mouseX, int mouseY){
		VPoint2D point = _currentSelectie.getPoint().getIsometricPosition(_zoomFactor);
		VPoint2D start = _currentSelectie.getPoint().getGridPosition();
		//bepaal de richtingscoëfficient
		float difX = point.X - mouseX;
		float difY = point.Y - mouseY;
		if (difX == 0) difX = 0.001f;
		float rc = difY / difX;
		
		if (rc < 0 && rc > -1.732f) {
			float x = Parameters.getGridX(difX, _zoomFactor, SNAP);
			return new VPoint2D(start.X - x, start.Y + x / 2);
		}
		else if (rc > 0 && rc < 1.732f) {
			float x = Parameters.getGridX(difX, _zoomFactor, SNAP);
			return new VPoint2D(start.X - x, start.Y - x / 2);
		}
		else {
			float y = Parameters.getGridY(difY, _zoomFactor, SNAP);
			return new VPoint2D(start.X, start.Y - y);
		}
	}
	/**
	 * Gaat na op welke plaats op 1 van de 3 assen de muisklik zich bevindt
	 * Meters worden onderverdeeld (SNAP = aantal onderverdelingen)
	 * @param mouseX
	 * @param mouseY
	 * @return: VPoint3D(x, y, z)  
	 */
	private VPoint3D getPoint3DOnAxis(int mouseX, int mouseY){
		VPoint3D start = _currentSelectie.getPoint();
		VPoint2D point = _currentSelectie.getPoint().getIsometricPosition(_zoomFactor);
		//bepaal de richtingscoëfficient
		float difX = point.X - mouseX;
		float difY = point.Y - mouseY;
		if (difX == 0) difX = 0.001f;
//		System.out.println("GridPanel.getPoint3DOnAxis() x="+Parameters.getGridX(difX, _zoomFactor, SNAP)+
//				", y="+Parameters.getGridY(difY, _zoomFactor, SNAP));
		float rc = difY / difX;
		//richting Y-as
		if (rc < 0 && rc > -1.732f) {
			return new VPoint3D(start.X, start.Y - 
					Parameters.getGridX(difX, _zoomFactor, SNAP), start.Z);
		}
		//richting X-as
		else if (rc > 0 && rc < 1.732f) {
			return new VPoint3D(start.X - 
					Parameters.getGridX(difX, _zoomFactor, SNAP), start.Y, start.Z);
		}
		//richting Z-as
		else {
			return new VPoint3D(start.X, start.Y, start.Z + 
					Parameters.getGridY(difY, _zoomFactor, SNAP));
		}
	}
	/**
	 * Controleert of een 3D punt op een standaard richting (x of y of z) ligt
	 * t.o.v. het vorig geselecteerde punt
	 * @param point
	 * @return
	 */
	private boolean isStandardDirection(VPoint3D point) {
		VPoint3D previous = _currentSelectie.getPoint();
//		System.out.println("GridPanel.isStandardDirection() "+point+" ; "+previous);
		if (previous.X == point.X && previous.Y == point.Y) return true;
		else if (previous.X == point.X && previous.Z == point.Z) return true;
		else if (previous.Y == point.Y && previous.Z == point.Z) return true;
		return false;
	}

	/**
	 * Geeft een punt terug als het muispunt dichtbij een breekpunt 
	 * op de lijn van het geselecteerd component ligt.
	 * Afstand tussen 2 breekpunten >= 1m /SNAP
	 * @param mouseX
	 * @param mouseX
	 * @return: coördinaten in pixels, null indien niet bij een breekpunt
	 */
	private VPoint2D getBreakPoint(int mouseX, int mouseY){
		_breakPointSnap = 0;
		for (VPoint2D p : _currentSelectie.getComponent().getSnapPointList(SNAP, _zoomFactor)) {
			_breakPointSnap++;
			if (Math.abs(p.X - mouseX) < _tolerance && Math.abs(p.Y - mouseY) < _tolerance){
				return p;
			}
		}
		_breakPointSnap = 0;
		return null;
	}
	/**
	 * Geeft het 3D-punt met relatieve waarden t.o.v. het ventilatorpunt,
	 * en met inachtname van het vlak waarin het aangeklikt punt van een bestaande component ligt.
	 * @param p: het aangegeven 2D-punt op de tekening
	 * @return: positieve waarden naar achter, naar rechts, naar boven
	 */
	private VPoint3D getRelativePointToVentilator(VPoint2D p){
		VPoint2D v = _ventilatorPoint2D;
		// naar achter, naar rechts, naar boven
		float x = 0, y = 0, z = 0;
//		if (_viewMode == FlowPanel.TOTAL_VIEW){
//		}
		if (_viewMode == FlowPanel.HORIZONTAL_XY){
			z = _currentSelectie.getPoint().Z;
			x = (-v.X + p.X) / 2 - v.Y + p.Y + z;
			y = (-v.X + p.X) / 2 + v.Y - p.Y - z;
		}
		else if (_viewMode == FlowPanel.BEFORE_BEHIND_YZ){
			x = _currentSelectie.getPoint().X;
			y = -v.X + p.X - x;
			z = (v.X - p.X) / 2 + v.Y - p.Y + x;
		}
		else if (_viewMode == FlowPanel.LEFT_RIGHT_XZ){
			y = _currentSelectie.getPoint().Y;
			x = -v.X + p.X - y;
			z = (-v.X + p.X) / 2 + v.Y - p.Y - y;
		}
		return new VPoint3D(x, y, z);
	}

	/**
	 * Als de linker muisknop ingedrukt wordt, zonder shift, 
	 * wordt de lijst met selecties leeg gemaakt (met shift maakt meervoudige selecties mogelijk)
	 * Als geklikt wordt op een selecteerbaar punt, wordt de selectie getekend
	 */
	@Override
	public void mousePressed(MouseEvent e) {
		_releasePoint3D = null;
		_previousEnd2D = null;
		_end2D = null;
		_pressPoint2D = new Point(e.getX(), e.getY());
		_panProcess = false;
		_dragging = false;
		_parent.setMousePositionInfo(null);
		
		if (_movingProcess){
			Selectie selectie = getSelectedComponent(e.getX(), e.getY(), false);
			if (selectie != null) _startMovePoint3D = selectie.getPoint();
    	}
		else if (!_breakingProcess){
			if (SwingUtilities.isRightMouseButton(e)){
				_pressSelectie = getSelectedComponentSwitch(e.getX(), e.getY());
			}
			else if (e.isShiftDown() || e.isControlDown()){
				_currentSelectie = getSelectedComponentSwitch(e.getX(), e.getY());
			}
			else {
				_currentSelectie =  getSelectedComponent(e.getX(), e.getY(), false);
			}
			if (_currentSelectie == null){
				_panPoint = e.getPoint();
				_panProcess = true;
				Program.getFrame().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
    		}
    		else {
    			if (!SwingUtilities.isRightMouseButton(e) && !e.isShiftDown() && 
    					!e.isControlDown()){// && !_movingProcess && !_panProcess){
    				_selectieList.clear();
    			}
				_releasePoint3D = _currentSelectie.getPoint();
				if (SwingUtilities.isRightMouseButton(e)){//e.isPopupTrigger()){
					setPopupMenusEnable();	
					_popupVisible = true;
					Point insertPoint = _currentSelectie.getPopupInsertPoint(_pressPoint2D);
					_popupMenu.show(this, insertPoint.x, insertPoint.y);
				}			
				repaintClip(SELECT_POINT);
			}
		}
	}

	/**
	 * Als de muisknop gelost wordt zonder slepen wordt het component geselecteerd
	 * Met slepen, en als er op een selecteerbaar punt (van een component) was geklikt, 
	 * en als er op een selecteerbaar punt wordt gelost, wordt een sleeplijn getekend.
	 */
	@Override
	public void mouseReleased(MouseEvent e) {
		_panProcess = false;
		Program.getFrame().setCursor(Cursor.getDefaultCursor());
		if (!SwingUtilities.isRightMouseButton(e) && _popupVisible) {
			_popupVisible = false;
			return;
		}
		else if (_breakingProcess){
			Selectie selectie = getSelectedComponent(e.getX(), e.getY(), false);
			if (_breakPointSnap > 0 && selectie != null &&
					_currentSelectie.getComponent() == selectie.getComponent()) 
				breakComponent();
			repaintClip(SELECTION);
			_breakingProcess = false;
		}
		//als de muis niet verschoven is tussen press en release:
		//geen component geselecteerd wordt -> deselecteren
		//wel component geselecteerd -> toevoegen aan selectielijst
		else if (!_movingProcess && !_dragging){
//			Math.abs(_pressPoint2D.x - e.getX()) < _tolerance && 
//				Math.abs(_pressPoint2D.y - e.getY()) < _tolerance){
			Selectie selectie = null;
			if (e.isShiftDown() || e.isControlDown()) 
				selectie = getSelectedComponentSwitch(e.getX(), e.getY());
			else selectie = getSelectedComponent(e.getX(), e.getY(), false);
				if (selectie == null) {
				System.out.println("GridPanel.mouseReleased(): selectie=null");
				_selectieList.clear();
				repaintClip(SELECTION);
				Program.setDeleteMenuItemEnabled(false);
			}
			else {
//				if (e.isShiftDown()) selectTracePart();//werkt nog niet
//				else 
				addToSelectieList(selectie);
				_parent.getPropertiesPanel().setSelectedComponentInfo(
						_selectieList, Selectie.MULTI_SELECTION);
//				_parent.setMouseInfo(_currentSelectie.getComponent().toString());
				Program.setMoveMenuItemEnabled(true);
				Program.setBreakMenuItemEnabled(selectie.isMidPoint());//_currentSelectie
				repaintClip(SELECTION);
			}
			Program.setDeleteMenuItemEnabled(true);
		}
		//als een component geselecteerd is (niet middenpunt) en op de huidige plaats van de
		//muiswijzer staat een componentpunt of gridpunt, dan wordt een lijn toegevoegd, of 
		//ingeval van move-proces wordt  een punt van een selectie verplaatst
		else if (_currentSelectie != null && !_currentSelectie.isMidPoint()){
			if (_viewMode == FlowPanel.TOTAL_VIEW){
				Selectie selectie = getSelectedComponent(e.getX(), e.getY(), false);
				if (selectie != null && !selectie.isMidPoint() && 
						isStandardDirection(selectie.getPoint()))
					_releasePoint3D = selectie.getPoint();
				else {
					VPoint3D point = getPoint3DOnAxis(e.getX(), e.getY());
					if (point != null)	_releasePoint3D = point;
				}
			}
			else {
				Selectie selectie = getSelectedComponent(e.getX(), e.getY(), false);
				if (selectie != null && !selectie.isMidPoint()) 
					_releasePoint3D = selectie.getPoint();
				else {
					VPoint2D point = getGridPoint(e.getX(), e.getY());
					if (point != null)	_releasePoint3D = getRelativePointToVentilator(point);
				}
			}
			if (_releasePoint3D != null){
				if (_movingProcess){
					if  (_startMovePoint3D != null) moveComponent(_releasePoint3D);
					_movingProcess = false;
				}
				else addComponent(null, true);
				repaintClip(SELECTION);
			}
			Program.setDeleteMenuItemEnabled(true);
		}
	}

	@Override
	public void mouseDragged(MouseEvent e) {
		_dragging = true;
		if (_panProcess){
			setPan(_panPoint, e.getPoint());
//			String s = "null"; if (_currentSelectie != null) s = _currentSelectie.getComponent().toString();
//			System.out.println("GridPanel.mouseDragged() selectie="+_selectieList.size()+", mode="+_drawMode);
		}
		else if (_currentSelectie != null){
			if (_breakingProcess){
				VPoint2D point = getBreakPoint(e.getX(), e.getY());
				if (point != null && !point.equals(_breakPoint2D)){
					_breakPoint2D = point;
					repaintClip(SELECT_BREAK);
//					System.out.println("GridPanel.mouseDragged() breakpoint: " + _breakPoint2D.toString());
//					System.out.println("selectie: " + _currentSelectie.toString());
				}
				else _breakPoint2D = null;
			}
			else if (!_currentSelectie.isMidPoint()){
				VPoint2D point = null;
				Selectie selectie = null;
				if (_viewMode == FlowPanel.TOTAL_VIEW){
					selectie = getSelectedComponent(e.getX(), e.getY(), false);
//					if (selectie != null) System.out.println("GridPanel.mouseDragged()2 "+selectie);
					if (selectie != null && !selectie.isMidPoint() && 
							isStandardDirection(selectie.getPoint())){
						point = selectie.getPoint().getGridPosition();
//						System.out.println("GridPanel.mouseDragged()3 "+point);
					}					
					if (point == null)	point = getGridPointOnAxis(e.getX(), e.getY());
				}
				else {
					selectie = getSelectedComponent(e.getX(), e.getY(), false);
					if (selectie != null) {
						point = selectie.getPoint().getGridPosition();
					}
					else {
						point = getGridPoint(e.getX(), e.getY());
					}
				}
				if (_currentSelectie.getPoint() != null && point != null){// || point3D != null)){	
					_previousEnd2D = _end2D;
					_end2D = point;
					if (_previousEnd2D != null  && !_previousEnd2D.equals(point)){
						if (_viewMode == FlowPanel.TOTAL_VIEW){
							VPoint3D point3D = getPoint3DOnAxis(e.getX(), e.getY());
//							System.out.println("Dragged: "+ point3D.toString());
							_parent.setMousePositionInfo(_currentSelectie.getPoint().
									getDistanceTo(point3D));
						}
						else if (selectie != null) _parent.setMousePositionInfo(_currentSelectie.getPoint().
									getDistanceTo(selectie.getPoint()));
						else if (point != null) _parent.setMousePositionInfo(_currentSelectie.getPoint().
								getDistanceTo(getRelativePointToVentilator(point)));
//						else if (point3D != null) 
						repaintClip(SELECT_POINT_DRAG);
					}
				}
			}
		}
	}


	@Override
	public void mouseClicked(MouseEvent e) {}

	@Override
	public void mouseEntered(MouseEvent arg0) {}

	@Override
	public void mouseExited(MouseEvent arg0) {}

	@Override
	public void mouseMoved(MouseEvent arg0) {}
	
	/**
	 * Luistert naar de events van de popup menus
	 */
	private class PopupMenuListener implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {
			if (e.getSource() == _break){
				getBreakPoint(_pressPoint2D.x, _pressPoint2D.y);
				_breakingProcess = true;
			}
			else {
				_currentSelectie = _pressSelectie.clone();
				if (e.getSource() == _delete){
					removeSelectedComponents();
					draw(false);
				}
				else if (e.getSource() == _addRoom){
					new EndComponentDialog(GridPanel.this).setVisible(true);
				}
				else if (e.getSource() == _selectSpecial){
					selectCurrentKanaalstuk();
				}
				else if (e.getSource() == _move){
					_movingProcess = true;
				}
				else if (e.getSource() == _cut){
					_parent.addUndo(Edit.COMPONENTS_REMOVE, getSelectedComponents());
					_startCopyPoint3D = _currentSelectie.getPoint();
					_selectieListCopy = new ArrayList<Selectie>();
					for (Selectie selectie : _selectieList) {
						_selectieListCopy.add(selectie);
						removeComponent(selectie.getComponent(), false, false);					
					}
					_selectieList.clear();
					draw(false);
				}
				else if (e.getSource() == _copy){			
					_startCopyPoint3D = _currentSelectie.getPoint();
					_selectieListCopy = new ArrayList<Selectie>();
					for (Selectie selectie : _selectieList) {
						if (selectie.getComponent().getType() != VComponentType.VENTILATOR)
							_selectieListCopy.add(selectie);
					}
					_startCopyPoint3D = _currentSelectie.getPoint();
				}
				else if (e.getSource() == _paste){
					_parent.addUndo(Edit.COMPONENTS_ADD, _parent.getComponentList().clone());
					for (Selectie selectie : _selectieListCopy) {
						VComponent comp = selectie.getComponent();
						if (comp.isTubeSimilar()) {
							addUndefinedTubeToSelectedPoint(comp);
						}
						//					else if (comp instanceof EComponent){
						//						if (comp.getRoom() != null && comp.getRoom().getComponent() == null){
						//							System.out.println("GridPanel.PopupMenuListener.addEComp");
						//							comp.getRoom().setComponent(comp);
						//							addComponentToPoint(comp);
						//						}
						//					}
					}
					_parent.updatePropertiesPanel();
					_parent.getPropertiesPanel().reCalculateVentilation(null);
					repaintClip(SELECTION);
				}
				else if (e.getSource() == _demper){
					addDemper(_currentSelectie.getComponent());
				}
				else if (e.getSource() == _collector){
					if (_currentSelectie.getComponent() instanceof ColComponent)
						_parent.addUndo(Edit.COMPONENT_CHANGE_COLLECTOR, _currentSelectie.getComponent());
					//				else
					//					_parent.addUndo(Edit.COMPONENT_ADD, _currentSelectie.getComponent());
					new CollectorDialog(GridPanel.this).setVisible(true);
				}
			}
			_popupVisible = false;			
		}	
	}
	public void setMovingProcess(){
		_movingProcess = true;
	}

	public void setBreakingProcess(){
		_breakingProcess = true;
	}
	/**
	 * Zet de popup menus aktief of passief al naargelang de selectie
	 */
	private void setPopupMenusEnable(){
		if (_pressSelectie.getComponent().getType() == VComponentType.VENTILATOR) {
			_delete.setEnabled(false);
			_move.setEnabled(false);
			_selectSpecial.setEnabled(false);
			_addRoom.setEnabled(false);
			_paste.setEnabled(_selectieListCopy.size() > 0);
			_collector.setEnabled(false);
		}
		else if (_pressSelectie.isMidPoint() && _pressSelectie.getComponent().isConnection()){
			_copy.setEnabled(false);
			_cut.setEnabled(false);
			_paste.setEnabled(false);
		}
		else {
			_delete.setEnabled(true);
			_delete.setText(Lang2.getString("Grid.popup_delete") + _pressSelectie.getComponent().getType().toString()); //$NON-NLS-1$
			_selectSpecial.setEnabled(_pressSelectie.getComponent().getType() != VComponentType.T_STUK);
			_move.setEnabled(!_pressSelectie.isMidPoint());
			_addRoom.setEnabled(!_pressSelectie.isMidPoint() && 
					(!Program.getVentilatie().isFlowAllAssigned(_parent.getFlowType()) 
					|| !Program.getVentilatie().isOutsideValveAssigned(_parent.getFlowType())));
			_paste.setEnabled(_selectieListCopy.size() > 0);
			_collector.setEnabled(true);
		}
		_break.setEnabled(_currentSelectie.getComponent().getType().isTube());
		_demper.setEnabled(_pressSelectie.getComponent().getType().isTube() && 
				_pressSelectie.getComponent().getBoreIn() > 0 &&
				Program.getVentilatie().isCalculateDone(_parent.getFlowType()));
	}
	/**
	 * Herberekent de ventilatie indien alle ventielen toegewezen zijn en er een buitenrooster is.
	 * @param component: herberekening van deze component
	 */
	public void reCalculateVentilation(VComponent component){
		_parent.updatePropertiesPanel();
		_parent.getPropertiesPanel().reCalculateVentilation(component);
	}
	/**
	 * Verwijdert de component van de lijst bij de ventilatie
	 * @param component
	 * @param addUndo: het verwijderen wordt toegevoegd aan de undo-list
	 */
	public void removeComponent(VComponent component, boolean addUndo, boolean removeFromSelectieList){
		if (component.getType() == VComponentType.VENTILATOR) return;
		if (addUndo) _parent.addUndo(Edit.COMPONENT_REMOVE, component);
		
		if (component instanceof ColComponent) ((ColComponent) component).removeRooms();
		else if (component.getRoom() != null) component.getRoom().setComponent(null);
		
		_parent.getComponentList().remove(component);
		if (removeFromSelectieList) removeFromSelectieList(component);
		LogPanel.line(Lang2.getString("Grid.log_deleteComp")+ component.getNameAndPoints()); //$NON-NLS-1$
		_parent.updatePropertiesPanel();
		_currentSelectie = null;
		_parent.getPropertiesPanel().disableComponentInWarningList(component);
//		_parent.getPropertiesPanel().checkVentilatie(null);
//		_parent.getPropertiesPanel().setComponentInfo(null);
		
	}
	/**
	 * Verwijdert de geselecteerde componenten uit de lijst van de ventilatie
	 */
	public void removeSelectedComponents(){
		_parent.addUndo(Edit.COMPONENTS_REMOVE, getSelectedComponents());
		for (Selectie selectie : _selectieList) {
			removeComponent(selectie.getComponent(), false, false);					
		}
		if (_selectieList.size() == 0 && _currentSelectie != null) 
			removeComponent(_currentSelectie.getComponent(), false, false);
		_selectieList.clear();
	}
	/**
	 * Voegt een component toe aan de lijst bij de ventilatie
	 * @param component: indien null, wordt een buis toegevoegd op basis van de geselecteerde punten
	 * @param addUndo: het toevoegen wordt toegevoegd aan de undo-list
	 */
	public VComponent addComponent(VComponent component, boolean addUndo){
		if (component == null){
			if (!_currentSelectie.getPoint().equals(_releasePoint3D)){
				component = new VComponent(
						VComponentType.KANAAL, _currentSelectie.getPoint(), _releasePoint3D);
			}
		}
		if (component == null){
			System.out.println("GridPanel.addComponent(), currentselectiepoint=" + _currentSelectie.getPoint().toString()
					+ ", releasepoint=" + _releasePoint3D.toString());
		}
		else if (component.isTube() && component.start.equals(component.end)){
			LogPanel.exception(Lang2.getString("Grid.log_nolengthComp") + component.getNameAndPoints());
		}
//		else if (component.isTube() && Program.getVentilatie().isLineOverlap(getFlowType(), component)){
//		LogPanel.exception(Lang2.getString("Grid.log_overlapComp") + component.getNameAndPoints());
//	}
		else {
			_parent.setMousePositionInfo(component.start.getDistanceTo(component.end));
			if (addUndo) _parent.addUndo(Edit.COMPONENT_ADD, component);
			_parent.getComponentList().add(component);
			LogPanel.line(Lang2.getString("Grid.log_newComp")+ component.getNameAndPoints()); //$NON-NLS-1$
//			_parent.getPropertiesPanel().checkVentilatie(component);
//			reCalculateVentilation(component);
			if (component instanceof EComponent){
				if (component.getRoom() != null)
					component.getRoom().setComponent(component);
				_parent.updatePropertiesPanel();
				reCalculateVentilation(new VComponent()); //met null -> recalculation
			}
			//Update gebeurt reeds in collectordialoog, maar moet toch gebeuren i.g.v. undo
			else if (component instanceof ColComponent){ 
				_parent.updatePropertiesPanel();
			}
			else {
				reCalculateVentilation(component);
			}
			_parent.getPropertiesPanel().setComponentInfo(component);
			Program.setDeleteAllMenuItemEnabled(true);
			ExtraComponent extra = Program.getVentilatie().getLineOverlap(getFlowType(), component);
			if (extra.getComponent() != null)
				LogPanel.subLine(extra.toString());
		}
		return component;
	}
	/**
	 * Voegt gekopieerde componenten toe bij het plakken (zonder diameter, naam, merk)
	 * @param component
	 */
	private void addUndefinedTubeToSelectedPoint(VComponent component){
		VComponent newComponent = component.clone();
		newComponent.setType(VComponentType.KANAAL);
		newComponent.setUndefined();
		newComponent.setBores(0);
		newComponent.move(_startCopyPoint3D, _currentSelectie.getPoint());
		_parent.getComponentList().add(newComponent);
		LogPanel.line("Voeg eindcomponent toe" + newComponent.getNameAndPoints());
	}
	/**
	 * Voegt een eindcomponent of collector toe op het punt waar de muisknop gelost werd
	 * Er wordt een undo-object toegevoegd
	 * @param component
	 */
	public void addEndComponent(VComponent component){
			component.setPoint(_releasePoint3D.clone());
			addComponent(component, true);
	}
	/**
	 * Breekt een buis in 2: het breekpunt vormt dan het eindpunt van de bestaande buis,
	 * het eindpunt van de bestaande buis vormt het eindpunt van de nieuwe buis
	 */
	private void breakComponent(){
		VComponent component = _currentSelectie.getComponent();
		VPoint3D breakpoint = component.getBreakPoint(_breakPointSnap, SNAP);
//		System.out.println("GridPanel.breakComponent() breakpoint: " + breakpoint.toString());
//		System.out.println("component: end="+component.end.toString()+", start="+component.start.toString());
		VComponent newComp = component.copy( breakpoint.clone(), component.end.clone()); //eigenlijk wordt end al ingesteld door copy.clone
		ArrayList<VComponent> list = new ArrayList<VComponent>();
		list.add(component);
		list.add(newComp);
		_parent.addUndo(Edit.COMPONENT_BREAK, list);
		component.end = breakpoint.clone();	
		LogPanel.line("Breek lijn: " + component.getNameAndPoints());
		addComponent(newComp, false);
		_breakPoint2D = null;
	}
	/**
	 * Verplaats geselecteerde componenten (of 1 punt als het een buis is) 
	 * naar het verschoven punt
	 * @param point
	 */
	private void moveComponent(VPoint3D point){
		_parent.addUndo(Edit.COMPONENT_POINTS, getSelectedComponents());
		VComponent selectedComponent = null;
		for (Selectie selectie : _selectieList) {
			VComponent component = selectie.getComponent();
			if (_startMovePoint3D.equals(component.start)){
//				_parent.addUndo(Edit.COMPONENT_POINTS, component);
				if (component.isTube())	component.start = point.clone();
				else component.setPoint(point.clone());
				selectedComponent = component;
			}
			else if (_startMovePoint3D.equals(component.end)){
//				_parent.addUndo(Edit.COMPONENT_POINTS, component);
				if (component.isTube())	component.end = point.clone();
				else component.setPoint(point.clone());
				selectedComponent = component;
			}
			if (component.isTube() && component.start.equals(component.end))
				removeComponent(component, false, false);
			else if (component instanceof ColComponent)
				((ColComponent)component).moveFlexibels();
		}
		LogPanel.line("Verplaats punt " + _startMovePoint3D.toString() + " van: " + getSelectieList());
		_startMovePoint3D = null;
		reCalculateVentilation(null);
		_parent.getPropertiesPanel().setComponentInfo(selectedComponent);
		removeNonExistingComponentsFromSelectieList();
	}
	/**
	 * Maakt van de geselecteerde buis een geluidsdemper
	 * @param component
	 */
	private void addDemper(VComponent selectedComponent) {
		VComponent demperTest = Program.getDatabase(Datatype.ACTIVE).getSilencerNoLongerThenTube(selectedComponent);
		VComponent demper = null;
		if (demperTest != null){
			demper = demperTest.clone();
			demper.start = selectedComponent.getStartPoint();
			demper.end = selectedComponent.getLinePoint(VLine3D.START, (float)(demper.getComponentLength()) / 1000);
			//zorg voor backup
			ArrayList<VComponent> list = new ArrayList<VComponent>();
			list.add(selectedComponent);
			list.add(demper);
			_parent.addUndo(Edit.COMPONENT_SILENCER, list);
			// wijzig startpunt oorspronkelijk kanaal
			selectedComponent.start = demper.getEndPoint();
			LogPanel.line(Lang3.getString("Edit.compSilencer") + demper.getNameAndPoints());
			VComponent comp = addComponent(demper, false);
			selectComponents(new VComponent[]{comp});
		}
	}
	/**
	 * Wijzigt start- en eindpunt van de originele component a.h.v. een clone
	 * Als de pointer niet voorkomt in de ventilatielijst, wordt de clone ingevoegd 
	 * @param pointer: pointer naar de component van de ventilatie
	 * @param newComp: clone van de component voor de wijziging
	 */
	public void restorePoints(VComponent pointer, VComponent newComp){
		boolean exist = false;
		for (VComponent oldComp : _parent.getComponentList()) {
			if (pointer == oldComp){
				oldComp.start = newComp.getStartPoint();
				oldComp.end = newComp.getEndPoint();
				exist = true;
				break;
			}
		}
		if (!exist) addComponent(newComp, false);
	}
	/**
	 * Verandert de eigenschappen van de originele component a.h.v. een clone
	 * @param pointer: pointer naar de component van de ventilatie
	 * @param newComp: clone van de component voor de wijziging
	 */
//	public void restoreProperties(VComponent pointer, VComponent newComp){//??waarom zoeken naar pointer
//		for (VComponent oldComp : _parent.getComponentList()) {
//			if (pointer == oldComp){
//				oldComp.setProperties(newComp);
//				
//				break;
//			}
//		}
//	}
	/**
	 * Wijzigt start- en eindpunt van de originele component a.h.v. een clone
	 * Als de clone niet voorkomt in de ventilatielijst, wordt de clone ingevoegd 
	 * @param pointer: pointer naar de component van de ventilatie
	 * @param newComp: clone van de component voor de wijziging
	 * @param add: indien de pointer niet gevonden wordt, voeg toe (true) of verwijder (false)
	 */
//	public void restoreComponent(VComponent pointer, VComponent newComp, boolean add){
//		boolean exist = false;
//		for (VComponent oldComp : _parent.getComponentList()) {
//			if (pointer == oldComp){
//				oldComp.start = newComp.getStartPoint();
//				oldComp.end = newComp.getEndPoint();
//				exist = true;
//				break;
//			}
//		}
//		if (!exist){
//			if (add) addComponent(newComp, false);
//			else removeComponent(pointer, false, true);
//		}
//	}

	private void selectTracePart(){
		JOptionPane.showMessageDialog(null, "selecteer tussen componenten");
	}
	/**
	 * Voegt de componenten toe aan de selectielijst, welke in het kanaalstuk
	 * (tussen T's, ventilator of eindscomponenten) van het geselecteerde 
	 * component liggen
	 */
	private void selectCurrentKanaalstuk(){
		ArrayList<VComponent> list = Program.getVentilatie().
				selectCurrentKanaalstuk(getFlowType(), _currentSelectie.getComponent());
		_selectieList.clear();
		for (VComponent vComponent : list) {
			_selectieList.add(new Selectie(vComponent, VLine3D.MID));
		}
		LogPanel.line("Selecteer kanaalstuk: " + getSelectieList());
		draw(true);
		_parent.getPropertiesPanel().setSelectedComponentInfo(_selectieList, Selectie.TRACE_SELECTION);
		
	}
	/**
	 * Voegt een selectie toe aan de selectielijst na controle op dubbels
	 * Als de lijst leeg is, wordt ook de huidige selectie toegevoegd
	 * @param selectie
	 */
	private void addToSelectieList(Selectie selectie){
		if (_selectieList.size() == 0 && _currentSelectie != null) {
			_selectieList.add(_currentSelectie);
		}
		if (selectie != null) {
			boolean addSel = true;
			for (Selectie sel : _selectieList) {
				if (sel.equals(selectie)) {
					addSel = false;
					break;
				}
			}
			if (addSel) {
				_selectieList.add(selectie);
			}
		}
	}
	/**
	 * Voegt een selectie toe aan de tijdelijke selectielijst na controle op dubbels
	 * @param selectie
	 * @param noShift: moet de gevonden selectie niet naar achter in de lijst?
	 * @return true als de selectie toegevoegd is, als selecties omgewisseld zijn
	 * of als noShift=true
	 */
	private boolean addToSelectieListTemp(Selectie selectie, boolean noShift){
		if (selectie != null) {
//			boolean addSel = true;
			Selectie oldSelectie = null; 
			for (Selectie sel : _selectieListTemp) {
				if (sel.equals(selectie)) {
//					addSel = false;
					oldSelectie = sel;
					break;
				}
			}
			if (oldSelectie == null) {
				_selectieListTemp.add(selectie);
				return true;
			}
			else if (!noShift){
				_selectieListTemp.remove(oldSelectie);
				_selectieListTemp.add(oldSelectie);
				return true;
			}
		}
		if (noShift) return true;
		return false;
	}
	/**
	 * Verwijdert de component (indien aanwezig) uit de selectielijst
	 * @param component
	 * @return true indien de component aanwezig was en verwijderd is
	 */
	private boolean removeFromSelectieList(VComponent component){
		Selectie toRemove = null;
		for (Selectie selectie : _selectieList) {
			if (component.equals(selectie.getComponent())){
				toRemove = selectie;
				break;
			}
		}
		if (toRemove != null) {
			_parent.getPropertiesPanel().hideComponentInfo(toRemove.getComponent());
			_selectieList.remove(toRemove);
			return true;
		}
		return false;
	}
	/**
	 * Verwijdert de componenten die niet meer bestaan uit de selectielijst
	 * Als van een te verwijderen component de eigenschappen worden getoond, wordt het
	 * eigenschappenvenster leeg gemaakt
	 */
	public void removeNonExistingComponentsFromSelectieList(){
		ArrayList<Selectie> listToRemove = new ArrayList<Selectie>();
		for (Selectie sel : _selectieList) {
			boolean exist = false;
			for (VComponent component : _parent.getComponentList()) {
				if (sel.getComponent().equals(component)){
					exist = true;
					break;
				}
			}
			if (!exist)	listToRemove.add(sel);
		}
		for (Selectie toRemove : listToRemove) {
			_parent.getPropertiesPanel().hideComponentInfo(toRemove.getComponent());
			_selectieList.remove(toRemove);
		}
	}
	/**
	 * Maakt de selectielijst leeg en voegt de componenten toe aan de lijst
	 * @param selectedValues: VComponent objecten
	 */
	public void selectComponents(Object[] selectedValues) {
		_selectieList.clear();
		_currentSelectie = null;
		for (Object object : selectedValues) {
			addToSelectieList(new Selectie((VComponent) object, VComponent.END));			
		}
		if (selectedValues.length == 1){
			_parent.getPropertiesPanel().setSelectedComponentInfo(_selectieList, Selectie.MULTI_SELECTION);
		}
	}
	/**
	 * Verandert de eigenschappen van het geselecteerde component
	 * @param newComponent: waarvan de eigenschappen overgenomen worden
	 * @return: het veranderde component
	 */
	public VComponent changeSelectedComponent(VComponent newComponent){
		if (_selectieList.size() > 0) {
			VComponent comp  = _selectieList.get(0).getComponent();
//			System.out.println("GridPanel.changeSelectedComponent(1)"+ comp.getNameAndPoints());
//			System.out.println("GridPanel.changeSelectedComponent(2)"+ newComponent.getNameAndPoints());
			if (comp.getType() == VComponentType.DEMPER && 
					comp.getComponentLength() != newComponent.getComponentLength()){
				VComponent nextComp = Program.getVentilatie().getNextComp(getFlowType(), comp);
				System.out.println("GridPanel.changeSelectedComponent()"+nextComp.getAngle(comp));
				//als de volgende comp een buis is, de hoek 180° is, en de lengte van de nieuwe demper korter
				//is dan de lengte van de oude demper + de buis, dan wordt de nieuwe demper gebruikt en start van de buis verplaatst
				if (nextComp != null && nextComp.isTube() && comp.getAngle(nextComp) == 180 &&
						comp.getComponentLength() + nextComp.getLength() * 1000 >= 
						newComponent.getComponentLength()) {
					LogPanel.startLine("Wijziging: " + comp.getDescription());
					ArrayList<VComponent> list = new ArrayList<VComponent>();
					list.add(comp);
					list.add(nextComp);
					_parent.addUndo(Edit.COMPONENT_CHANGE_SILENCER, list);
					comp.end = comp.getLinePoint(VLine3D.START, 
							(float)(newComponent.getComponentLength()) / 1000);
					nextComp.start = comp.getEndPoint();
					comp.setProperties(newComponent);
					LogPanel.end(" -> " + comp.getDescription());
//					System.out.println("GridPanel.changeSelectedComponent(3)"+nextComp.getNameAndPoints());
				}
			}
			else {
				LogPanel.startLine("Wijziging: " + comp.getDescription());
				_parent.addUndo(Edit.COMPONENT_CHANGE, comp);
				comp.setProperties(newComponent);
				LogPanel.end(" -> " + comp.getDescription());
			}
			return _selectieList.get(0).getComponent();
		}
		return null;
	}
	/**
	 * Verandert de eigenschappen van het geselecteerde componenten
	 * @param newComponent: waarvan de eigenschappen overgenomen worden
	 * @param selectieType
	 * @return: het eerste veranderde component
	 */
	public VComponent changeSelectedComponents(VComponent newComponent, int selectieType){
		VComponent compReturn = null;
		if (_selectieList.size() > 0) {
			_parent.addUndo(Edit.COMPONENTS_CHANGE, getSelectedComponents());
			LogPanel.line("Wijziging componenten:");
			for (Selectie selectie : _selectieList) {
				VComponent comp  = selectie.getComponent();
				if (comp.getType() == newComponent.getType()){
					comp.setProperties(newComponent);
					LogPanel.subLine(comp.getDescription() + " -> " + newComponent.getDescription());
					if (compReturn == null) compReturn = comp;
				}
				else if (newComponent.isTube() && comp.isEndComponent()){//if of else if???
					for (VComponent endComp : 
							Program.getDatabase(Datatype.ACTIVE).getComponentsFromBrand(comp.getBrand(), comp)) {
						if (endComp.getBoreIn() == newComponent.getBoreIn()){
							comp.setProperties(endComp);
							LogPanel.subLine(comp.getDescription() + " -> " + endComp.getDescription());
						}
					}
				}
				else if (newComponent.isTube() && comp.getType() == VComponentType.DEMPER){
					for (VComponent demper : 
							Program.getDatabase(Datatype.ACTIVE).getComponentsFromBrand(comp.getBrand(), comp)) {
						if (demper.getBoreIn() == newComponent.getBoreIn() &&
								demper.getComponentLength() == comp.getComponentLength()){
							comp.setProperties(demper);
							LogPanel.subLine(comp.getDescription() + " -> " + demper.getDescription());
						}
					}
				}
				else if (selectieType == Selectie.TRACE_SELECTION && 
						comp instanceof BComponent){
					comp.setBores(0);
				}
					
				System.out.println("GridPanel:"+comp.toString());
			}
		}
		return compReturn;
	}
	/**
	 * Maakt de lijst met geselekteerde punten of componenten leeg, 
	 * evenals de lijst met gekopieerde componenten
	 */
	public void clearSelecties(){
		_selectieList.clear();
		_selectieListCopy.clear();
		_currentSelectie = null;
		_parent.getPropertiesPanel().setComponentInfo(null);
	}

	/**
	 * Geeft een lijst met de geselecteerde componenten
	 * @return
	 */
	public ArrayList<VComponent> getSelectedComponents(){
		ArrayList<VComponent> list = new ArrayList<VComponent>();
		for (Selectie selectie : _selectieList) {
			list.add(selectie.getComponent());
		}
		return list;
	}
	private String getSelectieList(){
		String s = "";
		for (Selectie selectie : _selectieList) {
			s += selectie.getComponent().getName() + "; ";
		}
		return s;
	}
	/**
	 * Vraagt de huidige selectie op
	 * @return
	 */
	public Selectie getCurrentSelectie(){
		return _currentSelectie;
	}
	/**
	 * Is er een component geselecteerd?
	 * @return
	 */
	public boolean isComponentSelected(){
		return _selectieList.size() > 0 || _currentSelectie != null;
	}
	/**
	 * Is deze component geselecteerd?
	 * @param component
	 * @return
	 */
	public boolean isComponentSelected(VComponent component){
		for (Selectie selectie : _selectieList) {
			if (selectie.getComponent() == component) return true;
		}
		return false;
	}
	
	public float getZoomFactor(){
		return _zoomFactor;
	}
	public MyFont getCurrentFont(){
		return _currentFont;
	}
	public void setCurrentFont(MyFont font){
		_currentFont = font;
	}
	/**
	 * Stelt de rasterlijnen in of bepaalt of het ander tracé in de achtergrond wordt 
	 * getekend, of bepaalt of de drukverschillen getoond worden
	 * @param view: FlowPanel.BACKGROUND of TOGGLE_VIEW (volgend zicht) of SHOW_PRESSURES
	 * @return true als het andere tracé getekend wordt
	 */
	public boolean setView(int view){
//		System.out.println("GridPanel.setView()");
		boolean hide = false;
		if (view == FlowPanel.BACKGROUND) {
			_background = !_background;
			hide = _background;
		}
		else if (view == FlowPanel.TOGGLE_VIEW){
			if (_viewMode == FlowPanel.BEFORE_BEHIND_YZ) _viewMode = FlowPanel.TOTAL_VIEW;//HORIZONTAL_XY;
			else _viewMode++;
		}
		else if (view == FlowPanel.SHOW_PRESSURES){
			_allPressures = !_allPressures;
			hide = _allPressures;
		}
//		else _view = view;
		draw(true);
		return hide;
	}
	/**
	 * Geeft het zicht aarin het raster getekend is
	 * @return FlowPanel.HORIZONTAL, LEFT_RIGHT, BEFORE_BEHIND
	 */
	public int getView(){
		return _viewMode;
	}
	/**
	 * Geeft aan of het de toevoer of de afvoer betreft
	 * @return VentilationType.TRACE_IN, TRACE_OUT
	 */
	public FlowType getFlowType(){
		return _parent.getFlowType();
	}
	/**
	 * Geeft het parent flowPanel terug
	 * @return 
	 */
	public FlowPanel getFlowPanel(){
		return _parent;
	}
	/**
	 * Tekent het kanalennet op een image.
	 * De zoomfactor wordt tijdelijk vergroot met een factor om scherpere lijnen te bekomen
	 * @return
	 */
	public BufferedImage getImage() {
		BufferedImage bufferedImage = null;
		int zoom = 4;
//		_currentFont.setSizeByFactor(zoom);
//		MyFont.resizedFont(_currentFont, zoom, true);
//		_currentFont = _currentFont.resizeFont(zoom, true);
		_zoomFactor *= zoom;
		setGridValues();
		Rectangle2D viewRect = _parent.getViewport().getViewRect();
		int width = (int)(viewRect.getWidth() * zoom);
		int height = (int)(viewRect.getHeight() * zoom);
		try {
			bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
			Graphics2D g2 = bufferedImage.createGraphics();
			g2.setFont(_currentFont.getResizedFont(zoom, true));
			g2.translate(-viewRect.getX() * zoom, -viewRect.getY() * zoom);
			g2.setPaint(Color.WHITE);
			g2.fill(new Rectangle2D.Float(0, 0, width, height));
			g2.setStroke(Program.preferences.getGridStroke(zoom));
			drawGrid(g2);
			g2.setStroke(Program.preferences.getGenericStroke(zoom));
			if (_background) drawBackgroundComponents(g2);
			drawComponents(g2);
			
			g2.dispose();
		} 
		catch (Exception e) {
			LogPanel.exception("CreateBufferedImage: " + width + " x " + height + " pixels (" + 
					e.toString() + ")");
		}
		_zoomFactor /= zoom;
		setGridValues();
		return bufferedImage;
	}

}

