/*
 * (C) Copyright 1996- ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

// File Types.h
// Baudouin Raoult - ECMWF Jun 96

#ifndef eckit_Types_h
#define eckit_Types_h

#include <cstddef>
#include <utility>
#include <array>

#include "eckit/eckit.h"

#include "eckit/runtime/TaskID.h" // to be removed

namespace eckit {

//----------------------------------------------------------------------------------------------------------------------

typedef unsigned long Ordinal;  ///< for counting

typedef std::vector<Ordinal>                OrdinalList;

typedef std::vector<std::string>            StringList;
typedef std::set<std::string>               StringSet;
typedef std::map<std::string,std::string>   StringDict;

//----------------------------------------------------------------------------------------------------------------------

template<typename S, typename T>
inline std::ostream& operator<<(std::ostream& s, const std::pair<S, T>& p)
{
    s << "<" << p.first << "," << p.second << ">";
    return s;
}

//----------------------------------------------------------------------------------------------------------------------

template<class T>
class output_list {

    std::vector<T> v_;
	bool      first_;
    std::ostream&  s_;

	void flush();

public:
	void push_back(const T&);
	output_list(std::ostream&);
	~output_list();
};

template<class T>
class output_list_iterator : public output_iterator {

	output_list<T> *list_;

public:
	output_list_iterator(output_list<T>* l) : list_(l) {}
	~output_list_iterator() {}

    output_list_iterator<T>& operator=(const T& v) { list_->push_back(v); return *this; }
    output_list_iterator<T>& operator*() { return *this; }
    output_list_iterator<T>& operator++() { return *this; }
    output_list_iterator<T>& operator++(int) { return *this; }
};

//----------------------------------------------------------------------------------------------------------------------

class VectorPrintSimple{};
class VectorPrintContracted{};

template <typename T>
struct VectorPrintSelector { typedef VectorPrintContracted selector; };

template <> struct VectorPrintSelector<std::string> { typedef VectorPrintSimple selector; };
template <> struct VectorPrintSelector<double> { typedef VectorPrintSimple selector; };
template <typename K, typename V> struct VectorPrintSelector<std::pair<K,V> > { typedef VectorPrintSimple selector; };


template<class T>
inline std::ostream& __print_list(std::ostream& s, const T& t, VectorPrintContracted)
{
	output_list<typename T::value_type> l(s);
	output_list_iterator<typename T::value_type> os(&l);
    std::copy(t.begin(), t.end(), os);
    return s;
}

template <typename T>
inline std::ostream& __print_list(std::ostream& s, const std::vector<T>& t, VectorPrintSimple) {

    s << '[';
    for(Ordinal i = 0; i < t.size(); i++) {
        if (i != 0)
            s << ',';
        s << t[i];
    }
    s << ']';
    return s;
}

template <typename T, std::size_t N>
inline std::ostream& __print_list( std::ostream& s, const std::array<T, N>& t, VectorPrintSimple ) {
    s << '[';
    for ( Ordinal i = 0; i < t.size(); i++ ) {
        if ( i != 0 ) s << ',';
        s << t[i];
    }
    s << ']';
    return s;
}

template<typename K, typename V>
inline std::ostream& __print_container(std::ostream& s, const std::map<K,V>& m)
{
    const char* sep = "";
    s << "{";
    for (typename std::map<K,V>::const_iterator it = m.begin(); it != m.end(); ++it)
    {
        s << sep << it->first << "=" << it->second;
        sep = ",";
    }
    s << "}";
    return s;
}

template<typename T>
inline std::ostream& __print_container(std::ostream& s, const std::set<T>& m)
{
    const char *sep = "";
    s << "{";
    for (typename std::set<T>::const_iterator it = m.begin(); it != m.end(); ++it )
    {
        s << sep << *it;
        sep = ",";
    }
    s << "}";
    return s;
}

} // namespace eckit

//----------------------------------------------------------------------------------------------------------------------

namespace std {

    // n.b. This overload needs to go either in the namespace std:: (which contains
    //      ostream, vector), the global namespace, or the namespace containing T.
    //      Otherwise it will not be found when doing lookups.
    //
    //  --> Probably best to put it in std::. It is acceptable to add "template
    //      specializations for any standard library template" if the "declaration
    //      depends on user-defined types".

    template<class T>
    inline std::ostream& operator<<(std::ostream& s,const std::vector<T>& v)
    {
        return eckit::__print_list(s, v, typename eckit::VectorPrintSelector<T>::selector());
    }

    template <class T, std::size_t N>
    inline std::ostream& operator<<( std::ostream& s, const std::array<T, N>& v )
    {
        return eckit::__print_list( s, v, typename eckit::VectorPrintSelector<T>::selector() );
    }

    template<typename K, typename V>
    inline std::ostream& operator<<(std::ostream& s, const std::map<K,V>& m)
    {
        return eckit::__print_container(s,m);
    }

    template< typename T >
    inline std::ostream& operator<<(std::ostream& s, const std::set<T>& m)
    {
        return eckit::__print_container(s,m);
    }
}

//----------------------------------------------------------------------------------------------------------------------

namespace eckit {

//----------------------------------------------------------------------------------------------------------------------

class Stream; // forward

/// Operators to send vectors in streams
template<class T> Stream& operator<<(Stream&, const std::vector<T>&);
template<class T> Stream& operator>>(Stream&, std::vector<T>&);

/// Operators to send sets in streams
template<class T> Stream& operator<<(Stream&, const std::vector<T>&);
template<class T> Stream& operator>>(Stream&, std::vector<T>&);

/// Operators to send maps in streams
/// Note: the value type V must have a constructor from Stream&
template<class K, class V> Stream& operator<<(Stream&, const std::map<K,V>&);
template<class K, class V> Stream& operator>>(Stream&, std::map<K,V>&);

//----------------------------------------------------------------------------------------------------------------------

} // namespace eckit

#include "eckit/types/Types.cc"

#endif
