// (c) MIT 2003.  All rights reserved.

// -*-Mode: Java-*- 
// $Id: WSimPulley.java,v 1.11 2003/03/06 00:40:40 moltmans Exp $
// WSimPulley.java -- 
// Author: Michael Oltmans <moltmans@ai.mit.edu> 
// Maintainer: Michael Oltmans <moltmans@ai.mit.edu> 
// Copyright: Copyright (C) 1999 by Michael Oltmans 
// Created: <Fri Oct 15 18:48:59 1999> 
// Time-stamp: <2001-09-10 18:30:37 moltmans> 
// Keywords: 
package rec.simulate.widgets;

import java.util.*;
import java.awt.Color;
import rec.util.*;
import rec.sys.*;
import rec.core.widgets.*;
import rec.geo.*;

public abstract class WSimPulley extends WSimConstraint
{
    protected WSimBody[] m_PulleyBodies;
    protected WSimBody m_EndBody1;
    protected WSimBody m_EndBody2;

    protected int m_numWheels;
    protected int m_numRopes;
    protected int m_ropesLength = 0;
  
    protected List m_ropes;

    public WSimPulley( List wheels, List ropes )
    {
	setDefaultColor( "pulley", "sim_constraint" );
	setEnglishName( "Pulley" );
	
	m_numWheels = wheels.size();
	m_numRopes = ropes.size();
	
	m_PulleyBodies = new WSimBody[m_numWheels];

	Assert.check( m_numWheels == m_numRopes -1,
		      "Wrong number of ropes and wheels: ropes: " +
		      ropes + " wheels: " + wheels );
	
	
	// fill up the components
	m_Components = new GShape[3*wheels.size() + 1];
	int count = 0;
	
	double size = 0.0;
	
	Iterator it = wheels.iterator();
	while( it.hasNext() ) {
	    WCircle wheel = (WCircle)it.next();
	    size += wheel.size();
	    m_Components[count++] = new GCircle( wheel.getCircle() );
	    m_Components[count++] = new GCircle( wheel.getCenter(),
						 wheel.size() / 5.0 );
	}
	m_ropes = ropes;
	it = ropes.iterator();
	while( it.hasNext() ) {
	    WSimRope rope = (WSimRope)it.next();
	    m_Components[count++] = rope.getGeo().copy();
	    m_ropesLength += rope.getLength();
	}
	
	m_Name = "Pulley" + getId();
	m_Size = size / m_numWheels;
    }
    
    public GPoint getFirstAnchorPoint()
    {
	return getWheelN(0).getCenter();
    }

    public GPoint getFirstEndPoint()
    {
	return getWheelN(0).getCenter();
    }
    public GPoint getLastEndPoint()
    {
	return getWheelN( m_numWheels - 1 ).getCenter();
    }

    public int getNumRopes(){
	return m_numRopes;
    }
    
    public int getNumWheels(){
	return m_numWheels;
    }

    /** zero indexed **/
    public GCircle getWheelN( int n )
    {
	return (GCircle)m_Components[ 2*n ];
    }
    
    /** zero indexed **/
    public WSimRope getRopeN( int n )
    {
	return (WSimRope)m_ropes.get(n);
    }
    
    public List getRopes()
    {
	List l = new LinkedList();
	for( int i=0; i<m_numRopes; i++ ) {
	    l.add( getRopeN(i) );
	}
	return l;
    }
    
    public List getWheels()
    {
	List l = new LinkedList();
	for( int i=0; i<m_numWheels; i++ ) {
	    l.add( getWheelN(i) );
	}
	return l;
    }
    
    
    /**
     * Find the free ends of the rope (so we can check for attachments)
     **/
    public GPoint getFreeEnd1() 
    {
	WSimRope rope = (WSimRope)getRopeN( 0 );
	GCircle wheel = (GCircle)getWheelN( 0 );
	if ( wheel.includes( rope.getHeadPoint() , 20 ) ) {
	    return rope.getTailPoint();
	}
	else {
	    return rope.getHeadPoint();
	}
    }
    
    /** Find the free ends of the rope (so we can check for attachments) **/
    public GPoint getFreeEnd2() 
    {
	WSimRope rope = (WSimRope)getRopeN( m_numRopes - 1 );
	GCircle wheel = (GCircle)getWheelN( m_numWheels - 1 );
	if ( wheel.includes( rope.getHeadPoint() , 20 ) ) {
	    return rope.getTailPoint();
	}
	else {
	    return rope.getHeadPoint();
	}
    }
    
    public void move(GPoint p){

	GPoint center = getLocation().getCenter();
	
	for( int i = 0; i < m_Components.length; ++i ) {
	    if (m_Components[i] instanceof GComplex){

		GPoint complexCenter = ((GComplex)m_Components[i])
		    .getLocation().getCenter();

		double xdiff = center.getX() - complexCenter.getX();
		double ydiff = center.getY() - complexCenter.getY();

		((GComplex)m_Components[i]).translate(p.getX() - xdiff, 
						      p.getY() - ydiff);
     	    }
	    else{
		m_Components[i].translate( center, p );
	    }
	}
	
    }

    public boolean attach( WSimBody body )
    {
	if(m_EndBody1 == null &&
	   (body.includes( getFreeEnd1(), 10)||
	    body.contains( getFreeEnd1(), 0))){
	    
	    m_EndBody1  = body;
	    return true;
	}

	else if(m_EndBody2 == null &&
		(body.includes( getFreeEnd2(), 10) ||
		 body.contains( getFreeEnd2(), 0))){
	    m_EndBody2  = body;
	    return true;
	}

	else{

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

		if ((body.includes(((GCircle)m_Components[2*i]).getCenter(), 
				   10) ||
		     body.contains(((GCircle)m_Components[2*i]).getCenter(),0))
		    && m_PulleyBodies[i] == null){
		    m_PulleyBodies[i] = body;
		    return true;
		}
	    }
	}

	return false;
    }
    
    public void detach() 
    {
	m_EndBody1 = null;
	m_EndBody2 = null;

	for (int i = 0; i < m_numWheels; i++){
	    m_PulleyBodies[i] = null;
	}
    }
    
    public int numAttached()
    {
	int n = 0;
	if ( m_EndBody1 != null )
	    n = n + 1;
	if ( m_EndBody2 != null )
	    n = n + 1;

	for (int i = 0; i < m_numWheels; i++){
	    if(m_PulleyBodies[i] != null){
		n++;
	    }
	}
	return n;
    }
    
    public List getBodies() 
    {
	List list = new ArrayList( numAttached() );
	if( m_EndBody1 != null )
	    list.add( m_EndBody1 );
	if( m_EndBody2 != null )
	    list.add( m_EndBody2 );

	for (int i = 0; i < m_numWheels; i++){
	    if(m_PulleyBodies[i] != null){
		list.add(m_PulleyBodies[i]);
	    }
	}
	
	return list;
    }
    
    public GPoint getAttachmentPoint( WSimBody body ) 
    {
	if( body.equals( m_EndBody1 ) )
	    return getFreeEnd1();
	else if( body.equals( m_EndBody2 ) )
	    return getFreeEnd2();
	else{
	    for (int i = 0; i < m_numWheels; i++){
		if(m_PulleyBodies[i].equals(body)){
		    return ((GCircle)m_Components[2*i]).getCenter();
		}
	    }
	}
	return null;
    }
}
