/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include "StandardObject.h"

#include "IconFactory.h"
#include "Request.h"
#include "MvIconLanguage.h"
#include "IconClass.h"
#include "IconInfo.h"
#include "Dependancy.h"

#include "mars.h"

#include "Folder.h"

#include "Tokenizer.h"

//#include "Log.h"


class StandardObjectDependancy : public Dependancy
{
public:
    StandardObjectDependancy(IconObject*, const std::string&, int);
    ~StandardObjectDependancy() override;

    int n() { return n_; }
    const std::string& param() { return param_; }
    const Request& request() { return request_; }
    IconObjectH object() { return object_; }

private:
    Task* action(const Action&) override;
    void success(const Request&) override;
    void failure(const Request&) override;

    IconObjectH object_;
    std::string param_;
    int n_;
    Request request_;
    bool ok_;
};

StandardObjectDependancy::StandardObjectDependancy(IconObject* o, const std::string& param, int n) :
    object_(o),
    param_(param),
    n_(n),
    request_(""),
    ok_(false)
{
}

Task* StandardObjectDependancy::action(const Action& a)
{
    return object_->action(a);
}

void StandardObjectDependancy::success(const Request& r)
{
    ok_ = true;
    request_ = r;
}

void StandardObjectDependancy::failure(const Request& r)
{
    ok_ = false;
    request_ = r;
}

StandardObjectDependancy::~StandardObjectDependancy() = default;


StandardObject::StandardObject(Folder* parent, const IconClass& kind,
                               const std::string& name, IconInfo* info) :
    IconObject(parent, kind, name, info)
{
}

StandardObject::~StandardObject() = default;

void StandardObject::doubleClick()
{
    edit();
}

std::set<std::string> StandardObject::can()
{
    return IconObject::can();
}

void StandardObject::createFiles()
{
    IconObject::createFiles();
    Path p = path();
    if (!p.exists())
        p.saveText(className());
}

Request StandardObject::request() const
{
    Request r(path());
    r.print();
    return language().expand(r.justOneRequest());
}

void StandardObject::setRequest(const Request& r)
{
    Request req = language().expand(r, EXPAND_NO_DEFAULT | EXPAND_2ND_NAME);
    req.save(path());
}


void StandardObject::setDependancies(std::set<DependancyH>& dep, const Request& r)
{
    dep.clear();

    std::vector<std::string> icons = language().interfaces("icon");

    for (auto& icon : icons) {
        int i = 0;
        std::vector<IconObjectH> sub = subObjects(icon, r);

        for (auto& k : sub)
            dep.insert(new StandardObjectDependancy(k, icon, i++));
    }
}

const std::set<DependancyH>& StandardObject::dependancies()
{
    if (dependancies_.size())
        return dependancies_;

    dependancies_.clear();

    Request r = request();

    std::vector<std::string> icons = language().interfaces("icon");

    for (auto& icon : icons) {
        int i = 0;
        std::vector<IconObjectH> sub = subObjects(icon, r);

        for (auto& k : sub)
            dependancies_.insert(new StandardObjectDependancy(k, icon, i++));
    }

    return dependancies_;
}


Request StandardObject::fullRequest() const
{
    Request r = request();

    using Map1 = std::map<int, Request>;
    using Map2 = std::map<std::string, Map1>;

    Map2 m;

    for (const auto& dependancie : dependancies_) {
        auto* b = dynamic_cast<StandardObjectDependancy*>((Dependancy*)dependancie);
        const auto& p = b->param();
        auto n = b->n();
        const auto& r = b->request();

        m[p][n] = r;
    }

    for (auto& j : m) {
        const std::string& param = j.first;
        const Map1& reqs = j.second;

        ::request* empty = nullptr;
        MvRequest s(empty);

        for (const auto& req : reqs)
            s = s + req.second;

        r(param.c_str()) = s;
    }

    std::cout << "---> StandardObject fullrequest" << std::endl;
    r.print();
    std::cout << "<--- StandardObject fullrequest" << std::endl;

    return r;
}

bool StandardObject::rename(const std::string& newname)
{
    Request r = request();

    std::cout << "StandardObject::rename" << std::endl;
    r.print();

    std::vector<std::string> icons = language().interfaces("icon");
    std::map<std::string, std::vector<IconObjectH> > sub;

    for (auto& icon : icons)
        sub[icon] = subObjects(icon, r);

    if (!IconObject::rename(newname))
        return false;

    for (auto& icon : icons)
        setSubObjects(icon, sub[icon], r);

    r.print();
    setRequest(r);
    return true;
}

void StandardObject::duplicate()
{
    clone(parent(), info().x() + 20, info().y() + 20, false);
}

IconObject* StandardObject::clone(Folder* folder, bool update)
{
    return clone(folder, IconInfo::undefX(), IconInfo::undefY(), update);
}


IconObject* StandardObject::clone(Folder* folder, int x, int y, bool update)
{
    auto* other = dynamic_cast<StandardObject*>(IconObject::clone(folder, x, y, update));
    if (other == nullptr) {
        //-- this branch is for finding out why 'other' may be 0!
        //-- i.e. use these lines to set breakpoint in debugger!!

        std::string s("MvUI/StandardObject::clone()");
        // Log::info(s) << "internal error: nullptr!" << std::endl;

        return nullptr;  //-- probably we should not do this...
    }

    std::vector<std::string> icons = language().interfaces("icon");

    Request r = other->request();
    r.print();

    Folder* e = this->embeddedFolder(true);
    Folder* f = other->embeddedFolder(true);

    std::map<std::string, std::vector<IconObjectH> > sub;

    for (const auto& icon : icons) {
        std::vector<IconObjectH> v = this->subObjects(icon, r);
        std::vector<IconObjectH> vOther;

        for (auto& k : v) {
            if (e->ancestor(k)) {
                std::string name = relativeName(k);
                Tokenizer parse("/");
                std::vector<std::string> n;
                parse(name, n);

                std::cout << '[' << name << ']' << std::endl;

                IconObject* o = f;
                for (int i = 1; i < static_cast<int>(n.size()) && o; i++) {
                    std::cout << "SEARCH: " << n[i] << " in " << o->fullName() << std::endl;
                    o = o->find(n[i]);
                    if (o)
                        std::cout << "FOUND: " << o->fullName() << std::endl;
                    else
                        std::cout << "NOTFOUND" << std::endl;
                }

                if (o) {
                    std::cout << "CLONE: " << k->fullName() << std::endl;
                    vOther.emplace_back(o);
                    std::cout << "   IS: " << o->fullName() << std::endl;
                }
            }
            else {
                vOther.push_back(k);
            }
        }

        sub[icon] = vOther;
    }


    for (const auto& icon : icons)
        other->setSubObjects(icon, sub[icon], r);

    r.print();

    if (icons.size() > 0)
        other->setRequest(r);

    return other;
}

static IconMaker<StandardObject> maker1("Data");
static IconMaker<StandardObject> maker2("Window");
static IconMaker<StandardObject> maker3("Visdef");
static IconMaker<StandardObject> maker4("View");
static IconMaker<StandardObject> maker5("Preference");
