/* $Id: LineSegment.java 472 2011-11-19 13:32:43Z shayashi $ */
package smart_gs.logical;

import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import java.awt.Polygon;

import smart_gs.drawing_tool.ExLine2D;
import smart_gs.drawing_tool.view.LineSegView;
import smart_gs.drawing_tool.view.View;
import smart_gs.smleditor.swingui.LineSegEditor;
import smart_gs.util.GSLog;
import sml_editor.logical.LineDirection;

public class LineSegmentForEdit {
	private List<Point2D> points;
	private List<Point2D> real_points;
	protected View view;
	private LineDirection lineDirection;
	private GSLog log = GSLog.getInstance();
	private boolean visibility = true;
	
//	This constructor is used, when a brand new line segment is created from a list of points.
	public LineSegmentForEdit(LineSegEditor editor, List<Point2D> points,LineDirection lineDirection){
		this.points = points;

		if (points.size()<3) GSLog.getInstance().error("Line Segments must have more than three vertexes.");
		
		AffineTransform at = editor.createAffinetransformForTilt();
		AffineTransform invat = null;
		try {
			invat = at.createInverse();
		} catch (NoninvertibleTransformException e) {
			log.error("Cannot create inverse affine transformation");
			e.printStackTrace();
		}
		this.real_points = tiltPoints(invat,points);
		
		this.createView();
		this.lineDirection = lineDirection;
	}
	
	public List<Point2D> getReal_points() {
		return this.real_points;
	}
	
// This constructor is used to create a tilted copy of an *existing* line segment.
	public LineSegmentForEdit(LineSegEditor editor, LineSegmentForEdit line, LineDirection lineDirection) {
		this.real_points = line.getReal_points();

		AffineTransform at = editor.createAffinetransformForTilt();
		this.points = tiltPoints(at,real_points);
		
		this.createView();
		this.lineDirection = lineDirection;
	}

//      This constructor is used to create a *non-tilted* line segment from information in Segfo or XML file.
//  Combination of this and the previous one will construct a tilted line from information in Segfo or XML file.

	public LineSegmentForEdit(List<Point2D> points, LineDirection lineDirection, int index){
		this.real_points = points;
		this.points = this.real_points;
		this.createView(index);
		this.lineDirection = lineDirection;
	}

//	This constructor is used to create a deep copy.
	public LineSegmentForEdit(List<Point2D> real_points, List<Point2D> points, LineDirection lineDirection, View view) {
		this.real_points = real_points;
		this.points = points;
		this.view = view;
		this.lineDirection = lineDirection;
	}

	private List<Point2D> tiltPoints(AffineTransform atForTilt,	List<Point2D> points) {
		List<Point2D> tilted_points = new ArrayList<Point2D>();
		for (Point2D point : points){
			Point2D ans_point = new Point2D.Double(point.getX(),point.getY());
			atForTilt.transform(point,ans_point);
			tilted_points.add(ans_point);
		}
		return tilted_points;
	}

	private void createView(int index) {
		List<ExLine2D> lines = new ArrayList<ExLine2D>();
		for(int i=0;i<this.points.size()-1;i++){
			Point2D start = this.points.get(i);
			Point2D end = this.points.get(i+1);
			ExLine2D line = new ExLine2D(start,end);
			lines.add(line);
		}
		this.view = new LineSegView(lines,this.lineDirection, index);
	}

	public void setIndex(int i) {
		((LineSegView)view).setIndex(i);
	}
	
	public int getIndex() {
		return ((LineSegView)view).getIndex();
	}

	private void createView(){
		List<ExLine2D> lines = new ArrayList<ExLine2D>();
		for(int i=0;i<this.points.size()-1;i++){
			Point2D start = this.points.get(i);
			Point2D end = this.points.get(i+1);
			ExLine2D line = new ExLine2D(start,end);
			lines.add(line);
		}
		this.view = new LineSegView(lines);
	}
	
	public List<Point2D> getPoints() {
		return this.points;
	}
	
	public List<Point2D> getRealPoints() {
		return this.real_points;
	}
	
	public LineSegView getView(){
		return (LineSegView)this.view;
	}
	
	public LineSegView getViewDeepCopy(){
		return ((LineSegView)this.view).deepCopy();
	}
	
	public void setIsSelected(boolean b) {
		this.view.setIsSelected(b);
	}
	
	public boolean isSelected() {
		return this.view.isSelected();
	}
	
	//20110221 shimizu add
	public void changeSize(double m){
	  List<Point2D> points = this.getPoints();
	  for(int i=0;i<points.size();i++){
		  Point2D point = points.get(i);
		  double x = point.getX()*m;
		  double y = point.getY()*m;
		  point.setLocation(x,y);
	  }
	  int index = this.getView().getIndex();
	  this.createView(index);
	}
	
	public void slideLine(double x1, double y1){
		List<Point2D> points = this.getPoints();
		for(int i=0;i<points.size();i++){
			Point2D point = points.get(i);
			double x = point.getX() + x1;
			double y = point.getY() + y1;
			point.setLocation(x,y);
		}
		int index = this.getView().getIndex();
		this.createView(index);
	}
	

	public void rotateLine(AffineTransform transformation) {
		List<Point2D> points = this.getPoints();
		for(int i=0;i<points.size();i++){
			Point2D point = points.get(i);
			Point2D tmp = point;
			transformation.transform(point,tmp);
			points.set(i,tmp);
		}
		int index = this.getView().getIndex();
		this.createView(index);
	}
	
	
	
	//20100825 shimizu add
	public Polygon getPolygon(){
		Polygon polygon = new Polygon();
		for(int i=0; i<this.points.size()-1;i++){
			int x = (int)this.points.get(i).getX();
			int y = (int)this.points.get(i).getY();
			polygon.addPoint(x, y);
		}
		return polygon;
	}
	//20101110 shimizu wrote;
	public void addPoint(Point2D point){
		List<ExLine2D> lines = new ArrayList<ExLine2D>();
		for(int i=0;i<this.points.size()-1;i++){
			Point2D start = this.points.get(i);
			Point2D end = this.points.get(i+1);
			ExLine2D line = new ExLine2D(start,end);
			lines.add(line);
		}
	    double dist = lines.get(0).ptLineDist(point);
	    int x = 0;
		for(int i=1;i<lines.size();i++){
			Double d = lines.get(i).ptLineDist(point);
			if(d<dist){
				dist = d;
				x = i;
			}
			
		}
		this.points.add(x+1,point);
		int index = this.getView().getIndex();
		this.createView(index);
	}

	public boolean contains(Point2D point2d) {
		return this.view.contains(point2d);
	}

	public LineDirection getLineDirection() {
		return this.lineDirection;
	}

	public LineSegmentForEdit deepCopy() {
		List<Point2D> real_points_ans = new ArrayList<Point2D>();
		List<Point2D> points_ans = new ArrayList<Point2D>();
		
		for (Point2D point : getRealPoints()) {
			real_points_ans.add(new Point2D.Double(point.getX(),point.getY()));
		}
		
		for (Point2D point : getPoints()) {
			points_ans.add(new Point2D.Double(point.getX(),point.getY()));
		}

		return new LineSegmentForEdit(real_points_ans,points_ans,getLineDirection(),getView());
	}

	public void setVisible() {
		this.visibility = true;
	}
	
	public void setInvisible() {
		this.visibility = false;
	}
	
	public boolean isVisible() {
		return this.visibility;
	}

	public boolean isInvisible() {
		return ! this.visibility;
	}
	
}

