// $Id: ConditionsRepository.cpp 2336 2016-09-07 17:27:40Z markus.frank@cern.ch $
//==========================================================================
//  AIDA Detector description implementation for LCD
//--------------------------------------------------------------------------
// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
// All rights reserved.
//
// For the licensing terms see $DD4hepINSTALL/LICENSE.
// For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
//
// Author     : M.Frank
//
//==========================================================================

// Framework include files
#include "DDCond/ConditionsTextRepository.h"
#include "DDCond/ConditionsIOVPool.h"
#include "DDCond/ConditionsTags.h"
#include "DD4hep/Printout.h"
#include "XML/DocumentHandler.h"
#include "XML/XMLTags.h"

// C/C++ include files
#include <fstream>
#include <climits>
#include <cerrno>
#include <map>

using namespace std;
using namespace DD4hep;
using namespace DD4hep::Conditions;
typedef XML::Handle_t xml_h;
typedef XML::Element xml_elt_t;
typedef XML::Document xml_doc_t;
typedef XML::Collection_t xml_coll_t;

typedef map<Condition::key_type,Condition> AllConditions;

/// Default constructor
ConditionsTextRepository::ConditionsTextRepository()  {
}

/// Default destructor
ConditionsTextRepository::~ConditionsTextRepository()   {
}

namespace {

  int createXML(const string& output, const AllConditions& all) {
    const char comment[] = "\n"
      "      +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"
      "      ++++   Linear collider detector description LCDD in C++  ++++\n"
      "      ++++   DD4hep Detector description generator.            ++++\n"
      "      ++++                                                     ++++\n"
      "      ++++                                                     ++++\n"
      "      ++++                              M.Frank CERN/LHCb      ++++\n"
      "      +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n  ";
    DD4hep::XML::DocumentHandler docH;
    xml_doc_t doc = docH.create("collection", comment);
    xml_elt_t root = doc.root(), cond(0);
    for(AllConditions::const_iterator i=all.begin(); i!=all.end(); ++i)  {
      char text[32];
      Condition c = (*i).second;
      ::snprintf(text,sizeof(text),"0x%08X",c.key());
      root.append(cond = xml_elt_t(doc, _U(ref)));
      cond.setAttr(_U(key), text);
      cond.setAttr(_U(name), c.name());
      cond.setAttr(_U(ref), c.address());
    }
    printout(ALWAYS,"ConditionsRepository","++ Handled %ld conditions.",all.size());
    if ( !output.empty() )  {
      return docH.output(doc, output);
    }
    return 1;
  }

  /// Load the repository from file and fill user passed data structory
  int readXML(const string& input, ConditionsTextRepository::Data& data)    {
    struct Conv {
      /// Reference to optional user defined parameter
      ConditionsTextRepository::Data& data;
      /// Initializing constructor of the functor
      Conv(ConditionsTextRepository::Data& p) : data(p) {}
      /// Callback operator to be specialized depending on the element type
      void operator()(xml_h element) const   {
        string key = element.attr<string>(_U(key));
        size_t cap = data.capacity();
        ConditionsTextRepository::Entry e;
        ::sscanf(key.c_str(),"0x%08X",&e.key);
        e.name = element.attr<string>(_U(name));
        e.address = element.attr<string>(_U(ref));
        if ( data.size() == cap ) data.reserve(cap+500);
        data.push_back(e);
      }
    };
    XML::DocumentHolder doc(XML::DocumentHandler().load(input));
    xml_h root = doc.root();
    xml_coll_t(root, _U(ref)).for_each(Conv(data));
    return 1;
  }
  
  int createText(const string& output, const AllConditions& all, char sep)   {
    size_t siz_nam=0, siz_add=0, siz_tot=0;
    char fmt[64], text[2*PATH_MAX+64];
    ofstream out(output);
    if ( !out.good() )  {
      except("ConditionsTextRepository",
             "++ Failed to open output file:%s [errno:%d %s]",
             output.c_str(), errno, ::strerror(errno));
    }
    else if ( sep )  {
      ::snprintf(fmt,sizeof(fmt),"%%08X%c%%s%c%%s%c",sep,sep,sep);
    }
    else   {
      for(AllConditions::const_iterator i=all.begin(); i!=all.end(); ++i)  {
        Condition::Object* c = (*i).second.ptr();
        size_t siz_n = c->name.length();
        size_t siz_a = c->address.length();
        if ( siz_nam < siz_n ) siz_nam = siz_n;
        if ( siz_add < siz_a ) siz_add = siz_a;
        if ( siz_tot < (siz_n+siz_a) ) siz_tot = siz_n+siz_a;
      }
      siz_tot += 8+2+1;
      ::snprintf(fmt,sizeof(fmt),"%%08X %%-%lds %%-%lds",long(siz_nam),long(siz_add));
    }
    out << "dd4hep." << char(sep ? sep : '-')
        << "." << long(siz_nam)
        << "." << long(siz_add)
        << "." << long(siz_tot) << endl;
    for(AllConditions::const_iterator i=all.begin(); i!=all.end(); ++i)  {
      Condition c = (*i).second;
      ::snprintf(text, sizeof(text), fmt, c.key(), c.name().c_str(), c.address().c_str());
      out << text << endl;
    }
    out.close();
    return 1;
  }

  /// Load the repository from file and fill user passed data structory
  int readText(const string& input, ConditionsTextRepository::Data& data)    {
    size_t idx;
    ConditionsTextRepository::Entry e;
    long siz_nam, siz_add, siz_tot;
    char sep, c, text[2*PATH_MAX+64];
    ifstream in(input);
    in >> c >> c >> c >> c >> c >> c >> c >> sep 
       >> c >> siz_nam
       >> c >> siz_add
       >> c >> siz_tot;
    text[0] = 0;
    in.getline(text,sizeof(text),'\n');
    do {
      text[0] = 0;
      in.getline(text,sizeof(text),'\n');
      if ( in.good() )  {
        if ( siz_tot )  {
          // Direct access mode with fixed record size
          text[8] = text[9+siz_nam] = text[10+siz_nam+siz_add] = 0;
          e.name = text+9;
          e.address = text+10+siz_nam;  
          if ( (idx=e.name.find(' ')) != string::npos )
            e.name[idx]=0;
          if ( (idx=e.address.find(' ')) != string::npos )
            e.address[idx]=0;
        }
        else  {
          // Variable record size
          e.name=text+9;
          if ( (idx=e.name.find(sep)) != string::npos )
            text[9+idx]=0, e.address=text+idx+10, e.name=text+9;
          if ( (idx=e.address.find(sep)) != string::npos )
            e.address[idx]=0;
          else if ( (idx=e.address.find('\n')) != string::npos )
            e.address[idx]=0;
        }
        size_t cap = data.capacity();
        ::sscanf(text,"%08X",&e.key);
        if ( data.size() == cap ) data.reserve(cap+500);
        data.push_back(e);
      }
    } while(in.good() && !in.eof() );
    in.close();
    return 1;
  }
}

/// Save the repository to file
int ConditionsTextRepository::save(ConditionsManager manager, const string& output)  const  {
  typedef vector<const IOVType*> _T;
  typedef ConditionsIOVPool::Elements _E;
  typedef RangeConditions _R;
  AllConditions all;
  const _T types = manager.iovTypesUsed();
  for( _T::const_iterator i = types.begin(); i != types.end(); ++i )    {
    const IOVType* type = *i;
    if ( type )   {
      ConditionsIOVPool* pool = manager.iovPool(*type);
      if ( pool )  {
        const _E& e = pool->elements;
        for (_E::const_iterator j=e.begin(); j != e.end(); ++j)  {
          ConditionsPool* cp = (*j).second;
          _R rc;
          cp->select_all(rc);
          for(_R::const_iterator ic=rc.begin(); ic!=rc.end(); ++ic)
            all[(*ic).key()] = *ic;
        }
      }
    }
  }

  if ( output.find(".xml") != string::npos )   {
    /// Write XML file with conditions addresses
    return createXML(output, all);
  }
  else if ( output.find(".txt") != string::npos )   {
    /// Write fixed records with conditions addresses
    return createText(output, all, 0);
  }
  else if ( output.find(".daf") != string::npos )   {
    /// Write fixed records with conditions addresses
    return createText(output, all, 0);
  }
  else if ( output.find(".csv") != string::npos )   {
    /// Write records separated by ';' with conditions addresses
    return createText(output, all, ';');
  }
  return 0;
}

/// Load the repository from file and fill user passed data structory
int ConditionsTextRepository::load(const string& input, Data& data)  const  {
  if ( input.find(".xml") != string::npos )   {
    return readXML(input, data);
  }
  else if ( input.find(".txt") != string::npos )   {
    return readText(input, data);
  }
  else if ( input.find(".daf") != string::npos )   {
    return readText(input, data);
  }
  else if ( input.find(".csv") != string::npos )   {
    return readText(input, data);
  }
  return 0;
}