#include "mem_watcher.h"
#include "server.h"
#include <util/system/mem_info.h>

namespace NRTYDeployServer {
    const TString TWatchDog::LockName("restarted_deploy_manager");

    TWatchDog::TWatchDog(THttpDeployManager& owner)
        : Owner(owner)
        , Stopped(TSystemEvent::rManual)
    {}

    void TWatchDog::DoExecute() {
        UnlockRestart();
        while (!Stopped.WaitT(TDuration::Seconds(5))) {
            NMemInfo::TMemInfo mi = NMemInfo::GetMemInfo();
            DEBUG_LOG << "Check memory usage: vms=" << mi.VMS << ", rss=" << mi.RSS << Endl;
            if (mi.RSS > Owner.GetConfig().GetWatchDog().MaxMemoryCount) {
                ERROR_LOG << "To many memory used: " << mi.RSS << ", must be <= " << Owner.GetConfig().GetWatchDog().MaxMemoryCount << Endl;
                LockRestart();
                Owner.Stop();
                FAIL_LOG("To many memory used: %li", mi.RSS);
            }
        }
    }

    void TWatchDog::Start() {
        TGuard<TMutex> g(Mutex);
        if (!!Thread) {
            Stopped.Signal();
            Thread->Join();
        }
        Stopped.Reset();
        if (Owner.GetConfig().GetWatchDog().Enabled)
            Thread = SystemThreadFactory()->Run(this);
    }

    inline TString TWatchDog::GetMyName() const {
        return HostName() + ":" + ToString(Owner.GetConfig().GetHttpOptionsConfig().Port);
    }

    void TWatchDog::UnlockRestart() {
        NRTYDeploy::TAbstractLock::TPtr lock = Owner.GetStorage().WriteLockNode(LockName);
        TString locked;
        const TString& mustLock = GetMyName();
        if (!Owner.GetStorage().GetValue(LockName, locked, -1, false) || locked != mustLock)
            return;
        Owner.GetStorage().RemoveNode(LockName);
    }

    void TWatchDog::LockRestart() {
        while (true) {
            DEBUG_LOG << "Try to lock " << LockName << " ..." << Endl;
            NRTYDeploy::TAbstractLock::TPtr lock = Owner.GetStorage().WriteLockNode(LockName);
            TString locked;
            const TString& mustLock = GetMyName();
            if (!Owner.GetStorage().GetValue(LockName, locked, -1, false) || locked == mustLock)
                if (Owner.GetStorage().SetValue(LockName, mustLock, false, false)) {
                    DEBUG_LOG << "Try to lock " << LockName << " ...OK" << Endl;
                    return;
                }
            DEBUG_LOG << LockName << " is locked by " << locked << ", wait" << Endl;
            Sleep(TDuration::Seconds(1));
        }
    }

}
