package inp.camac;
/**
 * @author      Maxim V. Kollegov
 * @version     1.00, 02/04/2001
 */

import java.util.*;
import java.io.*;

/**
 * Basic CAMAC module class to be extended by actual modules 
 * implementations, this class represent basics common
 * methods required for CAMAC module
 */

public class Module {
  private Vector listeners = new Vector();
  private int N;             // 1-24
  private Crate crate;
  private String name="Abstract Module";
  
  public Module(){ }
  
  
  /**
   * Method to add listener interested in LAMs from this module
   * Besides adding listener logicaly it enables LAM from this 
   * position executing enableLAM on this module. Do it once when 
   * first listener added.
   * @param listener   LAMlistener interested in LAMs from this module
   */

  public void addLAMlistener(LAMlistener listener)  throws IOException{
  	 listeners.addElement(listener);
     if (listeners.size()==1) {
        enableLAM(true);
     }
  }
  
  /**
   * Method to remove listener interested in LAMs from this module
   * When last listener removed implementation disables LAM from this position.
   * @param listener   one to remove
   */  
  public void removeLAMlistener(LAMlistener listener)  throws IOException{
  	 listeners.removeElement(listener);
     if(listeners.size()==0) {
         enableLAM(false);     
     }
  }
  
  /**
   * Method called by CamacDriver when LAM from station at N
   * occures. Deafualt implenetation dispatch LAMevent to all 
   * LAMListeners added to this module.
   */
  protected void fireLAM(){
  	 LAMevent event= new LAMevent(N,this);
  	 synchronized(listeners) {
    	 for(int i=0;i<listeners.size();i++){
       	     ((LAMlistener)listeners.elementAt(i)).onLAM(event);
    	 }
  	 }
  }
  
 /**
  * set position number of this module in Crate.
  */
  protected void setN(int N){	 this.N = N;  }

  /**
   * get position number of this module in Crate.
   */
  public int getN(){ return this.N;  }

  /**
   * setCrate reference for this module
   */
  protected void setCrate(Crate c) { this.crate = c;  }

  /**
   * setCrate reference for this module
   */
  public Crate getCrate(){	 return this.crate;  }

  /** 
   * execute A,F on this module with data as many times as 
   * length of data[] is. if data[] is null, NAF without data 
   * transfer will be executed once.
   * Default implementation delegate this method to crate
   *
   * @param A     CAMAC subaddress
   * @param F     CAMAC function to execute
   * @param data  data array to transfer. NAF will be executed as many times 
   *              as data.length is. In case data is null NAF without data 
   *              transfer will be executed once. For write operations data 
   *              will be transfered from array to module. For read operations 
   *              data read from CAMAC will be placed in this array. For CAMAC 
   *              functions without data transfer, no transfer will be done.
   */
  public void AF(int A,int F, int data[]) throws IOException{
  	 if(N>0 && crate!=null){
      //delegate execution to crate
        crate.executeNAF(N,A,F,data,((F&8)==0) ? dataBits():0);
  	 }
  }

  /** 
   * Execute A,F on this module without data transfer.
   * Default implementation delegate this method to crate
   * 
   * @param A CAMAC subaddress
   * @param F CAMAC function to execute
   */  
  public void AF(int A,int F) throws IOException{
  	 if(N>0 && crate!=null){
       //delegate execution to crate
         crate.executeNAF(N,A,F,null,0);
  	 }
  }

  
  /**
   * public method to check is LAM by Q with F8 supported.
   * Default implementation always return false. Actual 
   * modules implementations must override this methods if
   * this command is supported. 
   */
  
  public boolean isF8supported(){
     return false;
  }

  /**
   * public method to check is LAM enable/disable with F26/F24 commands
   * supported by this module.Default implementation always return false;
   * Modules implementations must override this methods if
   * this command is supported. 
   */

  public boolean isF24F26supported(){
     return false;
  }

  /**
   * Public method query Module for number of significant bits.
   * This number can be used by CamacDriver implementations in order to 
   * optimize transfers. Default implementation suppose all 24 bits are 
   * required.
   */

  public int dataBits(){
     return 24;
  }


  /**
   * disable/enable LAM from module.
   * Default implementation execute F24/F26 if supported 
   * and change LAM mask in Crate 
   */     
  private void enableLAM(boolean enable) throws IOException{
     crate.enableLAM(N,enable);
     //module may not support f24/f26
     if(isF24F26supported()) AF( 0, enable ? 26:24 );

  }
  
   /**
    * Get name for this module
    */
  public String getModuleName(){
     return name;
  } 
  
   /**
    * Set module name
    * @return Module name
    */ 
  public void setModuleName(String name){
     this.name=name;
  } 

    /**
     * By default module must return Q for any supported F
     * if module is ready to execute it. But some hardware
     * can ignore this requirement, so if particular module do not 
     * return Q, returning false by this method for some F will 
     * suppress throwing exceptions when there is no Q for this function.
     */  
  public boolean mustHaveQ(int F){
     return true;
  }
  

  /**
   * By default module must return X for any supported F. But some hardware
   * can ignore this requirement, so if particular module do not 
   * return X , returning false by this method for some F will 
   * suppress throwing exceptions when there is no Q for this function.
   */  
  public boolean mustHaveX(int F){
     return true;
  }
    
}