package adaptive.agents.tutor;

import adaptive.core.*;
import java.awt.*;
import java.awt.event.*;
import adaptive.modulemanager.*;  //for state editing method

/*****************************************************************************
 *
 * Copyright 2000, Institute for Complex Engineered Systems,
 *                 Carnegie Mellon University
 * PROJECT: Self-adaptive Software
 * DESCRIPTION: produces a square wave
 *
 * @author Jonathan R Jackson <A HREF="mailto:jackson4@andrew.cmu.edu">jackson4@andrew.cmu.edu>/A>
 *
 * REVISION HISTORY:
 *
 * $Log: PBASquareWave.java,v $
 *
 * 
 ****************************************************************************/
public class PBASquareWave extends PBAgent {
    final static protected boolean DEBUG=true;
    
    final static int OUTWAVE=0;

  //put internal structure definitions here
    protected float frequency;   
    protected float dutyCycle;  //fraction of time that the wave is high, in range [0,1]
    protected float periodHigh;
    protected float periodLow;
    protected boolean level;
/**********************************************************************
Description: Required default constructor required by Agent interface.
sets the number, type, and names for input and output ports
@param none
@return nada
@exception none
**********************************************************************/
  public void initialize(){
    setNumInputsOutputs(0,1);
    setOutputPortType(Boolean.class,OUTWAVE);
    setOutputPortName("Square Wave",OUTWAVE);
    this.setSleepTime(0); //turn off default sleep since we'll be calling sleep()
  } /* initialize() */

/**********************************************************************
<! Description: > initializes output ports' values.
@param none
@return boolean (true=success),(false=failure)
@exception none
**********************************************************************/
  protected boolean initOutputVals(){
      setOutput(Boolean.FALSE,OUTWAVE);
      return( true ); //no errors
  } /* initOutputVals() */
    

/**********************************************************************
Description: allocates data internals.
@param none
@return nada
@exception none
**********************************************************************/
  protected void allocateInternals(){
      this.level=false;
  } /* allocateInternals() */
/**********************************************************************
<! Description: > serializes my state into an object.
@param none
@return Object holding the state
@exception none
**********************************************************************/
  public Object dumpState(){
      Object state[]=new Object[2];
      state[0]=new Float(this.frequency);
      state[1]=new Float(this.dutyCycle);
      return (state);  
  } /* dumpState() */

/**********************************************************************
<! Description: > takes a dumped object and loads it into my state.
@param none
@return true == OK, false == bad.
@exception none
**********************************************************************/
  public boolean processInternalState( Object statearr ){
    boolean retval=true;
    float period;
    Object[] state;
    System.out.println(getID()+" is processing internal state...");
    if (statearr!=null){
	state=(Object[])statearr;
    }
    else{
	state=(Object[])getDefaultState();
    }
    try{
	this.frequency=((Float)state[0]).floatValue();
	this.dutyCycle=((Float)state[1]).floatValue();

	if (this.frequency <0) this.frequency=0;
	if (this.dutyCycle <0) this.dutyCycle=0;
	if (this.dutyCycle >1) this.dutyCycle=1;

	period=1000.0f/frequency;
	this.periodHigh=period*this.dutyCycle;
	this.periodLow=period*(1-this.dutyCycle);
    }catch(Exception e){  
	retval=false;  //failed to get a valid state.  Do not start this agent.
    }
    return (retval);
  } /* processInternalState() */
    /*******************
<!Description:> returns an object that can be used as the default state
@param none
@return Object containing state
@exception none
    *********************************/
  public Object getDefaultState(){
    Object[] state=new Object[2];
    state[0]=new Float(1.0f);
    state[1]=new Float(0.5f);
    return state;
  }	 

    public Object getStateDescription(){
	String[] descript=new String[2];
	descript[0]="Frequency";
	descript[1]="Duty Cycle [0,1]";
	return descript;
    }
   
/**********************************************************************
Description: Required main loop of the agent.
     Called with each cycle of operation for the agent
@param none
@return nada
@exception none
**********************************************************************/
  public void runLoop(){
      float sleeptime=this.periodHigh;
      if (level) { //wave is high, switch to low
	  level=false;
	  sleeptime=this.periodLow;
      }
      else{//wave is low, switch to high
	  level=true;
      }
      if (DEBUG) System.out.println("Wave is "+level);
      setOutput(new Boolean(level),OUTWAVE);
      try{
	  sleep((int)sleeptime);
      }catch(InterruptedException ie){}
  } /* runLoop() */
  /***************************************************************************
   *
   * Returns the GUI dialog internal state editor. This should return
   * a new instance of the StateEditor. Or null if there is no state
   * editor.
   *
   * @param 
   *
   * @return the gui state editor
   *
   * @exception 
   *
   **************************************************************************/
  public StateEditor getStateEditor(java.awt.Frame parent) {

      return new BaseStateEditor(parent){
	  float freq;
	  float duty;
	  TextField printFreq;
	  TextField printDuty;
	  public Panel setupPanel(){
	      Object[] statearr=(Object[])state;
	      freq=((Float)statearr[0]).floatValue();
	      duty=((Float)statearr[1]).floatValue();
	      Panel panel=new Panel();
	      GridBagLayout layout=new GridBagLayout();
	      GridBagConstraints gbc=new GridBagConstraints();
	      panel.setLayout(layout);
	      Label f=new Label("Frequency");
	      Label d=new Label("Duty Cycle");
	      Scrollbar freqBar=new Scrollbar(Scrollbar.HORIZONTAL, (int)(freq*10) , 1, 0, 150);//freq *10, need to be ints
	      Scrollbar dutyBar=new Scrollbar(Scrollbar.HORIZONTAL, (int)(duty*100), 1, 0, 100);//duty cycle in percent
	      printFreq=new TextField(4);
	      printFreq.setText(""+freq);
	      printFreq.setEditable(false);
	      printDuty=new TextField(4);
	      printDuty.setText(""+duty);
	      printDuty.setEditable(false);

	      gbc.fill=GridBagConstraints.NONE;
	      gbc.anchor=GridBagConstraints.CENTER;
	      setConstraints(gbc,0,0,1,1,10,10);
	      panel.add(f,gbc);
	      setConstraints(gbc,0,1,1,1,10,10);
	      panel.add(d,gbc);
	      setConstraints(gbc,3,0,1,1,10,10);
	      panel.add(printFreq,gbc);
	      setConstraints(gbc,3,1,1,1,10,10);
	      panel.add(printDuty,gbc);
	      gbc.fill=GridBagConstraints.HORIZONTAL;
	      setConstraints(gbc,1,0,2,1,100,0);
	      panel.add(freqBar,gbc);
	      setConstraints(gbc,1,1,2,1,100,0);
	      panel.add(dutyBar,gbc);

	      freqBar.addAdjustmentListener(new AdjustmentListener(){
		  public void adjustmentValueChanged(AdjustmentEvent e){
		      freq=(float)e.getValue()/10.0f;
		      printFreq.setText(""+freq);
		  }
	      });
	      dutyBar.addAdjustmentListener(new AdjustmentListener(){
		  public void adjustmentValueChanged(AdjustmentEvent e){
		      duty=(float)e.getValue()/100.0f;
		      printDuty.setText(""+duty);
		  }
	      });
	      return panel;
	  }
	  public Object commitState(){
	      Object[] myState;
	      myState=new Object[2];
	      myState[0]=new Float(freq);
	      myState[1]=new Float(duty);
	      return myState;
	  }

      };
  }
}
