/*---------------------------------------------------------------------\
|                          ____ _   __ __ ___                          |
|                         |__  / \ / / . \ . \                         |
|                           / / \ V /|  _/  _/                         |
|                          / /__ | | | | | |                           |
|                         /_____||_| |_| |_|                           |
|                                                                      |
\---------------------------------------------------------------------*/
/** \file	zypp-core/parser/inidict.cc
 *
*/

#include "inidict.h"

#include <iostream>
#include <map>
#include <string>

#include <zypp-core/base/Logger.h>

using std::endl;

///////////////////////////////////////////////////////////////////
namespace zypp
{ /////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////
  namespace parser
  { /////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////
    //
    //	CLASS NAME : IniDict
    //
    ///////////////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////////////
    //
    //	METHOD NAME : IniDict::IniDict
    //	METHOD TYPE : Ctor
    //
    IniDict::IniDict( const InputStream &is,
                      const ProgressData::ReceiverFnc & progress )
    {
      read(is, progress );
    }

    IniDict::IniDict()
    {
    }

    void IniDict::read( const InputStream &is,
                        const ProgressData::ReceiverFnc & progress )
    {
      parse(is, progress );
    }

    ///////////////////////////////////////////////////////////////////
    //
    //	METHOD NAME : IniDict::~IniDict
    //	METHOD TYPE : Dtor
    //
    IniDict::~IniDict()
    {}

    void IniDict::consume( const std::string &section )
    {
      _dict[section]; // remember even empty sections
    }

    void IniDict::consume( const std::string &section, const std::string &key, const std::string &value )
    {
      _dict[section][key] = value;
    }


    IniDict::entry_const_iterator IniDict::entriesBegin(const std::string &section) const
    {
      SectionSet::const_iterator secit = _dict.find(section);
      if ( secit == _dict.end() )
      {
        return _empty_map.begin();
      }

      return (secit->second).begin();
    }

    IniDict::entry_const_iterator IniDict::entriesEnd(const std::string &section) const
    {
      SectionSet::const_iterator secit = _dict.find(section);
      if ( secit == _dict.end() )
      {
        return _empty_map.end();
      }

      return (secit->second).end();
    }

    Iterable<IniDict::entry_const_iterator> IniDict::entries(const std::string &section) const
    {
      SectionSet::const_iterator secit = _dict.find(section);
      if ( secit == _dict.end() )
      {
        return makeIterable( _empty_map.begin(), _empty_map.end() );
      }

      return makeIterable( (secit->second).begin(), (secit->second).end() );
    }

    IniDict::section_const_iterator IniDict::sectionsBegin() const
    {
      return make_map_key_begin( _dict );
    }

    IniDict::section_const_iterator IniDict::sectionsEnd() const
    {
      return make_map_key_end( _dict );
    }

    Iterable<IniDict::section_const_iterator> IniDict::sections() const
    {
      return makeIterable( sectionsBegin(), sectionsEnd() );
    }

    void IniDict::insertEntry( const std::string &section,
                               const std::string &key,
                               const std::string &value )
    {
      consume( section, key, value );
    }


    void IniDict::deleteSection( const std::string &section )
    {
      _dict.erase(section);
    }

    bool IniDict::hasSection( const std::string &section ) const
    {
      SectionSet::const_iterator secit = _dict.find(section);
      if ( secit == _dict.end() )
        return false;
      return true;
    }

    bool IniDict::hasEntry( const std::string &section,
                            const std::string &entry ) const
    {
      SectionSet::const_iterator secit = _dict.find(section);
      if ( secit == _dict.end() )
        return false;

      EntrySet::const_iterator entryit = (secit->second).find(entry);
      if ( entryit == (secit->second).end() )
        return false;

      return true;
    }

    std::ostream & IniDict::dumpAsIniOn( std::ostream & str ) const
    {
      // NOTE: The output format is used e.g in EconfDict_test.
      // If you change it you need to adapt the testcases.
      for ( const auto & section : sections() ) {
        str << "[" << section << "]" << endl;
        for ( const auto & [k,v] : entries( section ) ) {
          str << k << " = " << v << endl;
        }
      }
      return str;
    }

    std::ostream & operator<<( std::ostream & str, const IniDict & obj )
    {
      str << "<";
      const auto & sections { obj.sections() };
      if ( sections.empty() ) {
        str << "empty ini-file";
      } else {
        for ( const auto & section : sections ) {
          str << "[" << section << "](" << obj.entries( section ).size() << ")";
        }
      }
      return str << ">";
    }

    /////////////////////////////////////////////////////////////////
  } // namespace parser
  ///////////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////
} // namespace zypp
///////////////////////////////////////////////////////////////////
