/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 * 
 * Author: pgess <v.melnychenko@xreate.org>
 * Created on June 2, 2018, 1:08 PM
 */

 /** 
 * \file    src/aux/latereasoning.h
 * \brief   Late reasoning support
 */

#ifndef LATEREASONING_H
#define LATEREASONING_H

#include "transcendlayer.h"

namespace xreate{ namespace latereasoning{

/** \brief Represents Late Annotation, i.e. an annotation with late or runtime defined parameters*/
struct LateAnnotation{
    LateAnnotation(){}
    LateAnnotation(const Gringo::Symbol& symbolStatic);
    std::list<std::pair<std::list<Gringo::Symbol>, Gringo::Symbol>> guardedContent;
    std::list<SymbolPacked> guardKeys;
    boost::optional<Gringo::Symbol> select(const std::list<Expression>& keys, AST* root, TranscendLayer* transcend) const;
};

/** \brief Represents a group of all late annotations attached to a specific target */
struct LateAnnotationsGroup{
    std::unordered_map<Gringo::Symbol, LateAnnotation> annotations;
};

typedef std::map<Gringo::Symbol, LateAnnotation> LateModel;

/**
 * \brief Decorates \ref TranscendLayer to support late annotations processing
 * \extends TranscendLayer
 */
template<class Parent>
class LateReasoningTranscendDecorator: public Parent{
public:
    const LateAnnotationsGroup& queryLate(const std::string& alias) const{
        static LateAnnotationsGroup groupInvalid;
        if(!__modelLate.count(alias)) return groupInvalid;

        return __modelLate.at(alias);
    }
    
protected:    
    virtual bool processSolution(Gringo::Model const &model) override{
        const std::string& atomLateStatement = "late";

        for(const Gringo::Symbol& atom: model.atoms(clingo_show_type_atoms)){
            std::string atomName(atom.name().c_str());

            if(atomName == atomLateStatement){
                //late atom's format: (Target, (tuple of keys), (tuple of values), late-annotation)
                auto atomLate = TranscendLayer::parse<Gringo::Symbol, std::list<SymbolPacked>, std::list<Gringo::Symbol>, Gringo::Symbol>(atom);
                std::string atomAlias = std::get<3>(atomLate).name().c_str();
                addLateAtom(atomAlias, std::get<0>(atomLate),
                            std::get<3>(atomLate), std::get<1>(atomLate),
                            std::get<2>(atomLate));
            }
        }
        
        return Parent::processSolution(model);
    }

private:
    std::map<std::string, LateAnnotationsGroup> __modelLate;
    
    void addLateAtom(const std::string& alias, const Gringo::Symbol& annId,
                const Gringo::Symbol& content, const std::list<SymbolPacked>& guardKeys,
                const std::list<Gringo::Symbol>& guardValues){
        LateAnnotationsGroup& group = __modelLate[alias];

        LateAnnotation& annotation = group.annotations[annId];
        if (annotation.guardedContent.empty()){
            annotation.guardKeys = guardKeys;
        }
        annotation.guardedContent.push_back(std::make_pair(guardValues, content));
    }
};

}} // end of xreate::latereasoning

#endif /* LATEREASONING_H */

