#ifndef __JDB_JPMTRUNSETUPPARAMS__
#define __JDB_JPMTRUNSETUPPARAMS__

#include <ostream>
#include <sstream>
#include <string>
#include <map>

#include "JDB/JDB.hh"
#include "JDB/JSelector.hh"
#include "JDB/JSelectorSupportkit.hh"
#include "JDB/JDBToolkit.hh"
#include "JDB/JRuns.hh"
#include "JDB/JAllParams.hh"
#include "JDB/JRunsetupParams.hh"
#include "JDB/JUPI_t.hh"


/**
 * \author mdejong
 */
namespace JDATABASE {}
namespace JPP { using namespace JDATABASE; }

namespace JDATABASE {

  const char* const OPTICS_t         =  "OPTICS";          //!< sub-system of PMT
  const char* const PMT_THRESHOLD_t  =  "PMT_THRESHOLD";   //!< parameter name of PMT threshold
  const char* const PMT_HV_t         =  "PMT_HIGHVOLT";    //!< parameter name of PMT high voltage


  /**
   * Auxiliary class for PMT run setup parameters.
   *
   * This class provides for an implementation of the fallback method based on the %UPI of a PMT.
   */
  template<class JValue_t>
  class JPMTRunsetupParams :
    public std::map<std::string, JValue_t>
  {
  public:
    /**
     * Type definition of PMT parameter value.
     */
    struct result_type {
      bool     is_default;      //!< true if fallback; else false
      JValue_t value;           //!< value
    };


    /**
     * Get fallback %UPI.
     *
     * \param  upi            %UPI
     * \return                %UPI
     */
    static std::string getUPI(const JUPI_t& upi)
    {
      std::ostringstream os;

      os << upi.getPBS() 
	 << JUPI_t::SEPARATOR 
	 << upi.getVariant() 
	 << JUPI_t::SEPARATOR 
	 << JUPI_t::DOT;

      return os.str();
    }


    /**
     * Default constructor.
     */
    JPMTRunsetupParams()
    {}


    /**
     * Configure PMT run setup parameters for given detector and run.
     *
     * \param  id             detector identifier
     * \param  run            run number
     * \param  parameter      parameter name
     */
    void configure(const int id, const int run, const std::string& parameter)
    {
      using namespace std;
      using namespace JPP;

      this->clear();

      string     rs_oid;
      JAllParams upars;

      {
	ResultSet& rs = getResultSet(getTable<JRuns>(), getSelector<JRuns>(id, run));

	JRuns parameters;

	if (rs >> parameters) {
	  rs_oid = parameters.RUNSETUPID;
	}

	rs.Close();
      }
      {
	ResultSet& rs = getResultSet(getTable<JAllParams>(), getSelector<JAllParams>(OPTICS_t));

	for (JAllParams parameters; rs >> parameters; ) {
	  if (parameters.NAME == parameter) {
	    upars = parameters;
	  }
	}

	rs.Close();
      }
      {
	ResultSet& rs = getResultSet(getTable<JRunsetupParams>(), getSelector<JRunsetupParams>(getDetector(id), rs_oid));

	for (JRunsetupParams parameters; rs >> parameters; ) {
	  if (parameters.PAR_OID == upars.OID && parameters.ISINPUT == 'Y') {
	    if (parameters.VALUE != "") {
	      istringstream(parameters.VALUE) >> (*this)[parameters.UPIFILTER];
	    }
	  }
	}
      
	rs.Close();
      }
    }


    /**
     * Get PMT parameter value for given %UPI of PMT.
     *
     * \param  upi            %UPI
     * \return                PMT parameter value
     */
    result_type operator()(const JUPI_t& upi) const
    {
      typename JPMTRunsetupParams::const_iterator p = this->find(upi.toString());
  
      const bool is_default = (p == this->end());

      if (is_default) {
	p = this->find(getUPI(upi));
      }

      if (p != this->end())
	return { is_default, p->second };
      else
	THROW(JDatabaseException, "Invalid UPI " << upi);
    }

    
    /**
     * Write PMT run setup parameters to output stream.
     *
     * \param  out            output stream
     * \param  object         PMT run setup parameters
     * \return                output stream
     */
    friend inline std::ostream& operator<<(std::ostream& out, const JPMTRunsetupParams& object)
    {
      using namespace std;

      for (typename JPMTRunsetupParams::const_iterator i = object.begin(); i != object.end(); ++i) {
	out << left << setw(32) << i->first << ' ' << right << setw(6) << i->second << endl;
      }

      return out;
    }
  };
}

#endif