#include "preprocessor.h"

#include <library/cpp/yconf/patcher/unstrict_config.h>

#include <util/stream/file.h>

TConfigPatcher::TConfigPatcher(const TString& patchPrefix)
    : PatchPrefix(patchPrefix)
{
}

TString TConfigPatcher::ReadAndProcess(const TString& filename) {
    const TString preprocessed = Preprocessor.ReadAndProcess(filename);
    TUnstrictConfig unsConf;
    if (!unsConf.ParseMemory(preprocessed.data())) {
        TString err;
        unsConf.PrintErrors(err);
        ythrow yexception() << "Cannot parse config: " << err;
    }

    for (THashMap<TString, TString>::const_iterator i = Patches.begin(); i != Patches.end(); ++i) {
        TString patchLine(i->second);
        Preprocessor.ProcessLine(patchLine);
        unsConf.PatchEntry(i->first, patchLine, PatchPrefix);
    }
    return unsConf.ToString();
}

void TConfigPatcher::ReadEnvironment(const TString& filename, bool push) {
    TUnbufferedFileInput fi(filename);
    TString line;
    while (fi.ReadLine(line)) {
        size_t pos = line.find('=');
        if (pos != TString::npos)
            SetVariable(line.substr(0, pos), line.substr(pos + 1));
    }
    if (push)
        EnvironmentFiles.push_back(filename);
}

void TConfigPatcher::ReReadEnvironment() {
    for (TVector<TString>::const_iterator i = EnvironmentFiles.begin(); i != EnvironmentFiles.end(); ++i)
        ReadEnvironment(*i, false);
}

void TConfigPatcher::SetVariable(const TString& name, const TString& value) {
    Preprocessor.SetVariable(name, value);
    Variables[name] = value;
}

void TConfigPatcher::SetUndefinedVariable(const TString& name, const TString& value) {
    if (Variables.contains(name)) {
        return;
    }
    SetVariable(name, value);
}

void TConfigPatcher::AddPatch(const TString& path, const TString& value) {
    Patches[path] = value;
}

const THashMap<TString, TString>& TConfigPatcher::GetVariables() const {
    return Variables;
}

void TConfigPatcher::SetVariables(const THashMap<TString, TString>& variables) {
    for (auto& v : variables) {
        SetVariable(v.first, v.second);
    }
}
