#ifndef BmlLalHodoscopeReadout_HH
#define BmlLalHodoscopeReadout_HH

#include <iostream>
#include <fstream>

#include "RcdHeader.hh"
#include "RcdUserRW.hh"
#include "SubAccessor.hh"
#include "SubInserter.hh"
#include "DuplexSocket.hh"


class BmlLalHodoscopeReadout : public RcdUserRW {

public:
  BmlLalHodoscopeReadout(std::string ipAddress="localhost",
			 unsigned port=49153, unsigned tries=10) :
    RcdUserRW(), _socket(ipAddress.c_str(),port,tries), _readout(false), _cArray((char*)_array) {

    std::cout << std::endl << "BmlLalHodoscopeReadout::ctor()  SOCKET ESTABLISHED" << std::endl;

    char *c((char*)_command);
    c[ 0]='r';c[ 1]='u';c[ 2]='n';c[ 3]='d';
    c[ 4]='c';c[ 5]='f';c[ 6]='g';c[ 7]='d';
    c[ 8]='t';c[ 9]='r';c[10]='g';c[11]='z';
    c[12]='t';c[13]='r';c[14]='g';c[15]='d';
    c[16]='e';c[17]='v';c[18]='t';c[19]='d';

    for(unsigned i(0);i<5;i++) {
      std:: cout << "BmlLalHodoscopeReadout::ctor()  Command " << i << " = " 
		 << c[4*i+0] << c[4*i+1] << c[4*i+2] << c[4*i+3] << " = " 
		 << printHex(_command[i]) << std::endl;
    }
  }

  virtual ~BmlLalHodoscopeReadout() {
  }

  virtual bool record(RcdRecord &r) {
    if(doPrint(r.recordType())) {
      std::cout << std::endl << "BmlLalHodoscopeReadout::record()" << std::endl;
      r.RcdHeader::print(std::cout," ");
    }

    if(r.recordType()==RcdHeader::runStart) {

      // Access the DaqRunStart to get print level 
      SubAccessor accessor(r);
      std::vector<const DaqRunStart*> v(accessor.extract<DaqRunStart>());
      if(v.size()==1) _printLevel=v[0]->runType().printLevel();

      // Get run data
      assert(readRunData(r));
    }

    if(r.recordType()==RcdHeader::runEnd) {

      // Get run data
      assert(readRunData(r));
    }

    if(r.recordType()==RcdHeader::configurationStart) {
      SubAccessor accessor(r);

      std::vector<const BmlLalHodoscopeConfigurationData*>
        v(accessor.extract<BmlLalHodoscopeConfigurationData>());
 
      if(doPrint(r.recordType(),1)) {
        std::cout << " Number of BmlLalHodoscopeConfigurationData subrecords accessed = "
                  << v.size() << std::endl;
        for(unsigned i(0);i<v.size();i++) {
          v[i]->print(std::cout," ") << std::endl;
        }
      }

      // Determine if being used for this configuration
      _readout=(v.size()==1);

      // If so, then write configuration data
      if(_readout) {

	// Set configuration write
	_array[0]=_command[1];
	BmlLalHodoscopeConfigurationData *x((BmlLalHodoscopeConfigurationData*)(_array+1));
	*x=*(v[0]);
	x->numberOfWords(sizeof(BmlLalHodoscopeConfigurationData)/4 - 1);

	if(!_socket.send((char*)_array,4+sizeof(BmlLalHodoscopeConfigurationData))) {
	  std::cerr << "BmlLalHodoscopeReadout::record()  Error writing to socket;"
		    << " number of bytes written < " << 4+sizeof(BmlLalHodoscopeConfigurationData) << std::endl;
	  perror(0);
	  return false;
	}

	// Read back number of words
	int n(-1);
	n=_socket.recv((char*)_array,4);
	
	if(n!=4) {
	  std::cerr << "BmlLalHodoscopeReadout::readRunData()  Error reading from socket;"
		    << " number of bytes written = " << n << " < " << 4 << std::endl;
	  perror(0);
	  return false;
	}
    

	std::cout << "...got word " << printHex(_array[0]) << " = "
		  << _cArray[3] << _cArray[2]
		  << _cArray[1] << _cArray[0] << std::endl;


	// Check return
	if(_array[0]!=0x6b6f6b6f) {
	  std::cerr << "BmlLalHodoscopeReadout::record()  Error on cfgd;"
		    << " values returned = " << _array[0] << " != "
		    << 0x6b6f6b6f << std::endl;
	}




	/*


	//int nWords(_array[0]);
	//unsigned nWords(_array[1]+1);
	int nWords(60);

	n=-1;
	n=_socket.recv((char*)_array,4*nWords+1); //
	
	if(n!=4*nWords+1) {
	  std::cerr << "BmlLalHodoscopeReadout::record()  Error reading from socket;"
		    << " number of bytes read = " << n << " < " << 4*nWords+1 << std::endl;
	  perror(0);
	  return false;
	}

	if(doPrint(r.recordType(),2)) {
	  for(int i(0);i<(nWords+1);i++) {
	    std::cout << " Word " << std::setw(2) << i << " = "
		      << printHex(_array[i]) << " = "
		      << _cArray[3+4*i] << _cArray[2+4*i]
		      << _cArray[1+4*i] << _cArray[0+4*i] << std::endl;
	  }
	}

	*/
      }

      // Get configuration data
      assert(readConfigurationData(r));
    }

    if(r.recordType()==RcdHeader::configurationEnd) {
      if(_readout) {

	// Get configuration data
	assert(readConfigurationData(r));
      }
    }
    
    if(r.recordType()==RcdHeader::acquisitionStart) {
      if(_readout) {
	_numberOfTrgs=0;
	_numberOfEvts=0;

	// Zero trigger counter
	_array[0]=_command[2];
	if(!_socket.send((char*)_array,4)) {
	  std::cerr << "BmlLalHodoscopeReadout::record()  Error writing to socket;"
		    << " number of bytes written < " << 4 << std::endl;
	  perror(0);
	  return false;
	}

	int n(-1);
	n=_socket.recv((char*)_array,4);

	/*
	std::cout << "...got word " << printHex(_array[0]) << " = "
		  << _cArray[3] << _cArray[2]
		  << _cArray[1] << _cArray[0] << std::endl;
	*/	

	if(n!=4) {
	  std::cerr << "BmlLalHodoscopeReadout::record()  Error reading from socket;"
		    << " number of bytes read = " << n << " < 4" << std::endl;
	  perror(0);
	  return false;
	}

	// Check return
	if(_array[0]!=0x6b6f6b6f) {
	  std::cerr << "BmlLalHodoscopeReadout::record()  Error on trgz;"
		    << " values returned = " << _array[0] << " != "
		    << 0x6b6f6b6f << std::endl;
	}

	// Read back trigger counter
	assert(readTriggerData(r,readTriggerNumber()));
      }
    }
    
    if(r.recordType()==RcdHeader::acquisitionEnd) {
      if(_readout) {

	// Loop until hodoscope has caught up with DAQ
	unsigned i(0);
	for(i=0;i<10 && _numberOfTriggers<_numberOfTrgs;i++) {
	  readTriggerNumber();
	}

	if(i>1) std::cout << "BmlLalHodoscopeReadout::record()  "
			  << "Number of trgd reads for " << _numberOfTrgs
			  << " triggers = " << i << std::endl;
	
	// Read back trigger counter
	assert(readTriggerData(r,_numberOfTriggers));
      }
    }
    
    if(r.recordType()==RcdHeader::trigger ||
       r.recordType()==RcdHeader::triggerBurst) {
      if(_readout) {
	_numberOfTrgs++;

	// Read back trigger counter NO! UNRELIABLE DURING SPILL!
	//assert(readTriggerData(r,readTriggerNumber()));
      }
    }

    if(r.recordType()==RcdHeader::event) {
      if(_readout) {
	_numberOfEvts++;

	// Loop until hodoscope has caught up with DAQ
	unsigned i(0);
	for(i=0;i<10 && _numberOfTriggers<_numberOfEvts;i++) {
	  readTriggerNumber();
	}

	if(i>1) std::cout << "BmlLalHodoscopeReadout::record()  "
			  << "Number of trgd reads for " << _numberOfEvts
			  << " events = " << i << std::endl;
	
	_array[0]=_command[4];
	if(!_socket.send((char*)_array,4)) {
	  std::cerr << "BmlLalHodoscopeReadout::record()  Error writing to socket;"
		    << " number of bytes written < " << 4 << std::endl;
	  perror(0);
	  return false;
	}
	
	memset(_array,0xff,4*1024);

	int n(-1);
	n=_socket.recv((char*)_array,4);
	
	if(n!=4) {
	  std::cerr << "BmlLalHodoscopeReadout::record()  Error reading from socket;"
		    << " number of bytes written = " << n << " < " << 4 << std::endl;
	  perror(0);
	  return false;
	}

	int nWords(_array[0]);

	n=-1;
	n=_socket.recv((char*)(_array+1),4*nWords);
	
	if(n!=4*nWords) {
	  std::cerr << "BmlLalHodoscopeReadout::record()  Error reading from socket;"
		    << " number of bytes written = " << n << " < " << 4*nWords << std::endl;
	  perror(0);
	  return false;
	}

	if(doPrint(r.recordType(),2)) {
	  for(int i(0);i<(nWords+1);i++) {
	    std::cout << " Word " << std::setw(2) << i << " = "
		      << printHex(_array[i]) << " = "
		      << _cArray[3+4*i] << _cArray[2+4*i]
		      << _cArray[1+4*i] << _cArray[0+4*i] << std::endl;
	  }
	}

	SubInserter inserter(r);
	BmlLalHodoscopeEventData *x(inserter.insert<BmlLalHodoscopeEventData>());
	memcpy(x,_array,4*(nWords+1));
	inserter.extend(4*x->numberOfHits());
	
	if(doPrint(r.recordType(),1)) {
	  std::cout << std::endl << "BmlLalHodoscopeReadout::record()"
		    << "  Event data added to record" << std::endl;
	  x->print(std::cout," ") << std::endl;
	}
      }
    }

    return true;
  }

  bool readRunData(RcdRecord &r) {
      
    // Request run data
    _array[0]=_command[0];
    if(!_socket.send((char*)_array,4)) {
      std::cerr << "BmlLalHodoscopeReadout::readRunData()  Error writing to socket;"
		<< " number of bytes written < " << 4 << std::endl;
      perror(0);
      return false;
    }
    
    // Read back number of words
    memset(_array,0xff,sizeof(BmlLalHodoscopeRunData));

    int n(-1);
    n=_socket.recv((char*)_array,4);
    
    if(n!=4) {
      std::cerr << "BmlLalHodoscopeReadout::readRunData()  Error reading from socket;"
		<< " number of bytes written = " << n << " < " << 4 << std::endl;
      perror(0);
      return false;
    }
    
    int nWords(_array[0]);

    if(nWords>0) {
      int n(-1);
      n=_socket.recv((char*)(_array+1),4*nWords);
      
      if(n!=4*nWords) {
	std::cerr << "BmlLalHodoscopeReadout::readRunData()  Error reading from socket;"
		  << " number of bytes written = " << n << " < " << 4*nWords << std::endl;
	perror(0);
	return false;
      }
    }
    
    if(doPrint(r.recordType(),2)) {
      for(int i(0);i<(nWords+1);i++) {
	std::cout << " Word " << std::setw(2) << i << " = "
		  << printHex(_array[i]) << " = "
		  << _cArray[3+4*i] << _cArray[2+4*i]
		  << _cArray[1+4*i] << _cArray[0+4*i] << std::endl;
      }
    }
    
    BmlLalHodoscopeRunData *x((BmlLalHodoscopeRunData*)_array);
    SubInserter inserter(r);
    inserter.insert<BmlLalHodoscopeRunData>(*x);

    if(doPrint(r.recordType(),1)) {
      std::cout << std::endl << "BmlLalHodoscopeReadout::readRunData()"
		<< "  Run data added to record" << std::endl;
      x->print(std::cout," ") << std::endl;
    }

    return true;
  }

  bool readConfigurationData(RcdRecord &r) {
    if(_readout) {
      
      _array[0]=_command[1];
      if(!_socket.send((char*)_array,4)) {
	std::cerr << "BmlLalHodoscopeReadout::readConfigurationData()  Error writing to socket;"
		  << " number of bytes written < " << 4 << std::endl;
	perror(0);
	return false;
      }
      
      memset(_array,0xff,sizeof(BmlLalHodoscopeConfigurationData));
      
      int n(-1);
      n=_socket.recv((char*)_array,4);
      
      if(n!=4) {
	std::cerr << "BmlLalHodoscopeReadout::readConfigurationData()  Error reading from socket;"
		  << " number of bytes written = " << n << " < " << 4 << std::endl;
	perror(0);
	return false;
      }
      
      int nWords(_array[0]);
      //nWords=0;
      
      if(nWords>0) {
	n=-1;
	n=_socket.recv((char*)(_array+1),4*nWords);
	
	if(n!=4*nWords) {
	  std::cerr << "BmlLalHodoscopeReadout::readConfigurationData()  Error reading from socket;"
		    << " number of bytes written = " << n << " < " << 4*nWords << std::endl;
	  perror(0);
	  return false;
	}
      }
      
      if(doPrint(r.recordType(),2)) {
	for(int i(0);i<(nWords+1);i++) {
	  std::cout << " Word " << std::setw(2) << i << " = "
		    << printHex(_array[i]) << " = "
		    << _cArray[3+4*i] << _cArray[2+4*i]
		    << _cArray[1+4*i] << _cArray[0+4*i] << std::endl;
	}
      }
      
      BmlLalHodoscopeConfigurationData *x((BmlLalHodoscopeConfigurationData*)_array);
      SubInserter inserter(r);
      inserter.insert<BmlLalHodoscopeConfigurationData>(*x);
      
      if(doPrint(r.recordType(),1)) {
	std::cout << std::endl << "BmlLalHodoscopeReadout::readConfigurationData()"
		  << "  Configuration data added to record" << std::endl;
	x->print(std::cout," ") << std::endl;
      }
    }
    
    return true;
  }

  unsigned readTriggerNumber() {
    _array[0]=_command[3];
    if(!_socket.send((char*)_array,4)) {
      std::cerr << "BmlLalHodoscopeReadout::readTriggerNumber()  Error writing to socket;"
		<< " number of bytes written < " << 4 << std::endl;
      perror(0);
      return 0xffffffff;
    }
    
    int n(-1);
    n=_socket.recv((char*)_array,4);
    /*
    std::cout << "...got word " << printHex(_array[0]) << " = "
	      << _cArray[3] << _cArray[2]
	      << _cArray[1] << _cArray[0] << std::endl;
    */
    
    if(n!=4) {
      std::cerr << "BmlLalHodoscopeReadout::readTriggerNumber()  Error reading from socket;"
		<< " number of bytes written = " << n << " < 4" << std::endl;
      perror(0);
      return 0xffffffff;
    }

    _numberOfTriggers=_array[0];
    return _array[0];
  }

  bool readTriggerData(RcdRecord &r, unsigned nTrg) {
    /*
    _array[0]=_command[3];
    if(!_socket.send((char*)_array,4)) {
      std::cerr << "BmlLalHodoscopeReadout::record()  Error writing to socket;"
		<< " number of bytes written < " << 4 << std::endl;
      perror(0);
      return false;
    }
    
    int n(-1);
    n=_socket.recv((char*)_array,4);

    std::cout << "...got word " << printHex(_array[0]) << " = "
	      << _cArray[3] << _cArray[2]
	      << _cArray[1] << _cArray[0] << std::endl;
    
    if(n!=4) {
      std::cerr << "BmlLalHodoscopeReadout::record()  Error reading from socket;"
		<< " number of bytes written = " << n << " < 4" << std::endl;
      perror(0);
      return false;
    }
    */
    
    SubInserter inserter(r);
    BmlLalHodoscopeTriggerData *x(inserter.insert<BmlLalHodoscopeTriggerData>(true));
    x->counter(nTrg);

    if(doPrint(r.recordType(),1)) {
      std::cout << std::endl << "BmlLalHodoscopeReadout::readTriggerData()"
		<< "  Trigger data added to record" << std::endl;
	x->print(std::cout," ") << std::endl;
    }
    
    return true;
  }


private:
  DuplexSocket _socket;
  bool _readout;
  unsigned _command[5];
  unsigned _array[1024];
  char *_cArray;
  unsigned _numberOfTriggers;
  unsigned _numberOfTrgs;
  unsigned _numberOfEvts;
};

#endif
