package inp.camac.cc232;

import java.io.*;
import java.util.*;
import javax.comm.*;
import inp.camac.*;

public class DriverCC232 extends CamacDriver {
  final int MAXCRATES = 4;
  SerialConnectionCC232[] connections = new SerialConnectionCC232[MAXCRATES];
  boolean currentI[] = new boolean[MAXCRATES];
  
  String[] errors = {
    "No Q ",
    "No X",
    "Synchronization error",
    "Timeout error",
    "Overrun error",
    "Parity error",
    "Break detected",
    "Frame error"
  };


  public void init()  throws IOException {
     
  } 

  public void C(int crateN)         throws IOException{
     int[] temp = new int[1];
     temp[0]=4|(currentI[crateN] ? 1:0);
     executeNAF(crateN,0,2,16,temp,24,null);  
  }
  
  public void I(int crateN, boolean I)  throws IOException {
     int[] temp = new int[1];
     temp[0]=I ? 1:0;
     currentI[crateN] = I;
     executeNAF(crateN,0,2,16,temp,24,null);  
  }
  
  public void Z(int crateN)         throws IOException {
      int[] temp = new int[1];
      temp[0]=2|(currentI[crateN] ? 1:0);
      executeNAF(crateN,0,2,16,temp,24,null);    
  }

  public void setLAMMask(int crateN, int mask) throws IOException {
     int[] temp = new int[1];
     temp[0]=mask;
     executeNAF(crateN,0,0,16,temp,24,null);
  }
  
  public int readLAMs(int crateN) throws IOException{
      int[] temp = new int[1];
      executeNAF(crateN,0,1,0,temp,24,null);
      return temp[0];
  }
  
  public void close(int crateN){}

  public synchronized void executeNAF(int crateN, 
                  int N, 
                  int A, 
  				  int F, 
				  int[] data,
                  int bits,
                  Module module
                  
                  ) throws IOException {

      int[] tmp = new int[1];
      if(data!=null) {
        for(int i=0;i< data.length;i++) {
           tmp[0] = data[i];
           checkErr(connections[crateN].sendNAF( N,A,F,bits,tmp),module,F);
           data[i]=tmp[0];
        }
      }
      else {
           checkErr(connections[crateN].sendNAF( N,A,F,0,tmp),module,F);
      }                  
  }

  private void checkErr(int n, Module m, int F) throws IOException{
    String s="";
    for(int i=7;i>=0;i--) {
        if(((n>>i)&1) != 0 ) {
          if(i<2 && m!=null && ((m.mustHaveQ(F) && i==0) || (m.mustHaveX(F) && i==1 )))   
              s+=errors[i]+"\n";
        }
    }
    if(s.length()>0) throw new IOException(s);
  }

  
  public void initCrate(int crateN) throws IOException {
     if(crateN>=MAXCRATES || crateN<0 ) throw new IOException("Invalid crate number");         
     if(connections[crateN]!=null)  throw new IOException("Crate already opened");
     connections[crateN] = new SerialConnectionCC232(crateN, this);   
     setLAMMask(crateN,0xffffff);
     readLAMs(crateN);
  }
  
  

  public void close() {
     for(int i=0;i<MAXCRATES;i++) {
        if(connections[i]!=null) {
           connections[i].close();
           connections[i]=null;
        }
     }
  }
}



class SerialConnectionCC232   implements SerialPortEventListener, Runnable {
    CommPortIdentifier id ;
    SerialPort port;
    OutputStream out;
    InputStream   in;
    CamacDriver driver;
    int crateN;
    Thread th;

    final static int LAST  = 0x80;
    final static int NEXT  = 0x00;
    final static int FIRST = 0xC0;
    final static int LAM   = 0x40;
    final static int REQUEST=0x40;    
    
    final static int NOQ     = 1;
    final static int NOX     = 2;    
    final static int PROTOCOL= 4;
    final static int TIMEOUT = 8;
    final static int OVERRUN = 0x10;    
    final static int PARITY  = 0x20;
    final static int BREAK   = 0x40;
    final static int FRAME   = 0x80;

    SerialConnectionCC232(int crateN, CamacDriver driver) throws IOException {    
    
    /*
        Enumeration portList = CommPortIdentifier.getPortIdentifiers();
        int n=0;
        while (portList.hasMoreElements()) {
            CommPortIdentifier portId = (CommPortIdentifier) portList.nextElement();
            System.out.println(n+": "+portId.getName());
            if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
            }
            n++;
        }
        System.out.println("Nports = "+n);
     */    
    
        this.crateN=crateN;
        this.driver=driver;
        String name = "COM"+(crateN+1);         
        try {
            id   = CommPortIdentifier.getPortIdentifier(name);
            port = (SerialPort)id.open(getClass().getName(), 2000);
            port.setSerialPortParams(57600,
                SerialPort.DATABITS_8,
                SerialPort.STOPBITS_2,
                SerialPort.PARITY_EVEN);
            port.addEventListener(this);     
            out = port.getOutputStream();     
            in  = port.getInputStream();              
            port.notifyOnDataAvailable(true);
            port.notifyOnBreakInterrupt(true);
            port.notifyOnFramingError(true);
            port.notifyOnParityError(true);            
            port.notifyOnOverrunError(true);
        }catch(Exception e) {
           e.printStackTrace();
           throw new IOException(e.getMessage());  
        }
    }
    
    private int _rdata;
    private int _rexq;    

    public synchronized int sendNAF(int N, int A, int F, int nbits, int[] data )
                                   throws IOException {
        F=F & 0x3F;
        A=A & 0x3F;
        N=N & 0x3F;
                                           
        out.write(N | FIRST);
        out.write(A);
        _rexq=TIMEOUT;

        if((F & 8)==0) {
          if((F & 16)==0) {
            //read
             out.write(F|LAST);    
          }
          else {
            //write
            out.write(F | ((nbits==0)?LAST:0));
            while( nbits>0 ) {
               int b = data[0] & 0x3f;
               data[0]=data[0]>>6;
               nbits-=6;               
               out.write(b | ((nbits<=0)? LAST:0));
            }
          }
        }
        else {
            //no data
            out.write(F|LAST);
           
        }      
       try { wait(1000); }
       catch(InterruptedException e){
         return TIMEOUT;
       }         
       data[0]=_rdata;
       return _rexq;
    }
    
    public void serialEvent(SerialPortEvent event) {
      switch(event.getEventType()) {        
        case SerialPortEvent.BI:  _exq |= BREAK;   break;
        case SerialPortEvent.OE:  _exq |= OVERRUN; break;
        case SerialPortEvent.FE:  _exq |= FRAME;   break;
        case SerialPortEvent.PE:  _exq |= PARITY;  break;
            
        case SerialPortEvent.DATA_AVAILABLE:
            byte[] buf = new byte[20];  
            int nread = 0;
            try {
                 while (in.available() > 0) {
                    nread=in.read(buf,0,20);
                    for(int i=0;i<nread;i++) {
                       byteReceived(buf[i]);
                    }
                 }
            } catch (IOException e) {}
            break;
        default: break;            
       }
       
       if((_exq & 0xf8) != 0 ) {
          dataReady();
       } 
    }
    

    private int   _data=0;
    private int   _exq=0;
    private int   _ptr=0;
    private boolean _first=true;
    
    private void byteReceived(int b) {
               int sw = b & 0xC0;
               switch(sw) {
                  case LAST:
                         if(_first) {
                            _data=0;
                            _ptr=0;
                            _exq  |= ((b&0x7)^3);
                         }
                         else {
                            _data |= ((b&0x3f)<<_ptr);
                            _ptr+=6;
                         }
                         dataReady();
                         break;
                  case NEXT:
                         if(_first) {
                             _ptr=0;                         
                             _data = 0;
                             _exq  |= ((b&0x7)^3);
                             _first=false;
                         }                         
                         else {
                             _data |= ((b&0x3f)<<_ptr);
                             _ptr+=6;   
                         }
                         try {out.write(REQUEST);}
                         catch(Exception ee){}
                         break;
                  case LAM: 
                         processLAM(); 
                         break;                                    
                  default:
                        System.out.println(Integer.toString(b&0xff,16));
                        //_exq  |= PROTOCOL;
                        break;
               }
    }

    private void processLAM() {
       th=new Thread(this);
       th.start();
    }
    
    private synchronized void dataReady(){
       _rdata = _data;
       _rexq  = _exq;
       _exq   = 0;
       _first = true;
       notify();
    }
    
    
 
    public void close() {
      if(port!= null) {
        try { out.close(); } catch (IOException e) { }
        try { in.close();  } catch (IOException e) { }
        port.close();
        id=null;
        out=null;
        in=null;
        port=null;
      }
    }
    
 
   public void run() {
     try{
      int LAM = ((DriverCC232)driver).readLAMs(crateN);
      for(int i=0;i<24;i++) {
          if(((LAM>>i)&1) != 0) driver.onLAM(crateN,i+1);
      }
     }catch(Exception e){
        e.printStackTrace();
     }
   }
    

}