/*
 * (C) Copyright 1996-2016 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 TitleTemplate.h
    \brief Definition of the Template class TitleTemplate.

    Magics Team - ECMWF 2004

    Started: Mon 21-Jun-2004

    Changes:

*/

#ifndef TitleTemplate_H
#define TitleTemplate_H

#include "AutoVector.h"
#include "Data.h"
#include "Factory.h"
#include "TitleField.h"
#include "TitleTemplateAttributes.h"
#include "magics.h"

#include <stack>

namespace magics {
class GribDecoder;


class SimpleStringFormat {
public:
    SimpleStringFormat(const string& value, const string& format) : format_(format), value_(value) {}

protected:
    string format_;
    string value_;

    void operator()(ostream& out) const {
        string dup            = format_;
        string::size_type pos = dup.find("%s", 0);

        if (pos != string::npos) {
            dup.replace(pos, 2, value_);
        }
        out << dup;
    }

    friend ostream& operator<<(ostream& out, const SimpleStringFormat& format) {
        format(out);
        return out;
    }
};


class TitleFieldHandler {
public:
    TitleFieldHandler() {}
    virtual ~TitleFieldHandler(){};
    virtual void operator()(TitleField& field, vector<string>& lines, const GribDecoder&) { ASSERT(false); }
};


class TitleTemplate : public AutoVector<TitleTemplate>,
                      public TitleTemplateAttributes,
                      public std::stack<TitleTemplate*> {
public:
    TitleTemplate(map<string, string> criteria) : criteria_(criteria) {}
    TitleTemplate();
    virtual ~TitleTemplate() override;

    void add(TitleField* field) { template_.push_back(field); }

    template <class D>
    static void title(vector<string>& out, const D& data) {
        if (!singleton_)
            new TitleTemplate();
        (*singleton_)(out, data);
    }

    static void release() {
        if (singleton_) {
            delete singleton_;
            singleton_ = 0;
        }
    }

    template <class D>
    void operator()(vector<string>& out, const D& data) const {
        vector<string> lines;
        lines.push_back("");
        for (const_iterator child = begin(); child != end(); ++child) {
            if ((*child)->verify(data)) {
                (*(*child))(lines, data);
                for (vector<string>::iterator line = lines.begin(); line != lines.end(); ++line)
                    if (!line->empty())
                        out.push_back(*line);
                return;
            }
        }

        for (auto& entry : template_) {
            try {
                unique_ptr<TitleFieldHandler> object(SimpleObjectMaker<TitleFieldHandler>::create(entry->name()));
                (*object)(*entry, lines, data);
            }
            catch (NoFactoryException& e) {
                // The data do not know how to verify the criter ....
                MagLog::debug() << "Cannot Create the TitleFieldHandler for " << entry->name() << "\n";
                (*entry)(lines);
            }
        }

        for (vector<string>::iterator line = lines.begin(); line != lines.end(); ++line)
            if (!line->empty())
                out.push_back(*line);
    }


    bool verify(const GribDecoder& data) const;

    map<string, string>& criteria() { return criteria_; }

protected:
    //! Method to print string about this class on to a stream of type ostream (virtual).
    virtual void print(ostream&) const override;
    map<string, string> criteria_;
    AutoVector<TitleField> template_;
    static TitleTemplate* singleton_;
    void decode();

private:
    //! Copy constructor - No copy allowed
    TitleTemplate(const TitleTemplate&);
    //! Overloaded << operator to copy - No copy allowed
    TitleTemplate& operator=(const TitleTemplate&);

    // -- Friends
    //! Overloaded << operator to call print().
    friend ostream& operator<<(ostream& s, const TitleTemplate& p) {
        p.print(s);
        return s;
    }
};

}  // namespace magics
#endif
