package rec.simulate.widgets;

import rec.util.*;
import rec.geo.*;
import rec.sys.*;
import rec.core.widgets.*;
import rec.simulate.widgets.wm.*;
import java.io.*;
import java.awt.*;
import java.lang.*;
import java.util.*;
import java.util.List;

public abstract class WSimGearTrain extends WSimConstraint
{
    private int m_numGears;
    protected WSimGear[] m_gears;

    // an array of arrays indicating which gears mesh
    // i.e. if gear 4 meshed with gears 2 and 3 then
    // gearMeshes[4] = { 2, 3 }
    protected int[][] m_gearMeshes;

    public WSimGearTrain(WSimGear[] gears, WSimGearTrain[] trains, 
			 WSimGear newGear, boolean singleCircles){
	rebuild(gears, trains, newGear, singleCircles);
    }


    public void elect(Surface s)
    {
	super.elect(s);
	for (int i = 0; i < m_numGears; i++){
	    m_gears[i].setTrain(this);
	}
    }

    public WSimGearTrain rebuild(WSimGear[] gears, WSimGearTrain[] trains, 
				 WSimGear newGear, boolean singleCircles){

	m_Name = "GearTrain" + getId();
	setDefaultColor("gear", "gear");
	setEnglishName("Gear Train");

	int totalGears = gears.length + 1;

	int i;

	for (i = 0; i < trains.length; i++){
	    totalGears += trains[i].numGears();
	}

	m_gears = new WSimGear[totalGears];
	m_gearMeshes = new int[totalGears][];
	m_numGears = totalGears;

	int offset = 0;
	int newGearMeshes[] = new int[(totalGears - 1)];
	int numNewGearMeshes = 0;

	for (i = 0; i < gears.length; i++){

	    m_gears[i] = gears[i];
	    m_gearMeshes[i] = new int[1];

	    // This line says that newgear meshes with this gear.
	    // This is true for all gears in the gear array.
	    m_gearMeshes[i][0] = totalGears - 1;

	    numNewGearMeshes++;
	}

	offset += i;


	// i is which train we have
	// j is which gear we have within train i
	// offset is the number of gears inserted before train i

      	for(i = 0; i < trains.length; i++){
	    
	    WSimGear trainGears[] = trains[i].getGears();
	    int gearMeshes[][] = trains[i].getMeshes();
	    
	    int j;

	    for (j = 0; j < trains[i].numGears(); j++){
		
		m_gears[j+offset] = trainGears[j];

		if(gearsTangent(trainGears[j], newGear)){
		    m_gearMeshes[j+offset] = new int[gearMeshes[j].length + 1];
		}
		else{
		    m_gearMeshes[j+offset] = new int[gearMeshes[j].length];
		}

		int k;

		for (k = 0; k < gearMeshes[j].length; k++){
		    m_gearMeshes[j+offset][k] = gearMeshes[j][k] + offset;
		}

		if (gearsTangent(trainGears[j], newGear)){

		    m_gearMeshes[j+offset][k] = totalGears - 1;

		    newGearMeshes[numNewGearMeshes] = j + offset;

		    numNewGearMeshes++;

		}

	    }

	    offset += j;
	    
	}
	
	m_gears[offset] = newGear;
	m_gearMeshes[offset] = new int[numNewGearMeshes];

	for (i = 0; i < numNewGearMeshes; i++){
	    m_gearMeshes[offset][i] = newGearMeshes[i];
	}

	return this;
    }

    public boolean isMyGear(WCircle circle){
	for(int i = 0; i < m_numGears; i++){
	    if(m_gears[i].isMyCircle(circle)){
		return true;
	    }
	}
	return false;
    }
    
    private boolean gearsTangent(WSimGear gear1, WSimGear gear2){

	GPoint center1 = gear1.getCenter();
	GPoint center2 = gear2.getCenter();
	
	double distance = Geometry.distance(center1,center2);
	
	double radius1 = gear1.getRadius();
	double radius2 = gear2.getRadius();
	
	return (Math.abs(distance - (radius1 + radius2)) < 10);

    }	

    private int gearNumber(WSimGear gear){
	return 0;
    }

    boolean m_rerecognizing = false;
    boolean m_rerecognized = false;
    WSimGear gearRemoved;
    String gearsWeGot = "";
    //    Collection[] newRecognitions = new Collection[m_numGears];
    Widget[] baseWidgets;


    // Should probably do something smart here
    // For now just rerecognize everything.
    public void killGear(WSimGear gear){

	gearsWeGot += gear.toString();

	if(!m_rerecognizing && !m_rerecognized){

	    gearRemoved = gear;

	    m_rerecognizing = true;

	    Collection otherStuff = new HashSet();
	    otherStuff.add(this);
	    
	    baseWidgets = new Widget[m_numGears];

	    for (int i = 0; i < m_numGears; i++){

		if (!m_gears[i].equals(gear)){
		    otherStuff.add(m_gears[i]);
		    otherStuff.addAll(m_gears[i].getAttached());

// 		    Collection test = m_gears[i].getRootAncestors();
// 		    Object o = test.iterator().next();
// 		    baseWidgets[i] = (Widget) test.iterator().next();
// 		    if(!(baseWidgets[i] instanceof Squiggle)){
// 			baseWidgets[i] = null;
// 		    }

		    if(((Widget) m_gears[i]).isRoot()){
			baseWidgets[i] = null;
		    }
		    else{
			baseWidgets[i] = (Widget) m_gears[i].getParents().toArray()[0];
		    }
		}
		else {
		}
	    }
	    
	    Collection newRecognitions = getSurface().reRecognize(otherStuff);
	    
	    Iterator it = newRecognitions.iterator();

	    m_rerecognized = true;

	}
	else if(m_rerecognized){

	    // This is the case where we're deleting more than one gear
	    // from a train.  The gear we're removing now has already
	    // been rerecognized when the first gear was killed.  
	    // We want to roll back that rerecognition
	    // because it's no longer relavent, but this gear has already
	    // had it's attachments removed by the rerecognition so it doesn't
	    // have a handle to those interpretations.  Thus we store then
	    // above and delete them here.

	    if(!gear.equals(gearRemoved)){

		for (int i = 0; i < m_numGears; i++){
		    if (m_gears[i].equals(gear)){
			if(!(baseWidgets[i] == null)){
			    getSurface().killWidgets(baseWidgets[i].getAttached());
			}
		    }
		}
	    }

	}
	else{
	    return;
	}

    }

    public boolean attach(WSimBody body){
	//TODO:
	//if the body is a gear in this train return true.
	return false;
    }

    public void detach(){
	//TODO:
	// what is the right thing to do here?
	// if it has no gears it doesn't exist.
    }

    public List getBodies(){
	List list = new ArrayList(numAttached());
	for (int i = 0; i < m_numGears ; i++){
	    list.add(m_gears[i]);
	}
	return list;
    }

    public GPoint getAttachmentPoint(WSimBody body){
	return body.getLocation().getCenter();
    }

    public void die(){
	super.die();
	for(int i = 0; i < m_numGears; i++){
	    m_gears[i].trainDies(this);
	}
    }
    

    public int[][] getMeshes(){
	return m_gearMeshes;
    }

    public int numGears(){
	return m_numGears;
    }

    public int numAttached(){
	return numGears();
    }

    public WSimGear[] getGears(){
	return m_gears;
    }

    public void draw( Graphics g )
    {
// 	for(int i = 0; i < m_numGears; i++){
// 	    m_gears[i].display( g );
// 	}
    }

    public boolean includes( GPoint gp, double offset )
    {
// 	for(int i = 0; i < m_numGears; i++){
// 	    if ( m_gears[i].includes( gp, offset ) ){
// 		return true;
// 	    }
// 	}
	return false;
    }

    public boolean contains( GPoint gp, double offset )
    {
// 	for(int i = 0; i < m_numGears; i++){
// 	    if ( m_gears[i].contains( gp, offset ) ){
// 		return true;
// 	    }
// 	}
	return false;
    }

    public boolean contains( GPoint gp )
    {
// 	for(int i = 0; i < m_numGears; i++){
// 	    if ( m_gears[i].contains( gp ) ){
// 		return true;
// 	    }
// 	}
	return false;
    }


    public Rectangle getBounds()
    {
	Rectangle bounds = m_gears[0].getBounds();

	for(int i = 1; i < m_numGears; i++){
	    bounds = bounds.union(m_gears[i].getBounds());
	}

	return bounds;
    }

    public Location getLocation()
    {
	return( new Location(getBounds()));
    }

    public void move( GPoint gp )
    {

	//TODO
    }
}
