package j3deditor.bin.hierarchy;

import j3deditor.bin.editor.Scene2D;
import j3deditor.bin.hierarchy.util.J3DePoint2D;
import j3deditor.bin.util.Utilities;

import java.awt.Color;

import java.util.ArrayList;

import javax.vecmath.Point2f;

/**
 * Tool for manipulating data contained in the
 * specified <code>J3DeGeometryArray</code>.
 * 
 * @author Risto Seene
 * 
 * @see j3deditor.bin.hierarchy.J3DeGeometryArray
 */
public class GeometryManipulator{

	/** Transform mode, specifies that vertices can not be transformed. */
	public static final int NONE = 0;

	/** Transform mode, specifies that selected vertices can be translated. */
	public static final int TRANSLATE = 1;

	/** Transform mode, specifies that selected vertices can be rotated. */
	public static final int ROTATE = 2;

	/** Transform mode, specifies that selected vertices can be scaled. */
	public static final int SCALE = 3;


	/** Specifies that the manipulation target of this GeometryManipulator are vertex coordinates. */
	public static final int VERT_COORDS = 0;

	/** Specifies that the manipulation target of this GeometryManipulator are texture coordinates. */
	public static final int TEX_COORDS = 1;


	/** geometry which data this <code>GeometryManipulator</code> manipulates */
	private J3DeGeometryArray g;

	/** manipulation target (vertex coordinates or texture coordinates) */
	private int mTarget;

	/** texture coordinate set which coordinates can be manipulated */
	private int texCoordSet = 0;

	/** specifies how much polygonal mesh is scaled on each axis */
	private int[] scale;


	/** vertex that represents the center of selected vertices */
	private Vertex center;

	/** selected vertices */
	private Vertex[] selectedVertices;

	/** selected faces */
	private Vertex[][] selectedFaces;

	/** current transform mode */
	private int transformMode;

	/** type of the vertex state - vertex coordinates or texture coordinates */
	private int stateType;

	/** sticky vertices */
	private boolean stickyVerts = false;


	/**
	 * Creates an instance of <code>GeometryManipulator</code> which
	 * manipulates the data contained in the sepcified <code>J3DeGeometryArray</code>.
	 * 
	 * @param geometry
	 * 	<code>GeometryManipulator</code> which data can be manipulated
	 *        by this <code>GeometryManipulator</code>
	 * @param manipulationTarget
	 * 	specifies which coordinates can be manipulated with
	 *        <code>translateVertices()</code>, <code>scaleVertices()</code> and <code>rotateVertices()</code>
	 *        methods.<br/>
	 *        <code>VERT_COORDS</code> and <code>TEX_COORDS</code> can be used.
	 */
	public GeometryManipulator(J3DeGeometryArray geometry, int manipulationTarget){
		this.g = geometry;
		this.mTarget = manipulationTarget;
		if(manipulationTarget == GeometryManipulator.VERT_COORDS){
			this.stickyVerts = true;
		}
		this.stateType = Vertex.COORDINATE_VERTEX_STATE;
		if(this.mTarget == GeometryManipulator.TEX_COORDS){
			this.stateType = Vertex.TEXTURE_VERTEX_STATE;
		}

		this.selectedVertices = new Vertex[0];
		this.scale = new int[3];
		for(int i = 0; i < 3; i++){
			scale[i] = 1;
		}
	}

	/**
	 * Specifies the texture coordinate set which coordinates can be manipulated
	 * if this <code>GeometryManipulator</code> manipulates texture coordinates
	 * and geometry has texture coordinates.
	 * 
	 * @param set
	 * 	index of texture coordinate set
	 */
	public void setTexCoordSet(int set){
		this.texCoordSet = set;
	}

	/**
	 * Specifies how much polygonal mesh is scaled on each axis
	 * when used <code>scaleVertices</code> method.
	 * 
	 * @param axes
	 * 	multiplicator on each axis (3 axes: X, Y and Z)
	 */
	public void setScaleAxes(int[] axes){
		if(axes.length != 3){
			Utilities.out.throwWarning("Invalid number of scale axes!");
		}
		else {
			this.scale = axes;
		}
	}

	/**
	 * Returns the currently used scale axes multiplicators.
	 * 
	 * @return the currently used scale axes multiplicators
	 * 
	 * @see j3deditor.bin.hierarchy.GeometryManipulator#setScaleAxes(int[] axes)
	 */
	public int[] getScaleAxes(){
		return this.scale;
	}

	/**
	 * Creates and returns an array containing selected vertices.
	 * 
	 * @return an array of selected vertices
	 */
	private Vertex[] getSelectedVertices(){
		ArrayList<Vertex> vs = new ArrayList<Vertex>();
		int vertCount = this.getVertexCount();
		for(int i = 0; i < vertCount; i++){
			Vertex v = this.getVertex(i);
			if(v.getState(stateType) == Vertex.SELECTED){
				vs.add(v);
			}
		}

		return vs.toArray(new Vertex[vs.size()]);
	}

	/**
	 * Calculates the center of selected vertices and a vertex
	 * containing these coordinates.
	 * 
	 * @param vertices
	 * 	vertices which center will be calculated
	 * @return a vertex containing the coordinates of selection center
	 */
	private Vertex calculateCenter(Vertex[] vertices){
		if(vertices != null && vertices.length > 0){
			if(this.mTarget == GeometryManipulator.VERT_COORDS){
				float[] coords = new float[]{ vertices[0].getX(), vertices[0].getY(), vertices[0].getZ() };


				for(int i = 1; i < vertices.length; i++){
					Vertex v = vertices[i];
					coords[0] -= ((coords[0] - v.getX()) / (i + 1));
					coords[1] -= ((coords[1] - v.getY()) / (i + 1));
					coords[2] -= ((coords[2] - v.getZ()) / (i + 1));
				}
				return new VirtualVertex(this.g, coords[0], coords[1], coords[2], 0);
			}
			else if(this.mTarget == GeometryManipulator.TEX_COORDS){
				int texCoordType = this.g.getTexCoordType();
				float[][] tCoords = new float[this.g.getTexCoordSetCount()][texCoordType];
				for(int i = 0; i < this.g.getTexCoordSetCount(); i++){
					for(int j = 0; j < texCoordType; j++){
						tCoords[i][j] = vertices[0].getTex(j, i);
					}
				}
				for(int i = 1; i < vertices.length; i++){
					Vertex v = vertices[i];
					for(int j = 0; j < this.g.getTexCoordSetCount(); j++){
						for(int k = 0; k < texCoordType; k++){
							tCoords[j][k] -= ((tCoords[j][k] - v.getTex(k, j)) / (i + 1));
						}
					}
				}
				Vertex vv = new VirtualVertex(this.g, 0);
				for(int i = 0; i < this.g.getTexCoordSetCount(); i++){
					vv.setTexCoordinates(tCoords[i], i);
				}
				return vv;
			}
		}
		return new VirtualVertex(this.g, 0);
	}


	/**
	 * Returns the <code>J3DeGeometryArray</code> object which data this
	 * <code>GeometryManipulator</code> manipulates.
	 * 
	 * @return the <code>J3DeGeometryArray</code> object which data this
	 * 	<code>GeometryManipulator</code> manipulates
	 */
	public J3DeGeometryArray getGeometry(){
		return this.g;
	}

	/**
	 * Returns the number of valid vertices.
	 * 
	 * @return the number of valid vertices
	 */
	public int getVertexCount(){
		return this.g.getVertices().length;
	}

	/**
	 * Returns the number of vertices the
	 * <code>J3DeGeometryArray</code> contains.
	 * 
	 * @return the number of vertices the
	 * 	<code>J3DeGeometryArray</code> contains
	 *        
	 * @see javax.media.j3d.GeometryArray#getVertexCount()
	 */
	public int getAllVertCount(){
		return this.g.getVertexCount();
	}

	/**
	 * Returns a vertex at the specified index.
	 * 
	 * @param index
	 * 	index of requested vertex
	 * @return a vertex at the specified index
	 */
	public Vertex getVertex(int index){
		return this.g.getVertices()[index];
	}

	/**
	 * Sets the state of all "selected" vertices to "normal".
	 */
	public void deselectAllVertices(){
		int vertCount = this.getVertexCount();
		for(int i = 0; i < vertCount; i++){
			Vertex v = this.getVertex(i);
			if(v.getState(stateType) == Vertex.SELECTED){
				v.setState(Vertex.NORMAL, stateType);
			}
		}
		this.deselectFaces();
	}

	/**
	 * Returns an array that contains all selected vertices.
	 * 
	 * @return an array that contains all selected vertices
	 */
	public Vertex[] getTransformableVertices(){
		return selectedVertices;
	}

	/**
	 * Returns a vertex that represents the center of selected vertices.
	 * 
	 * @return a vertex that represents the center of selected vertices
	 */
	public Vertex getCenter(){
		return this.center;
	}

	/**
	 * Returns current transform mode.
	 * 
	 * @return current transform mode
	 */
	public int getTransformMode(){
		return transformMode;
	}

	/**
	 * Specifies the vertex manipulation mode.
	 * This mode also specifies the active mode of <code>View2D</code>
	 * objects which use this <code>GeometryManipulator</code>.
	 * 
	 * @param transformMode
	 * 	new vertex manipulation mode.<br/>
	 *        <code>NONE</code> - vertices cannot be transformed<br/>
	 *	<code>TRANSLATE</code> - vertices can be translated<br/>
	 *	<code>ROTATE</code> - vertices can be rotated<br/>
	 *	<code>SCALE</code> - vertices can be scaled
	 */
	public void setTransformMode(int transformMode){
		if(this.transformMode == 0){
			this.selectedVertices = this.getSelectedVertices();
			for(int i = 0; i < selectedVertices.length; i++){
				selectedVertices[i].setState(Vertex.TRANSFORMABLE, stateType);
			}
			if(this.selectedVertices.length > 0){
				this.transformMode = transformMode;
				this.center = calculateCenter(this.selectedVertices);
			}
		}
		else if(this.transformMode == transformMode){
			for(int i = 0; i < selectedVertices.length; i++){
				selectedVertices[i].setState(Vertex.SELECTED, stateType);
			}
			this.selectedVertices = new Vertex[0];
			this.transformMode = 0;
			this.center = null;
		}
	}

	/**
	 * All transformable (selected) vertices are translated
	 * using the specified value.
	 * 
	 * @param coordinateChange
	 * 	how much are vertices moved on each axis.<br/>
	 *        if manipulation target is vertex coordinates: X, Y and Z axes<br/>
	 *        if manipulation target is texture coordinate: S and T axes
	 */
	public void translateVertices(float[] coordinateChange){
		if(this.transformMode == GeometryManipulator.TRANSLATE){
			if(this.mTarget == GeometryManipulator.VERT_COORDS){
				for(int i = 0; i < this.selectedVertices.length; i++){
					Vertex v = this.selectedVertices[i];
					if(v.getState(stateType) == Vertex.TRANSFORMABLE){
						v.translate(coordinateChange);
					}
				}
			}
			else if(this.mTarget == GeometryManipulator.TEX_COORDS){
				for(int i = 0; i < this.selectedVertices.length; i++){
					Vertex v = this.selectedVertices[i];
					if(v.getState(stateType) == Vertex.TRANSFORMABLE){
						v.translateTex(coordinateChange, this.texCoordSet);
					}
				}
			}
		}
	}

	/**
	 * All transformable (selected) vertices are scaled
	 * using the specified value (relatively to selection center).
	 * 
	 * @param scaleChange
	 * 	new size of the scalable part of polygonal mesh.<br/>
	 *        <code>scaleChange < 1</code> - selected part of the polygonal mesh is made smaller<br/>
	 *        <code>scaleChange > 1</code> - selected part of the polygonal mesh is made larger
	 */
	public void scaleVertices(float scaleChange){
		if(this.transformMode == GeometryManipulator.SCALE){
			if(this.mTarget == GeometryManipulator.VERT_COORDS){
				float[] cCoords = this.getCenter().getCoordinates();
				for(int i = 0; i < this.selectedVertices.length; i++){
					Vertex v = this.selectedVertices[i];
					if(v.getState(stateType) == Vertex.TRANSFORMABLE){
						float[] coords = v.getCoordinates();
						for(int j = 0; j < coords.length; j++){
							coords[j] += ((coords[j] - cCoords[j]) * scaleChange * scale[j]);
						}
						v.setCoordinates(coords);
					}
				}
			}
			else if(this.mTarget == GeometryManipulator.TEX_COORDS){
				float[] cCoords = this.getCenter().getTexCoordinates(this.texCoordSet);
				for(int i = 0; i < this.selectedVertices.length; i++){
					Vertex v = this.selectedVertices[i];
					if(v.getState(stateType) == Vertex.TRANSFORMABLE){
						float[] coords = v.getTexCoordinates(this.texCoordSet);
						for(int j = 0; j < coords.length; j++){
							coords[j] += ((coords[j] - cCoords[j]) * scaleChange * scale[j]);
						}
						v.setTexCoordinates(coords, this.texCoordSet);
					}
				}
			}
		}
	}

	/**
	 * All transformable (selected) vertices are rotated around
	 * the specified axis by specified angle.
	 * Center of the selected vertices is used.
	 * 
	 * @param rotation
	 * 	rotation angle in radians
	 * @param axis
	 * 	1 - X-axis<br/>
	 *        2 - Y-axis<br/>
	 *        3 - Z-axis
	 */
	public void rotateVertices(float rotation, int axis){
		if(this.transformMode == GeometryManipulator.ROTATE){
			if(this.mTarget == GeometryManipulator.TEX_COORDS){
				float[] cCoords = this.center.getTexCoordinates(this.texCoordSet);
				Point2f center = new Point2f(cCoords[0], cCoords[1]);

				for(int i = 0; i < this.selectedVertices.length; i++){
					Vertex v = this.selectedVertices[i];
					float[] coords = v.getTexCoordinates(this.texCoordSet);
					Point2f point = rotateVertex(new Point2f(coords[0], coords[1]), center, rotation);
					coords[0] = point.getX();
					coords[1] = point.getY();
					v.setTexCoordinates(coords, this.texCoordSet);
				}
			}
			else {
				float[] cCoords = this.center.getCoordinates();
				for(int i = 0; i < this.selectedVertices.length; i++){
					Vertex v = this.selectedVertices[i];
					float[] coords = v.getCoordinates();
					if(axis == 1){
					          Point2f center = new Point2f(cCoords[2], cCoords[1]);
						Point2f point = rotateVertex(new Point2f(coords[2], coords[1]), center, rotation);
						coords[2] = point.getX();
						coords[1] = point.getY();
					}
					else if(axis == 2){
					          Point2f center = new Point2f(cCoords[0], cCoords[2]);
						Point2f point = rotateVertex(new Point2f(coords[0], coords[2]), center, rotation);
						coords[0] = point.getX();
						coords[2] = point.getY();
					}
					else if(axis == 3){
					          Point2f center = new Point2f(cCoords[0], cCoords[1]);
						Point2f point = rotateVertex(new Point2f(coords[0], coords[1]), center, rotation);
						coords[0] = point.getX();
						coords[1] = point.getY();
					}
					v.setCoordinates(coords);
				}
			}
		}
	}
	
	/**
	 * Rotates vertex in 2D space.
	 * 
	 * @param vertex
	 * 	the location of vertex
	 * @param axis
	 * 	the location of rotation axis
	 * @param angle
	 * 	rotation angle
	 * @return new location of vertex
	 */
	private Point2f rotateVertex(Point2f vertex, Point2f axis, float angle){
		vertex.sub(axis);
		vertex.set((float)(vertex.getX() * Math.cos(angle) + vertex.getY() * -Math.sin(angle)), (float)(vertex.getX() * Math.sin(angle) + vertex.getY() * Math.cos(angle)));
	          vertex.add(axis);
		
		return vertex;
	}

	/**
	 * Returns true if "sticky vertices" is enabled.
	 * 
	 * @return true if "sticky vertices" are enabled otherwise false
	 * 
	 * @see j3deditor.bin.hierarchy.GeometryManipulator#setStickyVertsEnabled(boolean stickyVerts)
	 */
	public boolean isStickyVertsEnabled(){
		return stickyVerts;
	}

	/**
	 * Specifies if the "sticky vertices" is enable or not.
	 * "Sticky vertices" means that all overlapping vertices are
	 * simultaenously selected, otherwise only the closest to camera
	 * is selected. Used by <code>VertexManipulator</code>.
	 * 
	 * @param stickyVerts
	 * 	true - enabled; otherwise not
	 * 
	 * @see j3deditor.bin.editor.util.VertexManipulator#selectVertex(J3DePoint2D p, Scene2D scene, boolean isShiftDown)
	 */
	public void setStickyVertsEnabled(boolean stickyVerts){
		this.stickyVerts = stickyVerts;
	}

	/**
	 * Adds a new vertex to the specified position.
	 * 
	 * @param x
	 * 	X-coordinate of the vertex
	 * @param y
	 * 	Y-coordinate of the vertex
	 * @param z
	 * 	Z-coordinate of the vertex
	 * @return valid vertex count < vertex count, and
	 * 	the addition of vertex was successful
	 */
	public boolean addVertex(float x, float y, float z){
		if(this.getVertexCount() < this.g.getVertexCount()){
			int vertCount = this.getVertexCount();
			this.g.changeValidVCount(vertCount + 1);
			this.getVertex(vertCount).setCoordinates(new float[]{ x, y, z });

			if(this.hasTexture()){
				float[][] texCoord = new float[][]{ { 0, 1, 1, 0 }, { 0, 0, 1, 1 } };
				int facePoint = vertCount % g.getVertexGroupMode();
				for(int i = 0; i < g.getTexCoordSetCount(); i++){
					this.getVertex(vertCount).setTexCoordinates(new float[]{ texCoord[0][facePoint], texCoord[1][facePoint] }, i);
				}
			}
			return true;
		}
		return false;
	}

	/**
	 * Selects faces by grouping selected vertices into
	 * <code>Vertex[][]</code> array and selecting additional
	 * vertices if needed.
	 * 
	 * @param allVerts
	 * 	if true only those faces are selected which all vertices are selected,
	 *        otherwise those faces are selected which have at least one selected vertex
	 */
	public void selectFaces(boolean allVerts){
		try{
			Vertex[] vs = this.g.getVertices();
			int faceSize = this.g.getVertexGroupMode();
			int faceCount = vs.length / faceSize;
			ArrayList<Vertex[]> faces = new ArrayList<Vertex[]>();

			for(int i = 0; i < faceCount; i++){
				int selectedVerts = 0;
				for(int j = 0; j < faceSize; j++){
					int index = i * faceSize + j;
					if(vs[index].getState(stateType) == Vertex.SELECTED){
						vs[index].inUse = false;
						selectedVerts++;
					}
				}

				boolean isSelected = false;
				if(!allVerts && selectedVerts > 0){
					isSelected = true;
				}
				else if(allVerts && selectedVerts == faceSize){
					isSelected = true;
				}

				if(isSelected){
					Vertex[] faceVerts = new Vertex[faceSize];
					for(int j = 0; j < faceSize; j++){
						int index = i * faceSize + j;
						vs[index].inUse = true;
						faceVerts[j] = vs[index];
					}
					faces.add(faceVerts);
				}
			}

			for(int i = 0; i < vs.length; i++){
				Vertex v = vs[i];
				if(v.inUse){
					v.setState(Vertex.SELECTED, this.stateType);
					v.inUse = false;
				}
				else if(v.getState(this.stateType) > Vertex.NORMAL){
					v.setState(Vertex.NORMAL, this.stateType);
				}
			}

			if(faces.size() < 1){
				this.selectedFaces = null;
			}
			else {
				this.selectedFaces = faces.toArray(new Vertex[faces.size()][faceSize]);
			}
		} catch(Throwable t){
			Utilities.out.throwError(t);
		}
	}

	/**
	 * Returns true if selected faces array is not null.
	 * 
	 * @return true if selected faces array is not null, otherwise false
	 */
	public boolean areFacesSelected(){
		return (this.selectedFaces != null);
	}

	/**
	 * Deselects all faces.
	 */
	public void deselectFaces(){
		this.selectedFaces = null;
	}

	/**
	 * Returns the array of selected faces.
	 * 
	 * @return the array of selected faces
	 */
	public Vertex[][] getSelectedFaces(){
		return this.selectedFaces;
	}

	/**
	 * Deletes selected faces.
	 * Faces must have been selected.
	 */
	public void deleteSelectedFaces(){
		try{
			if(this.selectedFaces != null){
				Vertex[] vs = new Vertex[this.selectedFaces.length * this.g.getVertexGroupMode()];
				int index = 0;
				for(int i = 0; i < this.selectedFaces.length; i++){
					for(int j = 0; j < this.g.getVertexGroupMode(); j++){
						vs[index] = this.selectedFaces[i][j];
						index++;
					}
				}
				this.g.deleteVertices(vs);
				this.deselectFaces();
			}
		} catch(Throwable t){
			Utilities.out.throwError(t);
		}
	}

	/**
	 * Creates and returns new <code>J3DeGeometryArray</code> object
	 * with the specified size (vertex data will be also copied to new object).
	 * This <code>GeometryManipulator</code> will also uses this new object to
	 * manipulate vertex data.
	 * 
	 * @param newSize
	 * 	new size of the polygonal mesh
	 * @return returns new <code>J3DeGeometryArray</code> object
	 * 	with the specified size
	 */
	public J3DeGeometryArray changeGeometrySize(int newSize){
		this.deselectFaces();
		int vertCount = newSize * this.g.getVertexGroupMode();

		Vertex[] vertices = this.g.getVertices();
		Vertex[] vs = null;
		if(vertCount >= vertices.length){
			vs = vertices;
		}
		else {
			vs = new Vertex[vertCount];
			for(int i = 0; i < vertCount; i++){
				vs[i] = vertices[i];
			}
		}

		this.g = this.g.createSimilarGeometryArray(vertCount, vs);
		return this.g;
	}

	/**
	 * Returns true if polygonal mesh is able to store texture coordinates.
	 * 
	 * @return true if polygonal mesh is able to store texture coordinates
	 */
	public boolean hasTexture(){
		return (this.g.getTexCoordType() > 0);
	}

	/**
	 * Sets the state of the given vertices to "hidden".
	 * 
	 * @param vertices
	 * 	vertices which state will be changed
	 */
	public void hideVertices(Vertex[] vertices){
		for(int i = 0; i < vertices.length; i++){
			vertices[i].setState(Vertex.HIDDEN, stateType);
		}
		this.deselectFaces();
	}

	/**
	 * Sets the state of the selected vertices to "hidden".
	 */
	public void hideSelectedVertices(){
		Vertex[] verts = this.getSelectedVertices();
		this.hideVertices(verts);
		this.selectedVertices = new Vertex[0];
	}

	/**
	 * Makes all hidden vertices visible.
	 */
	public void showAllVertices(){
		Vertex[] verts = this.g.getVertices();
		for(int i = 0; i < verts.length; i++){
			Vertex v = verts[i];
			if(v.getState(stateType) == Vertex.HIDDEN){
				v.setState(Vertex.NORMAL, stateType);
			}
		}
	}

	/**
	 * Inverts the order of vertices in the specified face.
	 * 
	 * @param face
	 * 	face which vertices will be inverted
	 */
	protected void invertVertices(Vertex[] face){
		int verts = face.length;
		if(verts > 1){
			Vertex v = new VirtualVertex(face[0]);
			face[0].setData(face[verts - 1]);
			face[verts - 1].setData(v);

			if(verts > 3){
				Vertex[] f = new Vertex[verts - 2];
				for(int i = 0; i < f.length; i++){
					f[i] = face[i + 1];
				}
				invertVertices(f);
			}
		}
	}

	/**
	 * Flips the face normals of all selected faces.
	 */
	public void flipFaces(){
		try{
			if(this.selectedFaces != null){
				for(int i = 0; i < this.selectedFaces.length; i++){
					this.invertVertices(this.selectedFaces[i]);
				}
				this.selectedFaces = null;
			}
		} catch(Throwable t){
			Utilities.out.throwError(t);
		}
	}

	/**
	 * Moves the specified vertices to the center of the selection.
	 * 
	 * @param vertices
	 * 	vertices which will be merged
	 */
	protected void mergeVertices(Vertex[] vertices){
		Vertex v = this.calculateCenter(vertices);

		for(int i = 0; i < vertices.length; i++){
			if(this.mTarget == GeometryManipulator.VERT_COORDS){
				vertices[i].setCoordinates(v.getCoordinates());
			}
			else if(this.mTarget == GeometryManipulator.TEX_COORDS){
				vertices[i].setTexCoordinates(v.getTexCoordinates(this.texCoordSet), this.texCoordSet);
			}
		}
	}

	/**
	 * Moves all selected vertices to the center of the selection.
	 */
	public void mergeSelectedVertices(){
		this.mergeVertices(this.getSelectedVertices());
	}

	/**
	 * Returns the color of the first selected vertex if available.
	 * 
	 * @return the color of the first selected vertex if available
	 */
	public Color getVertexColor(){
		if(this.g.hasColor()){
			Vertex[] vs = this.getSelectedVertices();
			if(vs != null && vs.length > 0){
				return vs[0].getRGB();
			}
		}
		return Color.gray;
	}

	/**
	 * Applies the specified color to the selected vertices
	 * if vertex color is enabled.
	 * 
	 * @param color
	 * 	color that will be applied to selected vertices
	 *        if vertex color is enabled
	 */
	public void setVertexColor(Color color){
		if(this.g.hasColor()){
			Vertex[] vs = this.getSelectedVertices();
			if(vs != null){
				for(int i = 0; i < vs.length; i++){
					vs[i].setRGB(color);
				}
			}
		}
	}

	/**
	 * Returns the alpha component of the vertex color of the first
	 * selected vertex if available.
	 * 
	 * @return the alpha component of the vertex color of the first
	 * 	selected vertex if available
	 */
	public float getVertexAlpha(){
		if(this.g.getColorType() == 4){
			Vertex[] vs = this.getSelectedVertices();
			if(vs != null && vs.length > 0){
				return vs[0].getAlpha();
			}
		}
		return 0;
	}

	/**
	 * Applies the specified alpha component to the vertex color of
	 * selected vertices if 4-component vertex color is enabled.
	 * 
	 * @param alpha
	 * 	alpha component which will be applied to
	 *        the selected vertices
	 */
	public void setVertexAlpha(float alpha){
		if(this.g.getColorType() == 4){
			Vertex[] vs = this.getSelectedVertices();
			if(vs != null){
				for(int i = 0; i < vs.length; i++){
					vs[i].setAlpha(alpha);
				}
			}
		}
	}
	
	/**
	 * Returns the texture coordinate value of the specified
	 * axis of the first selected vertex if texture coordinates
	 * are enabled.
	 * 
	 * @param axis
	 * 	axis of the texture coordinates.
	 * @return the texture coordinate value of the specified
	 * 	axis of the first selected vertex if texture coordinates
	 * 	are enabled.
	 */
	public float getTexCoord(int axis){
		if(this.g.getTexCoordType() >= axis){
			Vertex[] vs = this.getSelectedVertices();
			if(vs != null && vs.length > 0){
				return vs[0].getTex(axis, this.texCoordSet);
			}
		}
		return 0;
	}
	
	/**
	 * Applies the specified texture coordinate value to all selected
	 * vertices on specified axis if texture coordinates are enabled.
	 * 
	 * @param coordinate
	 * 	value which will be applied to all selected vertices
	 * @param axis
	 * 	texture coordinate axis: 0 - S, 1 - T, 2 - R, 3 - Q
	 */
	public void setTexCoord(float coordinate, int axis){
		if(this.g.getTexCoordType() >= axis){
			Vertex[] vs = this.getSelectedVertices();
			if(vs != null){
				for(int i=0; i<vs.length; i++){
					vs[i].setTex(coordinate, axis, this.texCoordSet);
				}
			}
		}
	}

}
