#ifndef CONTAINER_FOR_VECTOR
#define CONTAINER_FOR_VECTOR

#include <algorithm>
#include <functional>
#include <stdexcept>
#include <cmath>

#include "StringTools.hh"

#include "ContainerBaseTemplate.hh"

template<typename _SCALER_TYPE >
struct ValueSizeComp {
    Bool operator() (std::pair<const string, _SCALER_TYPE >& item1, std::pair<const string, _SCALER_TYPE >& item2) {
        return item1.second.size() < item2.second.size();
    };
};

/** Container For STL-Vector.
 *
 *  @see ContainerForScaler for add, replace, erase, empty, size, containe
 *
 *  @author TANIMORI Souichirou, AdvanceSoft Corp.
 *  @version 0.0
 *  @since   0.0
 */
template<typename _ELEMENT_TYPE>
class AdvContainerForVector : virtual public ContainerForScaler< vector< _ELEMENT_TYPE > > {
    public:
        static const string className; // = string("");

    public:
        /** constructor */
        AdvContainerForVector< _ELEMENT_TYPE >() { this->clear(); };

        /** constructor */
        ~AdvContainerForVector< _ELEMENT_TYPE >() { this->clear(); };

        /** get the size of the specified vector with the key.
         */
        UInt4 getVectorSize(const string& key) ;

        /** get the i-th. element of a vecttor.
         *  @param[in] key  the key for a vector
         *  @param[in] i    the index for the element
         */
        _ELEMENT_TYPE get(const string& key, const UInt4 i) throw(invalid_argument, out_of_range) ;

        /** replace the value with the specified key to new value.
         *  @param[in] key    the key for a value to be repalced
         *  @param[in] i      the index for the element of the value
         *  @param[in] value  the new value to be reparaced into
         */
        void replace(const string& key, const UInt4 i, const _ELEMENT_TYPE& value);

        /** output entry to the standard output.
         *  @param[in] indent
         */
        void dump(const UInt4 indent=0, const UInt4 indentDepth=4, const string& keyTitile="key", const string& sizeTitle="size", const string& valueTitle="value");
};

template<typename _ELEMENT_TYPE >
UInt4 AdvContainerForVector< _ELEMENT_TYPE >::getVectorSize(const string& key) {
    UInt4 retval=0;
    if (! this->contain(key)) {
        std::cerr<< "Warning: AdvContainerForVector::getVectorSize: " << __FILE__ << ":" << __LINE__ << ": not found the value with key \"" << key << "\"" << endl;
    } else {
        retval = this->cmap[key].size() ;
    }
    return retval;
};

template<typename _ELEMENT_TYPE >
_ELEMENT_TYPE AdvContainerForVector< _ELEMENT_TYPE >::get(const string& key, const UInt4 i) throw(invalid_argument, out_of_range) {
    if (! this->contain(key) ) {
        std::cerr << "Error: AdvContainerForVector::get: " << __FILE__ << ":" << __LINE__ << ": not found the value with key=\"" << key << "\"" << endl;
        throw invalid_argument(string("AdvContainerForVector::get: not found the entry with the specified key \"" + key + "\"\n"));
    }
    if ( i >= this->cmap.size() ) {
        StringTools tools;
        std::cerr << "Error: AdvContainerForVector::get: " << __FILE__ << ":" << __LINE__ << ": index is out of range: key=\"" << key << "\", index=" << i << endl;
        throw out_of_range(string("AdvContainerForVector::get: the index is out of range.  i=\"") + tools.UInt4ToString(i) + string("\"\n"));
    }
    return this->cmap[key].at(i);
};

template<typename _ELEMENT_TYPE >
void AdvContainerForVector< _ELEMENT_TYPE >::replace(const string& key, const UInt4 i, const _ELEMENT_TYPE& value) {
    if (! this->contain(key) ) {
        std::cerr << "Warning: AdvContainerForVector::get: " << __FILE__ << ":" << __LINE__ << ": not found the value with key=\"" << key << "\"" << endl;
    } else if ( i >= this->cmap.size() ) {
        std::cerr << "Warning: AdvContainerForVector::get: " << __FILE__ << ":" << __LINE__ << ": index is out of range: key=\"" << key << "\", index=" << i << endl;
    } else {
        this->cmap[key].at(i)=value;
    }
};

template< typename _ELEMENT_TYPE >
void AdvContainerForVector< _ELEMENT_TYPE >::dump(const UInt4 indent, const UInt4 indentDepth, const string& keyTitle, const string& sizeTitle, const string& valueTitle) {

    if (! this->cmap.empty() ) {
        size_t maxKeyWidth  = 0;
        maxKeyWidth = max(keyTitle.size(), max_element(this->cmap.begin(), this->cmap.end(), KeyLengthComp< vector< _ELEMENT_TYPE > >())->first.size());

        size_t maxSizeWidth = 0;
        maxSizeWidth = max_element(this->cmap.begin(), this->cmap.end(), ValueSizeComp< vector<_ELEMENT_TYPE> >())->second.size();
        maxSizeWidth = max(sizeTitle.size(), maxSizeWidth > 0 ? static_cast<size_t>(floor(log10(static_cast<Double>(maxSizeWidth)))+1) : 1);

        OutputTypeTitle ott;
        ott.title(indentDepth*indent, typeid(vector< _ELEMENT_TYPE >) );
        ott.header(indentDepth*(indent+1), maxKeyWidth, keyTitle, maxSizeWidth, sizeTitle, valueTitle);

        for_each(this->cmap.begin(), this->cmap.end(), OutputVectorEntry< _ELEMENT_TYPE >(indentDepth*(indent+1), maxKeyWidth, maxSizeWidth));
    }
};
 
#endif // CONTAINER_FOR_VECTOR
