#ifndef __JCOMPAREHISTOGRAMS__JTEST_T__
#define __JCOMPAREHISTOGRAMS__JTEST_T__

#include <istream>
#include <ostream>
#include <vector>

#include "Jeep/JPrint.hh"
#include "Jeep/JProperties.hh"

#include "JLang/JPredicate.hh"
#include "JLang/JColorFacet.hh"

#include "JCompareHistograms/JTestResult.hh"

#include <TFile.h>
#include <TObject.h>


/**
 * \author rgruiz, bjung
 */

namespace JCOMPAREHISTOGRAMS {}
namespace JPP { using namespace JCOMPAREHISTOGRAMS; }

namespace JCOMPAREHISTOGRAMS {


  /**
   * Interface to read input and write output for TObject tests.\n
   * This is an abstract class from which the different tests are derived.\n
   * All tests have in common the methods from this interface.\n
   * For each test, the parameters are passed through an istream, and parsed by the read() method.\n
   * For each test, the different results are stored in the internal results buffer.\n
   * The write() method is used to print a message with the result of the test.\n
   * The save() method is used to save a graphical output with the test results into a .root file.\n
   * For a list of available tests see JTestDictionary()
   */
  class JTest_t :
    public std::vector<JTestResult>
  {
  public:

    /**
     * Constructor.
     *
     * \param  testName      test name
     * \param  resultType    result type
     */
    JTest_t(const std::string& testName,
	    const std::string& resultType) :
      testName  (testName),
      resultType(resultType)
    {}

    
    /**
     * Tests compatibility between two TObjects.
     * 
     * \param  o1            First object
     * \param  o2            Second object
     */
    virtual void test(const TObject* o1, const TObject* o2) = 0;

    
    /**
     * Read test parameters from input.
     *
     * \param  in            input stream
     * \return               input stream
     */
    virtual std::istream& read (std::istream& in) = 0;

    
    /**
     * Write test result to output.
     *
     * \param  out           output stream
     * \param  delimiter     field delimiter
     * \param  onlyFailures  if true, write only failures.
     * \return               output stream
     */
    virtual std::ostream& write(std::ostream& out,
				const char    delimiter    = ' ',
				const bool    onlyFailures = false) const
    {
      using namespace std;
      using namespace JPP;

      for (vector<JTestResult>::const_iterator r = this->begin() ; r != this->end() ; ++r) {

	if (onlyFailures && r->passed) { continue; }
	
	print(out, *r, delimiter, true);
      }
      
      return out;
    }

    
    /**
     * Writes the test result to root file
     * \param f              A ROOT file 
     * \param path           Path in root file.
     * \param onlyFailures   If true, write only failures.
     */
    virtual void save(TFile* f ,
		      const std::string& path,
		      const bool         onlyFailures = false) const
    {
      using namespace std;
      using namespace JPP;

      if (f->GetDirectory(path.c_str())==0) {
	f->mkdir(path.c_str());
      }
      
      f->cd(path.c_str());

      for (vector<JTestResult>::const_iterator r = this->begin() ; r != this->end() ; ++r) {

	if (onlyFailures && r->passed) { continue; }
	
	r->obj->Write();
      }
    }


    /**
     * Get test name.
     *
     * \return               test name
     */
    const std::string& getTestName() const
    {
      return testName;
    }


    /**
     * Get result type.
     *
     * \return               result type
     */
    const std::string& getResultType() const
    {
      return resultType;
    }


    /**
     * Read test parameters from input.
     * 
     * \param  in            input stream
     * \param  test          test
     * \return               input stream
     */
    friend inline std::istream& operator>>(std::istream& in, JTest_t& test)
    {
      return test.read(in);
    }
    

    /**
     * Write test result to output.
     *
     * \param  out           output stream
     * \param  test          test
     * \return               output stream
     */
    friend inline std::ostream& operator<<(std::ostream& out, const JTest_t& test)
    {
      return test.write(out);
    }

    
  protected:

    const std::string testName;    //!< test name
    const std::string resultType;  //!< test result type
  };
}
  
#endif