//
// $Id: DhcConfigReader.hh,v 1.1 2008/06/27 10:34:01 meyern Exp $
//

#ifndef DhcConfigReader_HH
#define DhcConfigReader_HH

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <map>

#include "tinyxml.h"

#include "DhcLocation.hh"
#include "DhcLocationData.hh"
#include "DhcBeConfigurationData.hh"
#include "DhcFeConfigurationData.hh"
#include "DhcReadoutConfigurationData.hh"

#include "TtmLocation.hh"
#include "TtmLocationData.hh"
#include "TtmConfigurationData.hh"


class DhcConfigReader {

public:
  enum DhcElement {
    location=0x0,
    dcol,
    dcon,
    dcal,
    dhcal,
    trigger,
    pollInterval,
    enable,
    endOfDhcElementEnum
  };

  enum DhcAttribute {
    id=0x0,
    crate,
    slot,
    dcon_enable,
    csr,
    con,
    chip,
    plsr,
    intd,
    shp2,
    shp1,
    blrd,
    vtnd,
    vtpd,
    dcr,
    inj,
    kill,
    type,
    sec,
    usec,
    feslot,
    endOfDhcAttributeEnum
  };

  DhcConfigReader();
  virtual ~DhcConfigReader();

  bool load(const char* filename);
  void load_objects(TiXmlElement* pElem);
  bool reset();

  std::vector< DhcLocationData<DhcBeConfigurationData>* > vBcd() const {
    return _vBcd;
  }
  std::vector< DhcLocationData<DhcFeConfigurationData>* > vFcd() const {
    return _vFcd;
  }
  std::vector< DhcReadoutConfigurationData* > vRcd() const {
    return _vRcd;
  }

private:
  std::map<std::string, unsigned> _elementMap;
  std::map<std::string, unsigned> _attributeMap;
  std::map<std::string, unsigned> _componentMap;

  static const std::string _dhcElementName[endOfDhcElementEnum];
  static const std::string _dhcAttributeName[endOfDhcAttributeEnum];
  static const std::string _dhcComponentName[DhcLocation::endOfDhcComponentEnum];

  std::vector< DhcLocationData<DhcBeConfigurationData>* > _vBcd;
  std::vector< DhcLocationData<DhcFeConfigurationData>* > _vFcd;
  std::vector< DhcReadoutConfigurationData* > _vRcd;

  DhcLocation _location;
  DhcBeConfigurationData _beData;
  DhcFeConfigurationData _feData;
  DhcReadoutConfigurationData _roData;

};

DhcConfigReader::DhcConfigReader()
{
  for (unsigned i(0); i<endOfDhcElementEnum; i++ )
  _elementMap[_dhcElementName[i]] = i;

  for (unsigned i(0); i<endOfDhcAttributeEnum; i++ )
  _attributeMap[_dhcAttributeName[i]] = i;

  for (unsigned i(0); i<DhcLocation::endOfDhcComponentEnum; i++ )
  _componentMap[_dhcComponentName[i]] = i;

}

DhcConfigReader::~DhcConfigReader()
{
  reset();
}

bool
DhcConfigReader::reset()
{
  for (unsigned i(0); i<_vBcd.size(); i++)
    delete _vBcd[i];
  for (unsigned i(0); i<_vFcd.size(); i++)
    delete _vFcd[i];
  for (unsigned i(0); i<_vRcd.size(); i++)
    delete _vRcd[i];

  _vBcd.clear();
  _vFcd.clear();
  _vRcd.clear();

  return true;
}

bool
DhcConfigReader::load(const char* pFilename)
{
  // clear any existing configs
  reset();

  TiXmlDocument doc(pFilename);
  if (!doc.LoadFile()) {
    std::cerr << "Error loading configuration file "
	      << pFilename << std::endl;
    return false;
  }

  TiXmlHandle hDoc(&doc);
  TiXmlElement* pElem;
  TiXmlHandle hRoot(0);

  // block: name
  {
    pElem=hDoc.FirstChildElement().Element();
    // should always have a valid root but handle gracefully if it does
    if (!pElem) return false;

    // save this for later
    hRoot=TiXmlHandle(pElem);
  }
 
  // block: ReadoutConfiguration
  {
    pElem=hRoot.FirstChild( "ReadoutConfiguration" ).Element();
    for( ; pElem; pElem=pElem->NextSiblingElement("ReadoutConfiguration"))
      {
	load_objects(pElem);

	_vRcd.push_back(new DhcReadoutConfigurationData(_roData));
      }
  }

  // block: BeConfiguration
  {
    pElem=hRoot.FirstChild( "BeConfiguration" ).Element();
    for( ; pElem; pElem=pElem->NextSiblingElement("BeConfiguration"))
      {
	load_objects(pElem);

	size_t j = _vBcd.size();
	_location.dhcComponent(DhcLocation::be);
	_vBcd.push_back(new DhcLocationData<DhcBeConfigurationData>(_location));

	_vBcd[j]->data(_beData);
      }
  }

  // block: FeConfiguration
  {
    pElem=hRoot.FirstChild( "FeConfiguration" ).Element();
    for( ; pElem; pElem=pElem->NextSiblingElement("FeConfiguration"))
      {
	load_objects(pElem);

	size_t j = _vFcd.size();
	_vFcd.push_back(new DhcLocationData<DhcFeConfigurationData>(_location));

	_vFcd[j]->data(_feData);
      }
  }

  return true;
}

void
DhcConfigReader::load_objects(TiXmlElement* pElem)
{
  for( pElem=pElem->FirstChildElement(); pElem; pElem=pElem->NextSiblingElement())
    {
      unsigned _element=0xffff;

      std::string pKey(pElem->Value());
      if (_elementMap.find(pKey) != _elementMap.end()) {
	_element = _elementMap[pKey];
      }

      TiXmlAttribute* pAttrib=pElem->FirstAttribute();
      while (pAttrib)
	{
	  unsigned _attribute=0xffff;

	  std::string pAttr(pAttrib->Name());
	  if (_attributeMap.find(pAttr) != _attributeMap.end()) {
	    _attribute = _attributeMap[pAttr];

	  }

	  switch (_element) {
	  case location: {
	    switch (_attribute) {
	    case crate: {
	      _location.crateNumber(strtol(pAttrib->Value(),0,0));
	      break;
	    }
	    case slot: {
	      const char* s=pAttrib->Value();
	      if (strcmp(s,"broadcast") == 0)
		_location.slotBroadcast(true);
	      else
		_location.slotNumber(strtol(s,0,0));
	      break;
	    }

	    default: {
	      break;
	    }
	    };
	    break;
	  }

	  case dcol: {
	    switch (_attribute) {
	    case dcon_enable: {
	      _beData.dconEnable(strtol(pAttrib->Value(),0,0));
	      break;
	    }

	    case csr: {
	      _beData.csr(strtol(pAttrib->Value(),0,0));
	      break;
	    }

	    default: {
	      break;
	    }
	    };
	    break;
	  }

	  case dcon: {
	    switch (_attribute) {
	    case con: {
	      if (_componentMap.find(pAttrib->Value()) != _componentMap.end()) {
		unsigned _component = _componentMap[std::string(pAttrib->Value())];
		_location.dhcComponent(static_cast<DhcLocation::DhcComponent>(_component));
	      }
	      break;
	    }

	    default: {
	      break;
	    }
	    };
	    break;
	  }

	  case dcal: {
	    switch (_attribute) {
	    case chip: {
	      _feData.chipid(strtol(pAttrib->Value(),0,0));
	      break;
	    }
	    case plsr: {
	      _feData.plsr(strtol(pAttrib->Value(),0,0));
	      break;
	    }
	    case intd: {
	      _feData.intd(strtol(pAttrib->Value(),0,0));
	      break;
	    }
	    case shp2: {
	      _feData.shp2(strtol(pAttrib->Value(),0,0));
	      break;
	    }
	    case shp1: {
	      _feData.shp1(strtol(pAttrib->Value(),0,0));
	      break;
	    }
	    case blrd: {
	      _feData.blrd(strtol(pAttrib->Value(),0,0));
	      break;
	    }
	    case vtnd: {
	      _feData.vtnd(strtol(pAttrib->Value(),0,0));
	      break;
	    }
	    case vtpd: {
	      _feData.vtpd(strtol(pAttrib->Value(),0,0));
	      break;
	    }
	    case dcr: {
	      _feData.dcr(strtol(pAttrib->Value(),0,0));
	      break;
	    }
	    case inj: {
	      _feData.inj(strtoll(pAttrib->Value(),0,0));
	      break;
	    }
	    case kill: {
	      _feData.kill(strtoll(pAttrib->Value(),0,0));
	      break;
	    }

	    default: {
	      break;
	    }
	    };
	    break;
	  }

	  case dhcal: {
	    switch (_attribute) {
	    case crate: {
	      _roData.crateNumber(strtol(pAttrib->Value(),0,0));
	      break;
	    }

	    default: {
	      break;
	    }
	    };
	    break;
	  }

	  case trigger: {
	    switch (_attribute) {
	    case slot: {
	      break;
	    }

	    case type: {
	      const char* ttype=pAttrib->Value();
	      if (strcmp(ttype,"internal") == 0)
		_roData.triggerInternally(1);
	      else if (strcmp(ttype,"external") == 0)
		_roData.triggerExternally(1);
	      else if (strcmp(ttype,"waiting") == 0)
		_roData.triggerByWaiting(1);
	      break;
	    }

	    default: {
	      break;
	    }
	    };
	    break;
	  }

	  case pollInterval: {
	    int s; int u;
	    switch (_attribute) {
	    case sec: {
	      s=strtol(pAttrib->Value(),0,0);
	      break;
	    }

	    case usec: {
	      u=strtol(pAttrib->Value(),0,0);
	      break;
	    }

	    default: {
	      break;
	    }
	    };
	    _roData.pollInterval(UtlTimeDifference(s,u));
	    break;
	  }

	  case enable: {
	    unsigned s;
	    switch (_attribute) {
	    case slot: {
	      s=strtol(pAttrib->Value(),0,0);
	      _roData.slotEnable(s,true);
	      break;
	    }

	    case feslot: {
	      _roData.slotFeEnables(s,strtol(pAttrib->Value(),0,0));
	      break;
	    }

	    default: {
	      break;
	    }
	    };
	    break;
	  }

	  default: {
	    break;
	  }
	  };

	  pAttrib=pAttrib->Next();
	}

      load_objects(pElem);
    }
}

const std::string DhcConfigReader::_dhcElementName[]={
  "location",  "dcol",  "dcon",  "dcal",  "dhcal",
  "trigger",  "pollInterval",  "enable"
};

const std::string DhcConfigReader::_dhcAttributeName[]={
  "id",  "crate",  "slot",  "dcon_enable",  "csr",
  "con",  "chip",  "plsr",  "intd",  "shp2",  "shp1",
  "blrd",  "vtnd",  "vtpd",  "dcr",  "inj",  "kill",
  "type",  "sec",  "usec",  "feslot"
};

const std::string DhcConfigReader::_dhcComponentName[]={
  "dc0",  "dc1",  "dc2",  "dc3",  "dc4",  "dc5",
  "dc6",  "dc7",  "dc8",  "dc9",  "dc10",  "dc11",
  "be",  "febroadcast"
};

#endif // DhcConfigReader_HH
