package CookieCutter.g5;

import java.util.*;
import java.awt.*;
import java.awt.geom.*;

public class ConcaveCorner extends Corner {
	//public ConcaveCorner( int id, Point2D.Double prev, Point2D.Double mid, Point2D.Double next ) {
	public ConcaveCorner( int id, int owner, Corner prev, Point2D.Double mid, Corner next ) {
		super( id, owner, prev, mid, next );
	}

	public double innerAngle() throws Exception {
		return( 2 * Math.PI - super._angle() );
	}

	public double outerAngle() throws Exception {
		return( super._angle() );
	}

	public Corner complement( Vector corners ) throws Exception {
		double minDiff = java.lang.Double.MAX_VALUE;
		Corner complCorner = null;

		for( int i = 0; i < corners.size(); i++ ) {
			Corner corner = ( Corner )corners.elementAt( i );

			if( corner.getClass().toString().endsWith( "ConvexCorner" ) && !corner.isUsed() ) {
				double diff = super.idealMeasure( corner );

				if( Math.abs( diff ) < Math.abs( minDiff ) ) {
					minDiff = diff;
					complCorner = corner;
				}
				else if( Math.abs( diff ) == Math.abs( minDiff ) ) {

				}
			}
		}

		return( complCorner );
	}

	public double wastedArea( Corner corner ) throws Exception {
		if( corner.getClass().toString().endsWith( "ConvexCorner" ) ) {
			// get angle and arms for concave corner
			double concavePrevArm[] = new double[2];
			concavePrevArm[0] = prev().getX() - getX();
			concavePrevArm[1] = prev().getY() - getY();

			double concaveNextArm[] = new double[2];
			concaveNextArm[0] = next().getX() - getX();
			concaveNextArm[1] = next().getY() - getY();
			
			double concaveAngle = outerAngle();
			double concavePrevArmLen = VectorUtils.norm( concavePrevArm );
			double concaveNextArmLen = VectorUtils.norm( concaveNextArm );

			// get angle arms for convex corner
			double convexPrevArm[] = new double[2];
			convexPrevArm[0] = corner.prev().getX() - corner.getX();
			convexPrevArm[1] = corner.prev().getY() - corner.getY();

			double convexNextArm[] = new double[2];
			convexNextArm[0] = corner.next().getX() - corner.getX();
			convexNextArm[1] = corner.next().getY() - corner.getY();
			
			double convexAngle = corner.innerAngle();
			double convexPrevArmLen = VectorUtils.norm( convexPrevArm );
			double convexNextArmLen = VectorUtils.norm( convexNextArm );

			if( concaveAngle < convexAngle ) {
				double prevDepth = 0.0, nextDepth = 0.0;

				// figure out which convex arm will collide first
				if( correspondingVertices( corner ) ) {
					prevDepth = _penetrationDepth( concaveAngle, concavePrevArmLen, 
						convexAngle, convexPrevArmLen 
					);
					nextDepth = _penetrationDepth( concaveAngle, concaveNextArmLen, 
						convexAngle, convexNextArmLen 
					);
				}
				else {
					prevDepth = _penetrationDepth( concaveAngle, concavePrevArmLen, 
						convexAngle, convexNextArmLen 
					);
					nextDepth = _penetrationDepth( concaveAngle, concaveNextArmLen, 
						convexAngle, convexPrevArmLen 
					);
				}

				// pick the side that doesn't penetrate as deeply (since it collides)
				double minPenDepth = prevDepth < nextDepth ? prevDepth : nextDepth;
				double concaveArmLen = minPenDepth == prevDepth ? concavePrevArmLen : concaveNextArmLen;
				double vertical = concaveArmLen * Math.cos( concaveAngle / 2 );
				double baseline = ( vertical - minPenDepth ) * Math.tan( concaveAngle / 2 );
				double convexVert = baseline / Math.tan( convexAngle / 2 );

				return( baseline * ( vertical - convexVert ) );
			}

			// various triangle components computation
			double concavePrevBase = concavePrevArmLen * Math.sin( concaveAngle / 2 );
			double concavePrevVert = concavePrevArmLen * Math.cos( concaveAngle / 2 );
			double concaveNextBase = concaveNextArmLen * Math.sin( concaveAngle / 2 );
			double concaveNextVert = concaveNextArmLen * Math.cos( concaveAngle / 2 );

			double convexPrevBase = convexPrevArmLen * Math.sin( convexAngle / 2 );
			double convexPrevVert = convexPrevArmLen * Math.cos( convexAngle / 2 );
			double convexNextBase = convexNextArmLen * Math.sin( convexAngle / 2 );
			double convexNextVert = convexNextArmLen * Math.cos( convexAngle / 2 );

			double concaveVert = concavePrevArmLen > concaveNextArmLen ? concavePrevVert : concaveNextVert;
			double concaveBase = concavePrevArmLen > concaveNextArmLen ? concavePrevBase : concaveNextBase;
			/*
			double convexVert = convexPrevVert < convexNextVert ? convexPrevVert : convexNextVert;
			double convexBase = convexVert == convexPrevVert ? convexPrevBase : convexNextBase;

			//if( convexVert > concaveVert ) {
				//convexVert = concaveVert;
				//convexBase = concaveBase;
			//}

			return( concaveBase * ( concaveVert - convexVert ) );
			*/
			double convexVert = convexPrevArmLen > convexNextArmLen ? convexPrevVert : convexNextVert;
			double convexBase = convexPrevArmLen > convexNextArmLen ? convexPrevBase : convexNextBase;

			if( convexVert > concaveVert ) {
				convexVert = concaveVert;
				convexBase = convexVert * Math.tan( convexAngle / 2 );
			}

			return( concaveVert * concaveBase - convexVert * convexBase );
		}

		// not a convex corner so return an "infinite" value
		return( java.lang.Double.MAX_VALUE );
	}

	private double _penetrationDepth( double concaveAngle, double concaveArmLen, 
		double convexAngle, double convexArmLen ) {
		double concaveBase = concaveArmLen * Math.sin( concaveAngle / 2 );
		double convexBase = convexArmLen * Math.sin( convexAngle / 2 );

		if( convexBase > concaveBase ) {
			return( 0.0 );
		}

		double concaveVert = concaveArmLen * Math.cos( concaveAngle / 2 );
		double convexVert = convexBase / Math.tan( concaveAngle / 2 );
		return( concaveVert - convexVert );
	}
}
