/*
 * 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/.
 * 
 * File:   polymorphcompiler.h
 * Author: pgess <v.melnychenko@xreate.org>
 *
 * Created on October 7, 2017
 */

#ifndef POLYMORPHCOMPILER_H
#define POLYMORPHCOMPILER_H

#include "pass/compilepass.h"
#include "query/polymorph.h"

namespace xreate { namespace polymorph {

typedef Expression Guard;    
    
template <class Parent>
class PolymorphCodeScopeUnit: public Parent{
public:
    PolymorphCodeScopeUnit(const CodeScope* const codeScope, compilation::IFunctionUnit* f, CompilePass* compilePass)
            : Parent(codeScope, f, compilePass) {}

protected:
    compilation::ICallStatement* findFunction(const Expression& opCall) override {
        
        //Check does invocation require guards
        const std::string& nameCallee = opCall.getValueString();
        const std::list<ManagedFnPtr>& specializations = Parent::pass->man->root->getFunctionSpecializations(nameCallee);
        
        //Extern function 
        if (specializations.size() == 0){
            return Parent::findFunction(opCall);
        }
        
        //No other specializations. Check if it has no guard
        if (specializations.size() == 1){
            if (!specializations.front()->guard.isValid()) {
                return Parent::findFunction(opCall);
            }
        }
        
        //TODO There are overlapping guards and contexts. For now, skip if context found. 
        if (specializations.front()->guardContext.isValid()){
            return Parent::findFunction(opCall);
        }
        
        //Several specializations
        assert(Attachments::exists<PolymorphGuard>(opCall) && "Guard required");
        const Expression& guardSelected = Attachments::get<PolymorphGuard>(opCall);
        std::map<Guard, ManagedFnPtr> indexSpecs;
        for(ManagedFnPtr specialization: specializations){
            indexSpecs.emplace(specialization->guard, specialization);
        }
        
        assert(indexSpecs.count(guardSelected) && "Can't found appropriate guard");
        return new compilation::CallStatementRaw(Parent::pass->getFunctionUnit(indexSpecs.at(guardSelected))->compile(), Parent::pass->man->llvm);
    }
};

} } //end of xreate::polymorph

#endif /* POLYMORPHCOMPILER_H */

