#include "module.h"

#include <balancer/kernel/module/module.h>
#include <balancer/kernel/custom_io/stream.h>

#include <util/generic/adaptor.h>
#include <util/generic/ptr.h>
#include <util/generic/string.h>

#include <util/system/defaults.h>

using namespace NConfig;
using namespace NSrvKernel;

MODULE(shared) {
public:
    TModule(const TModuleParams& mp)
        : TModuleBase(mp)
    {
        Config->ForEach(this);

        if (!Name_) {
            ythrow TConfigParseError() << "no name";
        }

        if (Module_) {
            Y_ENSURE(!MainSharedModules().contains(Name_));
            MainSharedModules()[Name_] = this;
            MainSharedModule_ = this;
        }
    }
private:
    THashMap<TString, TModule*>& MainSharedModules() {
        struct TModuleData
            : public IModuleData
        {
            THashMap<TString, TModule*> MainSharedModules;
        };

        THolder<IModuleData>& moduleData = Control->GetModuleData("shared");

        if (!moduleData) {
            moduleData.Reset(new TModuleData);
        }

        TModuleData* res = dynamic_cast<TModuleData*>(moduleData.Get());
        Y_VERIFY(res);

        return res->MainSharedModules;
    }

    START_PARSE {
        ON_KEY("uuid", Name_) {
            return;
        }

        {
            Y_ENSURE(!Module_);
            NSrvKernel::TModuleParams mp = Copy(value->AsSubConfig());
            mp.Modules = &SubModules_;
            Module_.Reset(Loader->MustLoad(key, mp).Release());
            return;
        }
    } END_PARSE

    void DoFinalize() override {
        if (!MainSharedModule_) {
            auto it = MainSharedModules().find(Name_);

            if (it == MainSharedModules().end()) {
                ythrow yexception() << "no such module " << Name_.Quote();
            }

            MainSharedModule_ = it->second;
        } else {
            SubModules_.Finalize();
        }
        MainSharedModule_->LastSharedModule_ = this;
    }

    void DoInit(IWorkerCtl* process) override {
        if (MainSharedModule_->LastSharedModule_ == this) {
            MainSharedModule_->SubModules_.Init(process);
        }
    }

    void DoDispose(IWorkerCtl* process) override {
        if (MainSharedModule_->LastSharedModule_ == this) {
            MainSharedModule_->SubModules_.Dispose(process);
        }
    }

    TError DoRun(const TConnDescr& descr) const noexcept override {
        return MainSharedModule_->Module_->Run(descr);
    }

    bool DoCanWorkWithoutHTTP() const noexcept override {
        return true;
    }

    TBackendCheckResult CheckBackends(IWorkerCtl& ctl, bool runtimeCheck) const noexcept override {
        return MainSharedModule_->Module_->CheckBackends(ctl, runtimeCheck);
    }

private:
    TModule* MainSharedModule_ = nullptr;
    TString Name_;
    // only for MainSharedModule
    THolder<IModule> Module_;
    TModules SubModules_;
    TModule* LastSharedModule_ = nullptr;
};

IModuleHandle* NModShared::Handle() {
    return TModule::Handle();
}
