text:
//============================================================================
// Copyright 2009-2020 ECMWF.
// This software is licensed under the terms of the Apache Licence version 2.0
// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
// In applying this licence, ECMWF does not waive the privileges and immunities
// granted to it by virtue of its status as an intergovernmental organisation
// nor does it submit to any jurisdiction.
//============================================================================

#include "ServerHandler.hpp"

#include "Defs.hpp"
#include "ClientInvoker.hpp"
#include "File.hpp"
#include "NodeFwd.hpp"
#include "ArgvCreator.hpp"
#include "Str.hpp"

#include "ChangeNotify.hpp"
#include "ConnectState.hpp"
#include "DirectoryHandler.hpp"
#include "MainWindow.hpp"
#include "NodeObserver.hpp"
#include "SessionHandler.hpp"
#include "ServerComQueue.hpp"
#include "ServerDefsAccess.hpp"
#include "ServerObserver.hpp"
#include "ServerComObserver.hpp"
#include "ShellCommand.hpp"
#include "SuiteFilter.hpp"
#include "UiLogS.hpp"
#include "UIDebug.hpp"
#include "UpdateTimer.hpp"
#include "UserMessage.hpp"
#include "VNode.hpp"
#include "VSettings.hpp"
#include "VTaskObserver.hpp"

#include <QDateTime>
#include <QDebug>
#include <QMessageBox>
#include <QMetaType>

#include <iostream>
#include <algorithm>

#include <boost/algorithm/string/predicate.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/asio/ip/host_name.hpp>

std::vector<ServerHandler*> ServerHandler::servers_;
std::string ServerHandler::localHostName_;

//#define __UI_SERVEROBSERVER_DEBUG
//#define __UI_SERVERCOMOBSERVER_DEBUG
#define __UI_SERVERUPDATE_DEBUG

ServerHandler::ServerHandler(const std::string& name,const std::string& host, const std::string& port,
                             const std::string& user,bool ssl) :
   name_(name),
   host_(host),
   port_(port),
   user_(user),
   ssl_(ssl),
   client_(nullptr),
   updating_(false),
   communicating_(false),
   vRoot_(nullptr),
   suiteFilter_(new SuiteFilter()),
   comQueue_(nullptr),
   activity_(NoActivity),
   compatibility_(CanBeCompatible), //not yet known!
   connectState_(new ConnectState()),
   prevServerState_(SState::RUNNING),
   conf_(nullptr)
{
    if(localHostName_.empty())
	{
		localHostName_=boost::asio::ip::host_name();
	}

	//Create longname
	longName_=host_ + "@" + port_;
    fullLongName_ = name_ + "[" + longName_ + "]";

	conf_=new VServerSettings(this);

    //Connect up the timer for refreshing the server info. The timer has not
    //started yet.
    refreshTimer_=new UpdateTimer(this);
    connect(refreshTimer_, SIGNAL(timeout()),
            this, SLOT(refreshServerInfo()));

    //We will need to pass various non-Qt types via signals and slots for error messages.
    //So we need to register these types.
    if(servers_.empty())
    {
        qRegisterMetaType<std::string>("std::string");
        qRegisterMetaType<QList<ecf::Aspect::Type> >("QList<ecf::Aspect::Type>");
        qRegisterMetaType<std::vector<ecf::Aspect::Type> >("std::vector<ecf::Aspect::Type>");
    }

    //Add this instance to the servers_ list.
    servers_.push_back(this);

    createClient(true);
}

// the real deletion is performed by logout()/queueLoggedOut()
ServerHandler::~ServerHandler()
{
    Q_ASSERT(comQueue_ == nullptr);
    Q_ASSERT(client_ == nullptr);
}

void ServerHandler::logoutAndDelete()
{
    activity_ = DeleteActivity;

    stopRefreshTimer();

    //Save settings
    saveConf();

    //Notify the observers
    broadcast(&ServerObserver::notifyServerDelete);

    //The queue must be deleted before the client, since the thread might
    //be running a job on the client!!
    bool queueLoggedOut = true;
    if (comQueue_) {
        queueLoggedOut = comQueue_->logout();
    }

    //ComQueue will delete itself - it will delete the client and the ComThread as well
    comQueue_ = nullptr;
    client_ = nullptr;

    //Remove itself from the server vector
    auto it=std::find(servers_.begin(),servers_.end(),this);
    if(it != servers_.end())
        servers_.erase(it);

    delete vRoot_;
    delete connectState_;
    delete suiteFilter_;
    delete conf_;

    if(queueLoggedOut) {
        deleteLater();
    }
}

void ServerHandler::queueLoggedOut()
{
    //at this point both the queue and the thread are being deleted
    assert(client_ == nullptr);
    assert(comQueue_ == nullptr);
    assert(activity_ == DeleteActivity || activity_ == ClientRecreateActivity);
    if (activity_ == DeleteActivity) {
        deleteLater();
    } else if(activity_ == ClientRecreateActivity) {
        createClient(false);
    }
}

// called from the constructor with init=true
// must be called from/after recreateClient() with init=false
void ServerHandler::createClient(bool init)
{
    assert(client_ == nullptr);
    assert(comQueue_ == nullptr);

    //Create the client invoker. At this point it is empty.
    try
    {
        // True passed in to avoid reading SSL from the environment
        client_=new ClientInvoker(true/*gui*/,host_, port_);
    }
    catch(std::exception& e)
    {
        std::string errMsg = "Could not create ClientInvoker for host=" +
                              host_ + " port=" + port_ + " !";

        if(e.what())
            errMsg += " " +  std::string(e.what());

        UiLog().err() << errMsg;
        client_=nullptr;
        failedClientServer(errMsg);
    }

    bool ssl_enabled = false;
    std::string ssl_error;

    if(client_) {
#ifdef ECF_OPENSSL
        if (ssl_) {
            try
            {
                client_->enable_ssl();
                ssl_enabled =true;
            }
            catch(std::exception& e)
            {
                ssl_error = std::string(e.what());
            }
            // this works when there is a problem with the certificate.
            // The server can be still SSL-compatible if a certificate is there.
            // We will detect this situation dutin the initial load!!!
        } else {
            client_->disable_ssl();
        }
#endif

        if (!init || !user_.empty()) {
            try
            {
                client_->set_user_name(user_);
            }
            catch(std::exception& e)
            {
                UiLog().err() << std::string(e.what());
            }
        }

        client_->set_auto_sync(true); //will call sync_local() after each command!!!
        client_->set_retry_connection_period(1);
        client_->set_connection_attempts(1);
        client_->set_throw_on_error(true);
    }

    //Create the vnode root. This will represent the node tree in the viewer, but
    //at this point it is empty.
    if (init) {
        Q_ASSERT(vRoot_ == nullptr);
        vRoot_=new VServer(this);
    }

    // NOTE: we may not always want to create a thread here because of resource
    // issues; another strategy would be to create threads on demand, only
    // when server communication is about to start.

    //Create the queue for the tasks to be sent to the client (via the ServerComThread)! It will
    //create and take ownership of the ServerComThread. At this point the queue has not started yet.
    comQueue_=new ServerComQueue (this,client_);

    if (client_) {
        //Load settings
        if (init) {
            loadConf();
        }

        // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        // At this point nothing is running or active!!!!

        // we need to set it because in disconnected state reset is ignored!!!
        if (!init) {
            connectState_->state(ConnectState::Undef);
        }

#ifdef ECF_OPENSSL
        if(ssl_ && !ssl_enabled) {
            sslCertificateError(ssl_error);
            return;
        }
#endif

        //Check if the server is compatible with the client. If it is fine
        //reset() will be called to connect to the server and load the defs etc.
        //This is a safety check to avoid communicating with incompatible
        //servers!!!!
        checkServerVersion();
    }
}

void ServerHandler::recreateClient()
{
    activity_= ClientRecreateActivity;

    stopRefreshTimer();

    if(client_) {
        //Save settings
        saveConf();

        //We clear and stop everything before we delete the objects!!
        clearTree();

        //Create an object to inform the observers about the change
        VServerChange change;
        //Notify the observers that the scan has started
        broadcast(&ServerObserver::notifyBeginServerScan,change);
        //Notify the observers that scan has ended
        broadcast(&ServerObserver::notifyEndServerScan);

        connectState_->state(ConnectState::Disconnected);
        connectState_->errorMessage("");
        //setActivity(NoActivity);

        broadcast(&ServerObserver::notifyServerConnectState);
    }

    //The queue must be deleted before the client, since the thread might
    //be running a job on the client!!
    bool queueLoggedOut = true;
    if (comQueue_) {
        queueLoggedOut = comQueue_->logout();
    }

    //ComQueue will delete itself - it will delete the client and the ComThread as well
    comQueue_ = nullptr;
    client_ = nullptr;

    if (queueLoggedOut)
        createClient(false);
}

void ServerHandler::setSsl(bool ssl)
{
    if (ssl != ssl_) {
        ssl_ = ssl;

        if (connectState_->state() != ConnectState::VersionIncompatible &&
            connectState_->state() != ConnectState::FailedClient) {
            recreateClient();
        }
    }
}

void ServerHandler::setUser(const std::string& user)
{
    if (user != user_) {
        user_ = user;

        if (client_ && connectState_->state() != ConnectState::VersionIncompatible &&
            connectState_->state() != ConnectState::FailedClient) {
            recreateClient();
        }
    }
}

bool ServerHandler::isDisabled() const
{
    return (connectState_->state() == ConnectState::Disconnected ||
       connectState_->state() == ConnectState::VersionIncompatible ||
       connectState_->state() == ConnectState::SslIncompatible ||
       connectState_->state() == ConnectState::SslCertificateError ||
       connectState_->state() == ConnectState::SslCertificateError ||
       compatibility_ == Incompatible);
}

bool ServerHandler::isInLogout() const
{
    return (activity() == DeleteActivity || activity() == ClientRecreateActivity);
}

//-----------------------------------------------
// Refresh/update
//-----------------------------------------------

bool ServerHandler::updateInfo(int& basePeriod,int& currentPeriod,int &drift,int& toNext)
{
    if(!refreshTimer_->isActive())
        return false;

    toNext=secsTillNextRefresh();
    basePeriod=conf_->intValue(VServerSettings::UpdateRate);
    currentPeriod=refreshTimer_->interval()/1000;
    drift=-1;
    if(conf_->boolValue(VServerSettings::AdaptiveUpdate))
    {
        drift=conf_->intValue(VServerSettings::AdaptiveUpdateIncrement);
    }

    return true;
}

int ServerHandler::currentRefreshPeriod() const
{
    if(!refreshTimer_->isActive())
        return -1;

    return refreshTimer_->interval()/1000;
}

int ServerHandler::secsSinceLastRefresh() const
{
    return static_cast<int>(lastRefresh_.secsTo(QDateTime::currentDateTime()));
}

int ServerHandler::secsTillNextRefresh() const
{
     if(refreshTimer_->isActive())
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
         return refreshTimer_->remainingTime()/1000;
#else
         return 0;
#endif
     return -1;
}

void ServerHandler::stopRefreshTimer()
{
    refreshTimer_->stop();
#ifdef __UI_SERVERUPDATE_DEBUG
    UiLogS(this).dbg() << "ServerHandler::stopRefreshTimer -->";
#endif
    broadcast(&ServerComObserver::notifyRefreshTimerStopped);
}

void ServerHandler::startRefreshTimer()
{
    UiLogS(this).dbg() << "ServerHandler::startRefreshTimer -->";

    if(!conf_->boolValue(VServerSettings::AutoUpdate))
    {
        return;
    }

    //If we are not connected to the server the
	//timer should not run.
    if(isDisabled())
		return;

    if(!refreshTimer_->isActive())
	{
        int rate=conf_->intValue(VServerSettings::UpdateRate);
        if(rate <=0) rate=1;
        refreshTimer_->setInterval(rate*1000);
        refreshTimer_->start();
        broadcast(&ServerComObserver::notifyRefreshTimerStarted);
	}

#ifdef __UI_SERVERUPDATE_DEBUG
    UiLogS(this).dbg() << " refreshTimer interval: " <<  refreshTimer_->interval();
#endif
}

void ServerHandler::updateRefreshTimer()
{
    UiLogS(this).dbg() << "ServerHandler::updateRefreshTimer -->";

    if (isInLogout()) {
        stopRefreshTimer();
        return;
    }

    if(!conf_->boolValue(VServerSettings::AutoUpdate))
    {
        stopRefreshTimer();
        return;
    }

    //If we are not connected to the server the
	//timer should not run.
    if(isDisabled())
		return;

    int rate=conf_->intValue(VServerSettings::UpdateRate);
    if(rate <=0) rate=1;

    if(refreshTimer_->isActive())
	{
        refreshTimer_->stop();
    }

    refreshTimer_->setInterval(rate*1000);
    refreshTimer_->start();
    broadcast(&ServerComObserver::notifyRefreshTimerChanged);

#ifdef __UI_SERVERUPDATE_DEBUG
    UiLogS(this).dbg() << " refreshTimer interval: " << refreshTimer_->interval();
#endif

}

void ServerHandler::driftRefreshTimer()
{
    if (isInLogout()) {
        stopRefreshTimer();
        return;
    }

    if(!conf_->boolValue(VServerSettings::AutoUpdate))
    {
        return;
    }

    //We increase the update frequency
    if(activity_ != LoadActivity &&
       conf_->boolValue(VServerSettings::AdaptiveUpdate))
    {
#ifdef __UI_SERVERUPDATE_DEBUG
        UiLogS(this).dbg() << "driftRefreshTimer -->";
#endif

        int rate=conf_->intValue(VServerSettings::UpdateRate);
        int baseDelta=conf_->intValue(VServerSettings::AdaptiveUpdateIncrement);
        int delta=baseDelta;  //linear mode

        //doubling mode
        if(conf_->stringValue(VServerSettings::AdaptiveUpdateMode) == "doubling")
        {
            delta=refreshTimer_->interval()/1000-rate;
            if(delta==0)
                delta=baseDelta;
        }
        //x modes
        else
        {
            float f=conf_->stringValue(VServerSettings::AdaptiveUpdateMode).toFloat();
            if(f >= 1. && f <=5.)
            {
                delta=(refreshTimer_->interval()/1000-rate)*(f-1);
                if(delta==0)
                    delta=1;
            }
        }

        refreshTimer_->drift(delta,conf_->intValue(VServerSettings::MaxAdaptiveUpdateRate));

        broadcast(&ServerComObserver::notifyRefreshTimerChanged);
    }

#ifdef __UI_SERVERUPDATE_DEBUG
    UiLogS(this).dbg() << "driftRefreshTimer interval: " << refreshTimer_->interval();
#endif

}

//returns true if the current total (drifted) period is within the maximum allowed
bool ServerHandler::checkRefreshTimerDrift() const
{
    if (isInLogout()) {
        return true;
    }

    if(!conf_->boolValue(VServerSettings::AutoUpdate))
    {
        return true;
    }

    if(activity_ != LoadActivity &&
       conf_->boolValue(VServerSettings::AdaptiveUpdate))
    {
        return (refreshTimer_->interval()*1000 <
                conf_->intValue(VServerSettings::MaxAdaptiveUpdateRate)*60);
    }
    return true;
}

//mark that a refresh request was sent to the queue
void ServerHandler::refreshScheduled()
{
    lastRefresh_=QDateTime::currentDateTime();
    broadcast(&ServerComObserver::notifyRefreshScheduled);
}

//mark that a refresh request was sent to the queue
void ServerHandler::refreshFinished()
{
    broadcast(&ServerComObserver::notifyRefreshFinished);
}

void ServerHandler::setActivity(Activity ac)
{
	activity_=ac;
	broadcast(&ServerObserver::notifyServerActivityChanged);
}

ServerHandler* ServerHandler::addServer(const std::string& name,const std::string& host, const std::string& port,
                                        const std::string& user, bool ssl)
{
    auto* sh=new ServerHandler(name,host,port,user,ssl);
    return sh;
}

void ServerHandler::removeServer(ServerHandler* server)
{
	auto it=std::find(servers_.begin(), servers_.end(),server);
	if(it != servers_.end())
	{
		ServerHandler *s=*it;
		servers_.erase(it);
        s->logoutAndDelete();
	}
}

ServerHandler* ServerHandler::findServer(const std::string &alias)
{
	for(std::vector<ServerHandler*>::const_iterator it=servers_.begin(); it != servers_.end(); ++it)
	{
		ServerHandler *s=*it;

		if (s->name() == alias)
		{
			return s;
		}
	}
	return nullptr; // did not find it
}

//This function can be called many times so we need to avoid locking the mutex.
SState::State ServerHandler::serverState()
{
	if(connectState_->state() != ConnectState::Normal || activity() == LoadActivity)
	{
		prevServerState_= SState::RUNNING;

	}
	//If we are here we can be sure that the defs pointer is not being deleted!! We
	//access it without locking the mutex!!!
	else
	{
		//If get the defs can be 100% sure that it is not being deleted!! So we can
		//access it without locking the mutex!!!
		defs_ptr d=safelyAccessSimpleDefsMembers();
		if(d && d.get())
		{
			prevServerState_= d->set_server().get_state();
			return prevServerState_;
		}
	}

	return prevServerState_;
}

//This function can be called many times so we need to avoid locking the mutex.
NState::State ServerHandler::state(bool& suspended)
{
	if(connectState_->state() != ConnectState::Normal || activity() == LoadActivity)
		return NState::UNKNOWN;

	suspended=false;

	defs_ptr d=safelyAccessSimpleDefsMembers();
	if(d && d.get())
	{
		suspended=d->isSuspended();
		return d->state();
	}

	return NState::UNKNOWN;
}

defs_ptr ServerHandler::defs()
{
	defs_ptr null;

	if(client_)
	{
		return client_->defs();
	}
	else
	{
		return null;
	}
}

defs_ptr ServerHandler::safelyAccessSimpleDefsMembers()
{
	defs_ptr null;

	//The defs might be deleted during reset so it cannot be accessed.
	if(activity_ == LoadActivity)
	{
		return null;
	}
	//Otherwise it is safe to access certain non-vector members
	else if(client_)
	{
		return client_->defs();
	}
	else
	{
		return null;
	}
}

//-------------------------------------------------------------
// Run client tasks.
//
// The preferred way to run client tasks is to define and add a task to the queue. The
// queue will manage the task and will send it to the ClientInvoker. When the task
// finishes the ServerHandler::clientTaskFinished method is called where the
// result/reply can be processed.
//--------------------------------------------------------------

//It is protected! Practically it means we
//we can only run it through CommandHandler!!!
void ServerHandler::runCommand(const std::vector<std::string>& cmd)
{
    if(isDisabled())
        return;

    //We do not know if the server is compatible
    if(compatibility_ ==  CanBeCompatible)
    {
        //aync - will call reset if successfull
        checkServerVersion();
        return;
    }

    //Shell command - we should not reach this point
    if(cmd.size() > 0 && cmd[0]=="sh")
    {
        UI_ASSERT(0,"cmd=" << cmd[0]);
        return;
    }

	VTask_ptr task=VTask::create(VTask::CommandTask);
	task->command(cmd);
	comQueue_->addTask(task);
}

void ServerHandler::run(VTask_ptr task)
{
    if(isDisabled())
		return;

    //We do not know if the server is compatible
    if(compatibility_ ==  CanBeCompatible)
    {
        //aync - will call reset if successfull
        checkServerVersion();
        return;
    }

	switch(task->type())
	{
	case VTask::ScriptTask:
		return script(task);
		break;
	case VTask::JobTask:
		return job(task);
		break;
	case VTask::OutputTask:
		return jobout(task);
		break;
	case VTask::ManualTask:
		return manual(task);
        break;
    case VTask::JobStatusFileTask:
        return jobstatus(task);
        break;
	case VTask::HistoryTask:
	case VTask::MessageTask:
	case VTask::StatsTask:
	case VTask::ScriptPreprocTask:
	case VTask::ScriptEditTask:
	case VTask::ScriptSubmitTask:
	case VTask::SuiteListTask:
    case VTask::WhySyncTask:
    case VTask::ZombieListTask:
    case VTask::ZombieCommandTask:
    case VTask::JobStatusTask:
    case VTask::PlugTask:
		comQueue_->addTask(task);
		break;
	default:
		//If we are here we have an unhandled task type.
		task->status(VTask::REJECTED);
		break;
	}
}

void ServerHandler::script(VTask_ptr task)
{
	/*static std::string errText="no script!\n"
		      		"check ECF_FILES or ECF_HOME directories, for read access\n"
		      		"check for file presence and read access below files directory\n"
		      		"or this may be a 'dummy' task.\n";*/

	task->param("clientPar","script");
	comQueue_->addTask(task);
}

void ServerHandler::job(VTask_ptr task)
{
	/*static std::string errText="no script!\n"
		      		"check ECF_FILES or ECF_HOME directories, for read access\n"
		      		"check for file presence and read access below files directory\n"
		      		"or this may be a 'dummy' task.\n";*/

	task->param("clientPar","job");
	comQueue_->addTask(task);
}

void ServerHandler::jobout(VTask_ptr task)
{
	//static std::string errText="no job output...";

	task->param("clientPar","jobout");
	comQueue_->addTask(task);
}

void ServerHandler::jobstatus(VTask_ptr task)
{
    //static std::string errText="no job output...";

    task->param("clientPar","stat");
    comQueue_->addTask(task);
}

void ServerHandler::manual(VTask_ptr task)
{
	//std::string errText="no manual ...";
	task->param("clientPar","manual");
	comQueue_->addTask(task);
}

//The core refresh function. That should be the only one directly accessing the queue.
void ServerHandler::refreshInternal()
{
    //We add and refresh task to the queue. On startup this function can be called
    //before the comQueue_ was created so we need to check if it exists.
    if(comQueue_)
    {
        comQueue_->addNewsTask();
    }
}

//The user initiated a refresh - using the toolbar/menu buttons or
//called after a user command was issued
void ServerHandler::refresh()
{
    if (isInLogout()) {
        stopRefreshTimer();
        return;
    }

    if(isDisabled())
        return;

    //We do not know if the server is compatible
    if(compatibility_ ==  CanBeCompatible)
    {
        //aync - will call reset if successfull
        checkServerVersion();
        return;
    }

    refreshInternal();

    //Reset the timer to its original value (i.e. remove the drift)
    updateRefreshTimer();
}

//This slot is called by the timer regularly to get news from the server.
void ServerHandler::refreshServerInfo()
{
    if (isInLogout()) {
        stopRefreshTimer();
        return;
    }

    UiLogS(this).dbg() << "Auto refreshing server";
    refreshInternal();

    //We reduce the update frequency
    driftRefreshTimer();
}

//======================================================================================
// Manages node changes.
//======================================================================================

//This slot is called when a node changes.
void ServerHandler::slotNodeChanged(const Node* nc,std::vector<ecf::Aspect::Type> aspect)
{
    UiLogS(this).dbg() << "ServerHandler::slotNodeChanged - node: " + nc->name();

    //for(std::vector<ecf::Aspect::Type>::const_iterator it=aspect.begin(); it != aspect.end(); ++it)
    //UserMessage::message(UserMessage::DBG, false, std::string(" aspect: ") + boost::lexical_cast<std::string>(*it));

	//This can happen if we initiated a reset while we sync in the thread
	if(vRoot_->isEmpty())
	{
        UiLogS(this).dbg() << " tree is empty - no change applied!";
		return;
	}

	VNode* vn=vRoot_->toVNode(nc);

	//We must have this VNode
	assert(vn != NULL);

	//Begin update for the VNode
	VNodeChange change;
	vRoot_->beginUpdate(vn,aspect,change);

	//TODO: what about the infopanel or breadcrumbs??????
	if(change.ignore_)
	{
        UiLogS(this).dbg() << " update ignored";
		return;
	}
	else
	{
		//Notify the observers
		broadcast(&NodeObserver::notifyBeginNodeChange,vn,aspect,change);

		//End update for the VNode
		vRoot_->endUpdate(vn,aspect,change);

		//Notify the observers
		broadcast(&NodeObserver::notifyEndNodeChange,vn,aspect,change);

        UiLogS(this).dbg() << " update applied";
	}
}

void ServerHandler::addNodeObserver(NodeObserver *obs)
{
	auto it=std::find(nodeObservers_.begin(),nodeObservers_.end(),obs);
	if(it == nodeObservers_.end())
	{
		nodeObservers_.push_back(obs);
	}
}

void ServerHandler::removeNodeObserver(NodeObserver *obs)
{
	auto it=std::find(nodeObservers_.begin(),nodeObservers_.end(),obs);
	if(it != nodeObservers_.end())
	{
		nodeObservers_.erase(it);
	}
}

void ServerHandler::broadcast(NoMethod proc,const VNode* node)
{
	for(std::vector<NodeObserver*>::const_iterator it=nodeObservers_.begin(); it != nodeObservers_.end(); ++it)
		((*it)->*proc)(node);
}

void ServerHandler::broadcast(NoMethodV1 proc,const VNode* node,const std::vector<ecf::Aspect::Type>& aspect,const VNodeChange& change)
{
    //When the observers are being notified (in a loop) they might
    //want to remove themselves from the observer list. This will cause a crash. To avoid
    //this we create a copy of the observers and use it in the notification loop.
    std::vector<NodeObserver*> nObsCopy=nodeObservers_;

    for(std::vector<NodeObserver*>::const_iterator it=nObsCopy.begin(); it != nObsCopy.end(); ++it)
    {
        ((*it)->*proc)(node,aspect,change);
    }

#if 0
    for(std::vector<NodeObserver*>::const_iterator it=nodeObservers_.begin(); it != nodeObservers_.end(); ++it)
		((*it)->*proc)(node,aspect,change);
#endif
}

//---------------------------------------------------------------------------
// Manages Defs changes and desf observers. Defs observers are notified when
// there is a change.
//---------------------------------------------------------------------------

//This slot is called when the Defs change.
void ServerHandler::slotDefsChanged(std::vector<ecf::Aspect::Type> aspect)
{
    UiLog().dbg() << "ServerHandler::slotDefsChanged -->";
    //for(std::vector<ecf::Aspect::Type>::const_iterator it=aspect.begin(); it != aspect.end(); ++it)
    //	UserMessage::message(UserMessage::DBG, false, std::string(" aspect: ") + boost::lexical_cast<std::string>(*it));

	//Begin update for the VNode
    //VNodeChange change;
	vRoot_->beginUpdate(aspect);

	//Notify the observers
	//broadcast(&NodeObserver::notifyBeginNodeChange,vn,aspect,change);

	//End update for the VNode
	//vRoot_->endUpdate(vn,aspect,change);

	//Notify the observers
	//broadcast(&NodeObserver::notifyEndNodeChange,vn,aspect,change);

	//UserMessage::message(UserMessage::DBG, false," --> Update applied");

	for(std::vector<ServerObserver*>::const_iterator it=serverObservers_.begin(); it != serverObservers_.end(); ++it)
		(*it)->notifyDefsChanged(this,aspect);
}

void ServerHandler::addServerObserver(ServerObserver *obs)
{
	auto it=std::find(serverObservers_.begin(),serverObservers_.end(),obs);
	if(it == serverObservers_.end())
	{
        serverObservers_.push_back(obs);
#ifdef __UI_SERVEROBSERVER_DEBUG
        UiLog(this).dbg() << "ServerHandler::addServerObserver -->  " << obs;
#endif
	}
}

void ServerHandler::removeServerObserver(ServerObserver *obs)
{
#ifdef __UI_SERVEROBSERVER_DEBUG
    UI_FUNCTION_LOG_S(this)
#endif
    auto it=std::find(serverObservers_.begin(),serverObservers_.end(),obs);
	if(it != serverObservers_.end())
	{
		serverObservers_.erase(it);
#ifdef __UI_SERVEROBSERVER_DEBUG
        UiLog(this).dbg() << " remove: " << obs;
#endif
	}
}

void ServerHandler::broadcast(SoMethod proc)
{
#ifdef __UI_SERVEROBSERVER_DEBUG
    UI_FUNCTION_LOG_S(this)
#endif

    bool checkExistence=true;

    //When the observers are being notified (in a loop) they might
	//want to remove themselves from the observer list. This will cause a crash. To avoid
	//this we create a copy of the observers and use it in the notification loop.
	std::vector<ServerObserver*> sObsCopy=serverObservers_;

	for(std::vector<ServerObserver*>::const_iterator it=sObsCopy.begin(); it != sObsCopy.end(); ++it)
	{
		//We need to check if the given observer is still in the original list. When we delete the server, due to
		//dependencies it is possible that the observer is already deleted at this point.
		if(!checkExistence || std::find(serverObservers_.begin(),serverObservers_.end(),*it) != serverObservers_.end())
        {
            ((*it)->*proc)(this);
        }
	}
}

void ServerHandler::broadcast(SoMethodV1 proc,const VServerChange& ch)
{
	for(std::vector<ServerObserver*>::const_iterator it=serverObservers_.begin(); it != serverObservers_.end(); ++it)
		((*it)->*proc)(this,ch);
}

//------------------------------------------------
// ServerComObserver
//------------------------------------------------

void ServerHandler::addServerComObserver(ServerComObserver *obs)
{
    auto it=std::find(serverComObservers_.begin(),serverComObservers_.end(),obs);
    if(it == serverComObservers_.end())
    {
        serverComObservers_.push_back(obs);
#ifdef __UI_SERVERCOMOBSERVER_DEBUG
        UiLog(this).dbg() << "ServerHandler::addServerComObserver -->  " << obs;
#endif
    }
}

void ServerHandler::removeServerComObserver(ServerComObserver *obs)
{
    auto it=std::find(serverComObservers_.begin(),serverComObservers_.end(),obs);
    if(it != serverComObservers_.end())
    {
        serverComObservers_.erase(it);
        #ifdef __UI_SERVERCOMOBSERVER_DEBUG
            UiLog(this).dbg() << "ServerHandler::removeServerComObserver --> " << obs;
        #endif
    }
}

void ServerHandler::broadcast(SocMethod proc)
{
    bool checkExistence=true;

    //When the observers are being notified (in a loop) they might
    //want to remove themselves from the observer list. This will cause a crash. To avoid
    //this we create a copy of the observers and use it in the notification loop.
    std::vector<ServerComObserver*> sObsCopy=serverComObservers_;

    for(std::vector<ServerComObserver*>::const_iterator it=sObsCopy.begin(); it != sObsCopy.end(); ++it)
    {
        //We need to check if the given observer is still in the original list. When we delete the server, due to
        //dependencies it is possible that the observer is already deleted at this point.
        if(!checkExistence || std::find(serverComObservers_.begin(),serverComObservers_.end(),*it) != serverComObservers_.end())
            ((*it)->*proc)(this);
    }
}

//-------------------------------------------------------------------
// This slot is called when the comThread finished the given task!!
//-------------------------------------------------------------------

//There was a drastic change during the SYNC! As a safety measure we need to clear
//the tree. We will rebuild it when the SYNC finishes.
void ServerHandler::slotRescanNeed()
{
	clearTree();
}

void ServerHandler::clientTaskFinished(VTask_ptr task,const ServerReply& serverReply)
{
    UiLogS(this).dbg() << "ServerHandler::clientTaskFinished -->";

    //The status of the tasks sent from the info panel must be properly set to
    //FINISHED!! Only that will notify the observers about the change!!

    // task finished but a reset is already on its way
    if (activity() == LoadActivity && task->type()  != VTask::ResetTask) {
        //TODO: more refinement here is needed
        return;
    }

    // The news reply has to be carefully checked
    if(task->type() == VTask::NewsTask)
    {
        // this was a news() called to check the ssl-incompatibilty. If it works we
        // can suppos everyting is fine and can continue with the init!!!
        if (task->param("sslcheck") == "1") {
            compatibleServer();
            return;
        }

        // we've just asked the server if anything has changed - has it?
        refreshFinished();

        switch (serverReply.get_news())
        {
            case ServerReply::NO_NEWS:
            {
                // no news, nothing to do
                UiLogS(this).dbg() << " NO_NEWS";

                //If we just regained the connection we need to reset
                if(connectionGained())
                {
                    reset();
                }
                break;
            }

            case ServerReply::NEWS:
            {
                // yes, something's changed - synchronise with the server

                //If we just regained the connection we need to reset
                UiLogS(this).dbg() << " NEWS - send SYNC command";
                if(connectionGained())
                {
                    reset();
                }
                else
                {
                    comQueue_->addSyncTask();
                }
                break;
            }

            case ServerReply::DO_FULL_SYNC:
            {
                // yes, a lot of things have changed - we need to reset!!!!!!
                UiLogS(this).dbg() << " DO_FULL_SYNC - will reset";
                connectionGained();
                reset();
                break;
            }

            default:
            {
                break;
            }
        }
    }

    // reset is also special
    else if(task->type()  == VTask::ResetTask)
    {
        //If not yet connected but the sync task was successful
        resetFinished();
    }

    // the very first command sent to a server
    else if(task->type()  == VTask::ServerVersionTask)
    {
        QString version=QString::fromStdString(serverReply.get_string());
        int majorVersion=0;
        if(!version.isEmpty())
        {
            QStringList versionLst=version.split(".");
            if(versionLst.count() > 0)
                majorVersion=versionLst[0].toInt();
        }
        if(majorVersion >= 5)
        {
            compatibleServer();
        }
        else
        {
            incompatibleServer(version.toStdString());
        }
    }

    // the rest of the tasks
    else
    {
         UiLogS(this).dbg() << " " << task->typeString() << " finished";

        // Potentially all the comands can call sync_local() implicitly so
        // we need to check for all of them if a sync happened

        //This typically happens when a suite is added/removed
        if(serverReply.full_sync())
        {
            UiLogS(this).dbg() << " full sync requested - rescanTree";

            //we do not want a new suite list through rescanTree() if the task itself was
            //to get a new suite list!!
            //with this we try to avoid infinite recursion!!!
            bool neednewSuiteList=task->type() != VTask::SuiteListTask;

            //This might update the suites + restart the timer
            rescanTree(neednewSuiteList);
        }
        else if(serverReply.in_sync())
        {
            //The tree can be empty for various reasons, we might have cleared it during a sync. In
            //this case we need to rebuild it.
            //We only rebuild the tree when have a proper connection
            if( vRoot_->isEmpty() && connectState_->state() == ConnectState::Normal)
            {
                //we do not want a new suite list through rescanTree() unless the task itself
                //was a sync!
                //With this we try to avoid unnecessary calls to update the suite list and avoid infinite recursion!!!
                bool neednewSuiteList=task->type() != VTask::SyncTask;

                //This might update the suites + restart the timer
                rescanTree(neednewSuiteList);
            }
            broadcast(&ServerObserver::notifyEndServerSync);
        }

        //See which type of task finished. What we do now will depend on that.
        switch(task->type())
        {

            //CommandTask silently performs sync_local().
            case VTask::CommandTask:
            {
                updateRefreshTimer();
                break;
            }

            //WhySyncTask performs sync_local(true).
            case VTask::WhySyncTask:
            {
                updateRefreshTimer();
                break;
            }

            case VTask::ScriptTask:
            case VTask::ManualTask:
            case VTask::HistoryTask:
            case VTask::JobTask:
            case VTask::JobStatusFileTask:
            {
                task->reply()->fileReadMode(VReply::ServerReadMode);
                task->reply()->setText(serverReply.get_string());
                task->reply()->setReadTruncatedTo(truncatedLinesFromServer(task->reply()->text()));
                break;
            }

            case VTask::OutputTask:
            {
                task->reply()->fileReadMode(VReply::ServerReadMode);

                std::string err;
                VFile_ptr f(VFile::create(false));
                f->setFetchMode(VFile::ServerFetchMode);
                f->setFetchModeStr("fetched from server " + name());
                f->setSourcePath(task->reply()->fileName());
                f->setTruncatedTo(truncatedLinesFromServer(serverReply.get_string()));
                f->write(serverReply.get_string(),err);
                task->reply()->tmpFile(f);
                break;
            }

            case VTask::MessageTask:
            {
                task->reply()->setTextVec(serverReply.get_string_vec());
                break;
            }

            //PlugTask silently performs sync_local().
            case VTask::PlugTask:
            {
                auto target = task->param("target_server_name");
                if (ServerHandler* targetSvr = ServerHandler::find(target)) {
                    targetSvr->refresh();
                }
                break;

            }
            case VTask::StatsTask:
            {
                task->reply()->text( serverReply.get_string() );
                break;
            }

            case VTask::ScriptPreprocTask:
            case VTask::ScriptEditTask:
            {
                task->reply()->text(serverReply.get_string());
                break;
            }

            //Submitting the task was successful
            case VTask::ScriptSubmitTask:
            {
                task->reply()->text(serverReply.get_string());
                break;
            }

            case VTask::SuiteListTask:
            {
                //Update the suite filter with the list of suites actually loaded onto the server.
                //If the suitefilter is enabled this might have only a subset of it in our tree
                updateSuiteFilterWithLoaded(serverReply.get_string_vec());
                break;
            }

            case VTask::ZombieListTask:
            {
                task->reply()->zombies(serverReply.zombies());
                break;
            }

            default:
                break;
        }

        task->status(VTask::FINISHED);
    }
}

//-------------------------------------------------------------------
// This slot is called when the comThread failed the given task!!
//-------------------------------------------------------------------

void ServerHandler::clientTaskFailed(VTask_ptr task,const std::string& errMsg,const ServerReply& serverReply)
{
    UiLogS(this).dbg() << "ServerHandler::clientTaskFailed -->";

	//TODO: suite filter  + ci_ observers

    // task failed but a reset is already on its way
    if (activity() == LoadActivity && task->type()  != VTask::ResetTask) {
        //TODO: more refinement here is needed
        return;
    }

    if(task->type() == VTask::NewsTask)
    {
        //This was a news() called after ServerVersionTask failed!
        if (task->param("sslcheck") == "1" &&
            errMsg.find("possibly non-ssl server") != std::string::npos) {
            sslIncompatibleServer(errMsg);
        } else {
            connectionLost(errMsg);
            refreshFinished();
        }
    }

    else if(task->type() == VTask::ResetTask)
    {
        resetFailed(errMsg);
    }

    // the very first command sent to a server
    else if(task->type()  == VTask::ServerVersionTask)
    {
        if(serverReply.invalid_argument())
        {
            incompatibleServer("");
        }
        // if there is no EOF it could be an SSL incomptability issue!
        else if(!serverReply.eof())
        {
            // but ... the error message is not make it 100% certain!
            // So we will ask for the news! It will give back an error message
            // that be could properly parsed to identify if it is really
            // an SSL issue!
            compatibility_ = Compatibility::CanBeCompatible;
            comQueue_->addNewsTask("sslcheck","1");
        }
        else
        {
            connectionLost(errMsg);
        }
    }
    else if(task->type()  == VTask::CommandTask)
    {
        //comQueue_->addSyncTask();
        task->reply()->setErrorText(errMsg);
        task->status(VTask::ABORTED);
        UserMessage::message(UserMessage::WARN, true, errMsg);
    }
    else if(task->type()  == VTask::PlugTask)
    {
        task->reply()->setErrorText(errMsg);
        task->status(VTask::ABORTED);
        UserMessage::message(UserMessage::WARN, true, errMsg);
    }
    else
    {
        if(errMsg.empty())
            connectionLost(errMsg);

        task->reply()->setErrorText(errMsg);
        task->status(VTask::ABORTED);
    }
}

void ServerHandler::connectionLost(const std::string& errMsg)
{
	connectState_->state(ConnectState::Lost);
	connectState_->errorMessage(errMsg);
	broadcast(&ServerObserver::notifyServerConnectState);
}

bool ServerHandler::connectionGained()
{
	if(connectState_->state() != ConnectState::Normal)
	{
		connectState_->state(ConnectState::Normal);
		broadcast(&ServerObserver::notifyServerConnectState);
		return true;
	}
	return false;

}

void ServerHandler::disconnectServer()
{
	if(connectState_->state() != ConnectState::Disconnected)
	{
		connectState_->state(ConnectState::Disconnected);
		broadcast(&ServerObserver::notifyServerConnectState);

		//Stop the queue
		comQueue_->disable();

		//Stop the timer
		stopRefreshTimer();
	}
}

void ServerHandler::connectServer()
{
	if(connectState_->state() == ConnectState::Disconnected)
	{
		//Start the queue
		comQueue_->enable();

		//Start the timer
		startRefreshTimer();

		//Try to get the news
        refreshInternal();

		//TODO: attach the observers!!!!
	}
}

void ServerHandler::checkServerVersion()
{
    comQueue_->addServerVersionTask();
}

void ServerHandler::failedClientServer(const std::string& msg)
{
    compatibility_=Incompatible;
    stopRefreshTimer();
    if(comQueue_) {
        comQueue_->disable();
    }

    connectState_->state(ConnectState::FailedClient);
    connectState_->errorMessage(msg);
    broadcast(&ServerObserver::notifyServerConnectState);
}

void ServerHandler::compatibleServer()
{
    compatibility_=Compatible;
    reset();
}

void ServerHandler::incompatibleServer(const std::string& version)
{
    compatibility_=Incompatible;
    stopRefreshTimer();
    comQueue_->disable();
    setActivity(NoActivity);

    connectState_->state(ConnectState::VersionIncompatible);
    connectState_->errorMessage("Server version = " +
                ((version.empty())?"UNKNOWN":version) +
                 " !  Client can only communicate to servers with version >= 5.0.0! ");
    broadcast(&ServerObserver::notifyServerConnectState);
}

void ServerHandler::sslIncompatibleServer(const std::string& msg)
{
    compatibility_=Incompatible;
    stopRefreshTimer();
    comQueue_->disable();

    connectState_->state(ConnectState::SslIncompatible);
    std::string errStr = "Cannot communicate to server.";
#if ECF_OPENSSL
    if (ssl_) {
        errStr += " Server is marked as SSL in the UI. Please check if the server is really SSL-enabled! Also check settings in Manage servers dialogue!";
    } else {
        errStr += " Server is marked as non-SSL in the UI. Please check if SSL is really disabled in the server! Also check settings in Manage servers dialogue!";
    }
#else
    errStr += " ecFlow was built without SSL support but it migth be enabled in the server!";
#endif

    connectState_->errorMessage(errStr + "\n\n"+ msg);
    broadcast(&ServerObserver::notifyServerConnectState);
}

void ServerHandler::sslCertificateError(const std::string& msg)
{
#if ECF_OPENSSL
    assert(ssl_);
    compatibility_=Incompatible;
    stopRefreshTimer();
    comQueue_->disable();

    connectState_->state(ConnectState::SslCertificateError);
    std::string errStr = "Cannot communicate to server. Server is marked as SSL in the UI, but an SSL certificate error was detected.";

    connectState_->errorMessage(errStr + "\n\n" + msg);
    broadcast(&ServerObserver::notifyServerConnectState);
#endif
}

void ServerHandler::reset()
{
    UiLogS(this).dbg() << "ServerHandler::reset -->";

    if(connectState_->state() == ConnectState::Disconnected ||
       connectState_->state() == ConnectState::VersionIncompatible ||
       connectState_->state() == ConnectState::SslIncompatible ||
       connectState_->state() == ConnectState::SslCertificateError ||
       connectState_->state() == ConnectState::FailedClient ||
       compatibility_ == Incompatible)
    {
        return;
    }

	//---------------------------------
	// First part of reset: clearing
	//---------------------------------

	if(comQueue_->prepareReset())
	{
		//Stop the timer
		stopRefreshTimer();

		// First part of reset: clear the tree
		clearTree();

		// Second part of reset: loading

		//Indicate that we reload the defs
		setActivity(LoadActivity);

        //mark the current moment as last refresh
        lastRefresh_=QDateTime::currentDateTime();

		//NOTE: at this point the queue is not running but reset() will start it.
		//While the queue is in reset mode it does not accept tasks.
		comQueue_->reset();
	}
	else
	{
        UiLogS(this).dbg() << " skip reset";
	}
}

//The reset was successful
void ServerHandler::resetFinished()
{
    setActivity(NoActivity);

	//Set server host and port in defs. It is used to find the server of
	//a given node in the viewer.
	{
		ServerDefsAccess defsAccess(this);  // will reliquish its resources on destruction

		defs_ptr defs = defsAccess.defs();
		if(defs != nullptr)
		{
			ServerState& st=defs->set_server();
			st.hostPort(std::make_pair(host_,port_));
		}
	}

	//Create an object to inform the observers about the change
	VServerChange change;

	//Begin the full scan to get the tree. This call does not actually
	//run the scan but counts how many suits will be available.
	vRoot_->beginScan(change);

	//Notify the observers that the scan has started
	broadcast(&ServerObserver::notifyBeginServerScan,change);

	//Finish full scan
	vRoot_->endScan();

    //assert(change.suiteNum_ == vRoot_->numOfChildren());

	//Notify the observers that scan has ended
    broadcast(&ServerObserver::notifyEndServerScan);

	//The suites might have been changed
    updateSuiteFilter(true);

	//Restart the timer
	startRefreshTimer();

	//Set the connection state
	if(connectState_->state() != ConnectState::Normal)
	{
		connectState_->state(ConnectState::Normal);
		broadcast(&ServerObserver::notifyServerConnectState);
	}
}

//The reset failed and we could not connect to the server, e.g. because the the server
//may be down, or there is a network error, or the authorisation is missing.
void ServerHandler::resetFailed(const std::string& errMsg)
{
	//This status is indicated by the connectStat_. Each object needs to be aware of it
	//and do its tasks accordingly.

    //Create an object to inform the observers about the change
    VServerChange change;
    //Notify the observers that the scan has started
    broadcast(&ServerObserver::notifyBeginServerScan,change);
    //Notify the observers that scan has ended
    broadcast(&ServerObserver::notifyEndServerScan);

	connectState_->state(ConnectState::Lost);
	connectState_->errorMessage(errMsg);
	setActivity(NoActivity);

	broadcast(&ServerObserver::notifyServerConnectState);

	//Restart the timer
	startRefreshTimer();
}

//This function must be called during a SYNC!!!!!!!!
//If it calls me must be sure that notifyEndServerScan is called
//in the end!!!

void ServerHandler::clearTree()
{
    UiLogS(this).dbg() << "ServerHandler::clearTree -->";

	if(!vRoot_->isEmpty())
	{
		//Notify observers that the clear is about to begin
        broadcast(&ServerObserver::notifyBeginServerClear);

		//Clear vnode
		vRoot_->clear();

		//Notify observers that the clear ended
        broadcast(&ServerObserver::notifyEndServerClear);
	}

    UiLogS(this).dbg() << " <-- clearTree";
}

void ServerHandler::rescanTree(bool needNewSuiteList)
{
    UiLogS(this).dbg() << "ServerHandler::rescanTree -->";

	setActivity(RescanActivity);

	//---------------------------------
	// First part of rescan: clearing
	//---------------------------------

	//Stop the timer
	stopRefreshTimer();

	//Stop the queue as a safety measure: we do not want any changes during the rescan
	comQueue_->suspend(false);

	//clear the tree
	clearTree();

	//At this point nothing is running and the tree is empty (it only contains
	//the root node)

	//--------------------------------------
	// Second part of rescan: loading
	//--------------------------------------

	//Create an object to inform the observers about the change
	VServerChange change;

	//Begin the full scan to get the tree. This call does not actually
	//run the scan but counts how many suits will be available.
	vRoot_->beginScan(change);

	//Notify the observers that the scan has started
	broadcast(&ServerObserver::notifyBeginServerScan,change);

	//Finish full scan
	vRoot_->endScan();

	//Notify the observers that scan has ended
    broadcast(&ServerObserver::notifyEndServerScan);

	//Restart the queue
	comQueue_->start();

	//The suites might have been changed
    updateSuiteFilter(needNewSuiteList);

	//Start the timer
	startRefreshTimer();

	setActivity(NoActivity);

    UiLogS(this).dbg() << "<-- rescanTree";
}

//====================================================
// Suite filter
//====================================================

void ServerHandler::setSuiteFilterWithOne(VNode* n)
{
    if(n)
    {
        if(VNode* sn=n->suite())
            if(VSuiteNode *suite=sn->isSuite())
            {
                if(suiteFilter_->isEnabled() == false)
                {
                    suiteFilter_->setEnabled(true);
                    suiteFilter_->selectOnlyOne(suite->strName());
                    //after reset the loaded suites need to be read again from the server!
                    suiteFilter_->setLoadedInitialised(false);
                    reset();
                }
                else if(suiteFilter_->isOnlyOneFiltered(suite->strName()) == false)
                {
                    suiteFilter_->selectOnlyOne(suite->strName());
                    //after reset the loaded suites need to be read again from the server!
                    suiteFilter_->setLoadedInitialised(false);
                    reset();
                }
            }
    }
}

void ServerHandler::updateSuiteFilter(SuiteFilter* sf)
{
UI_FUNCTION_LOG_S(this)

    if(isDisabled())
        return;

    //We do not know if the server is compatible
    if(compatibility_ ==  CanBeCompatible)
    {
        //aync - will call reset if successfull
        checkServerVersion();
        return;
    }

    if(suiteFilter_->update(sf))
	{
		//If only this flag has changed we exec a custom task for it
		if(suiteFilter_->changeFlags().sameAs(SuiteFilter::AutoAddChanged))
		{
			comQueue_->addSuiteAutoRegisterTask();
		}
		//Otherwise we need a full reset
		else
		{
			reset();
		}

        //Save the settings
        conf_->saveSettings();
	}
}

//Update the suite filter with the list of suites actually loaded onto the server.
//If the suitefilter is enabled we might have only a subset of it in our tree.
void ServerHandler::updateSuiteFilterWithLoaded(const std::vector<std::string>& loadedSuites)
{
UI_FUNCTION_LOG_S(this)
    if(suiteFilter_->setLoaded(loadedSuites))
    {
        //If the filtered state changed and the filter is active we need to reset
        if(suiteFilter_->isEnabled())
           reset();
    }
}

//Update the suite filter with the list of suites stored in the defs (in the tree). It only
//makes sense if the filter is disabled since in this case the defs stores all the loaded servers.
void ServerHandler::updateSuiteFilterWithDefs()
{
UI_FUNCTION_LOG_S(this)

    if(suiteFilter_->isEnabled())
		return;

    //The filte ris disabled
	std::vector<std::string> defSuites;
	vRoot_->suites(defSuites);
	suiteFilter_->setLoaded(defSuites);
}

//Only called internally after reset or serverscan!!
void ServerHandler::updateSuiteFilter(bool needNewSuiteList)
{
UI_FUNCTION_LOG_S(this)

    if(isDisabled())
        return;

    //We do not know if the server is compatible
    if(compatibility_ ==  CanBeCompatible)
    {
        //aync - will call reset if successfull
        checkServerVersion();
        return;
    }

    //We only fetch the full list of loaded suites from the server
    //via the thread when
    //  -the suiteFilter is enabled!
    if(suiteFilter_->isEnabled())
    {
        //This will call updateSuiteFilterWithLoaded()
        if(needNewSuiteList)
        {
            comQueue_->addSuiteListTask();
        }
    }
    //Otherwise the defs contains the full list of suites
    else
    {
        std::vector<std::string> defSuites;
        vRoot_->suites(defSuites);
        suiteFilter_->setLoaded(defSuites);
    }

#if 0
    //We only fetch the full list of loaded suites from the server
    //via the thread when
    //  -the suiteFilter is not yet initialised
    //   OR
    //  -the suiteFilter is observerved and it is enabled!
    if(!suiteFilter_->isLoadedInitialised() ||
       (suiteFilter_->hasObserver() && suiteFilter_->isEnabled()))
	{
		//This will call updateSuiteFilterWithLoaded()
		comQueue_->addSuiteListTask();
	}
	else
	{
		std::vector<std::string> defSuites;
		vRoot_->suites(defSuites);
		suiteFilter_->setLoaded(defSuites);
	}
#endif
}

bool ServerHandler::readFromDisk() const
{
	return conf_->boolValue(VServerSettings::ReadFromDisk);
}

QString ServerHandler::uidForServerLogTransfer() const
{
    return conf_->stringValue(VServerSettings::UidForServerLogTransfer);
}

int ServerHandler::maxSizeForTimelineData() const
{
    return conf_->intValue(VServerSettings::MaxSizeForTimelineData);
}

QString ServerHandler::nodeMenuMode() const
{
    return conf_->stringValue(VServerSettings::NodeMenuMode);
}

QString ServerHandler::defStatusNodeMenuMode() const
{
    return conf_->stringValue(VServerSettings::NodeMenuModeForDefStatus);
}

void ServerHandler::confChanged(VServerSettings::Param par,VProperty* prop)
{
	switch(par)
	{
    case VServerSettings::AutoUpdate:
    case VServerSettings::UpdateRate:
    case VServerSettings::AdaptiveUpdate:
    case VServerSettings::AdaptiveUpdateMode:
    case VServerSettings::AdaptiveUpdateIncrement:
        updateRefreshTimer();
        break;
    case VServerSettings::MaxAdaptiveUpdateRate:
    {
        if(!checkRefreshTimerDrift())
        {
            updateRefreshTimer();
        }
        break;
    }
    case VServerSettings::NodeMenuMode:
        MainWindow::updateMenuMode(this);
        break;

	case VServerSettings::NotifyAbortedEnabled:
	case VServerSettings::NotifyRestartedEnabled:
	case VServerSettings::NotifyLateEnabled:
	case VServerSettings::NotifyZombieEnabled:
	case VServerSettings::NotifyAliasEnabled:
		checkNotificationState(par);
		break;
	default:
		break;
	}
}

void ServerHandler::checkNotificationState(VServerSettings::Param par)
{
	std::string id=VServerSettings::notificationId(par);
	if(id.empty())
		return;

    bool enabled=false;
	for(std::vector<ServerHandler*>::const_iterator it=servers_.begin(); it != servers_.end(); ++it)
	{
		ServerHandler *s=*it;
		if(s->conf()->boolValue(par))
		{
			enabled=true;
			break;
		}
	}

    ChangeNotify::updateNotificationStateFromServer(id,enabled);
}

//Called from changeNotify
bool ServerHandler::checkNotificationState(const std::string& notifierId)
{
    bool enabled=false;
    VServerSettings::Param par=VServerSettings::notificationParam(notifierId);

    for(std::vector<ServerHandler*>::const_iterator it=servers_.begin(); it != servers_.end(); ++it)
    {
        ServerHandler *s=*it;
        if(s->conf()->boolValue(par))
        {
            enabled=true;
            break;
        }
    }

    return enabled;
}

void ServerHandler::saveSettings()
{
	for(std::vector<ServerHandler*>::const_iterator it=servers_.begin(); it != servers_.end(); ++it)
		(*it)->saveConf();
}

void ServerHandler::saveConf()
{
	conf_->saveSettings();
}

void ServerHandler::loadConf()
{
	//This will call confChanged for any non-default settings
	conf_->loadSettings();
}

void ServerHandler::writeDefs(const std::string& fileName)
{
    if(isDisabled() || compatibility_ ==  CanBeCompatible)
        return;

    comQueue_->suspend(true);
    ServerDefsAccess defsAccess(this);  // will reliquish its resources on destruction
    defs_ptr defs = defsAccess.defs();
    if(defs)
    {
        defs->save_as_filename(fileName,PrintStyle::MIGRATE);
    }
    comQueue_->start();
}

void ServerHandler::writeDefs(VInfo_ptr info,const std::string& fileName)
{
    if(isDisabled() || compatibility_ ==  CanBeCompatible)
        return;

    if(!info || !info->node())
        return;

    comQueue_->suspend(true);
    ServerDefsAccess defsAccess(this);  // will reliquish its resources on destruction
    defs_ptr defs = defsAccess.defs();
    if(defs)
    {
        PrintStyle style(PrintStyle::MIGRATE);
        std::ofstream out(fileName.c_str());
        out << "defs_state MIGRATE" << std::endl;
        out << info->node()->node()->print();
        out << std::endl;
        out.close();
    }
    comQueue_->start();
}

//--------------------------------------------
// Other
//--------------------------------------------

void ServerHandler::searchBegan()
{
    if(isDisabled() || compatibility_ ==  CanBeCompatible)
        return;

    UiLogS(this).dbg() << "ServerHandler::searchBegan --> suspend queue";
	comQueue_->suspend(true);
}

void ServerHandler::searchFinished()
{
    if(isDisabled() || compatibility_ ==  CanBeCompatible)
        return;

    UiLogS(this).dbg() << "ServerHandler::searchFinished --> start queue";
	comQueue_->start();

}

int ServerHandler::truncatedLinesFromServer(const std::string& txt) const
{
    //if the text is truncated the following line is added to the bottom of it:
    //# >>>>>>>> File truncated down to 15. Truncated from the end of file <<<<<<<<<
    //We search for this string and if truncation did happen we indicate it in the reply
    size_t txtSize=txt.size();
    if(txt.find(">> File truncated down to",
        (txtSize > 200)?(txtSize-100):0) != std::string::npos)
    {
        return conf_->intValue(VServerSettings::MaxOutputFileLines);
    }

    return -1;
}

//--------------------------------------------------------------
//
//   Find the server for a node.
//   TODO: this is just a backup method. We might not want to use it
//         at all, since it is not safe.
//
//--------------------------------------------------------------

ServerHandler* ServerHandler::find(const std::string& name)
{
	for(std::vector<ServerHandler*>::const_iterator it=servers_.begin(); it != servers_.end(); ++it)
			if((*it)->name() == name)
					return *it;
	return nullptr;
}

max_stars_repo_name: ecmwf/ecflow

max_issues_repo_name: ecmwf/ecflow

max_forks_repo_name: ecmwf/ecflow


text:
#include <stdexcept>

#include <boost/test/unit_test.hpp>

#include "rectangle.hpp"
#include "base-types.hpp"

namespace
{
  const double eps = 1e-10;
}

BOOST_AUTO_TEST_SUITE(RectangleConstructor)

BOOST_AUTO_TEST_CASE(ValidArguments_ValidInitialization)
{
  double width = 5.6, height = 43.1;
  yakovlev::point_t pos = { 2.3, -93.2 };
  yakovlev::Rectangle rect = { width, height, pos };
  BOOST_CHECK_CLOSE(rect.getWidth(), width, eps);
  BOOST_CHECK_CLOSE(rect.getHeight(), height, eps);
  BOOST_CHECK_CLOSE(rect.getCenter().x, pos.x, eps);
  BOOST_CHECK_CLOSE(rect.getCenter().y, pos.y, eps);
}

BOOST_AUTO_TEST_CASE(InvalidWidth_ThrowsInvalidArgument)
{
  BOOST_CHECK_THROW(yakovlev::Rectangle(-3.2, 3.2, { 0.0, 0.0 }), std::invalid_argument);
  BOOST_CHECK_THROW(yakovlev::Rectangle(0.0, 3.2, { 0.0, 0.0 }), std::invalid_argument);
}

BOOST_AUTO_TEST_CASE(InvalidHeight_ThrowsInvalidArgument)
{
  BOOST_CHECK_THROW(yakovlev::Rectangle(3.2, -432.12, { 0.0, 0.0 }), std::invalid_argument);
  BOOST_CHECK_THROW(yakovlev::Rectangle(243.2, 0.0, { 0.0, 0.0 }), std::invalid_argument);
}

BOOST_AUTO_TEST_SUITE_END()

//=====================================================================================

BOOST_AUTO_TEST_SUITE(RectangleFrame)

BOOST_AUTO_TEST_CASE(ValidRectangle_ValidFraming)
{
  double width = 3.42, height = 1.23;
  yakovlev::point_t pos = { 0.00312783, 323.2 };
  yakovlev::Rectangle rect = { width, height, pos };
  yakovlev::rectangle_t frame = rect.getFrameRect();
  BOOST_CHECK_CLOSE(frame.width, width, eps);
  BOOST_CHECK_CLOSE(frame.height, height, eps);
  BOOST_CHECK_CLOSE(frame.pos.x, pos.x, eps);
  BOOST_CHECK_CLOSE(frame.pos.y, pos.y, eps);
}

BOOST_AUTO_TEST_SUITE_END()

//=====================================================================================

BOOST_AUTO_TEST_SUITE(RectangleMove)

BOOST_AUTO_TEST_CASE(By_ValidArguments_CenterMoved)
{
  yakovlev::point_t center = { 83.3, -93.2 };
  yakovlev::Rectangle rect = { 423.3, 23.4, center };
  double moveX = 312.4, moveY = 434.27;
  rect.move(moveX, moveY);
  yakovlev::point_t movedCenter = rect.getCenter();
  BOOST_CHECK_CLOSE(movedCenter.x, center.x + moveX, eps);
  BOOST_CHECK_CLOSE(movedCenter.y, center.y + moveY, eps);
}

BOOST_AUTO_TEST_CASE(To_ValidArguments_CenterMoved)
{
  yakovlev::Rectangle rect = { 53.2, 312.43, { 2.3, -93.2 } };
  yakovlev::point_t newCenter = { 84.2, -1.4043 };
  rect.move(newCenter);
  BOOST_CHECK_CLOSE(rect.getCenter().x, newCenter.x, eps);
  BOOST_CHECK_CLOSE(rect.getCenter().y, newCenter.y, eps);
}

BOOST_AUTO_TEST_SUITE_END()

//=====================================================================================

BOOST_AUTO_TEST_SUITE(RectangleMovePersistence)

BOOST_AUTO_TEST_CASE(By_ValidArguments_WidthPersists)
{
  double width = 4.3;
  yakovlev::Rectangle rect = { width, 312.43, { 2.3, -93.2 } };
  rect.move(3245.4, -154.532432);
  BOOST_CHECK_CLOSE(rect.getWidth(), width, eps);
}

BOOST_AUTO_TEST_CASE(By_ValidArguments_HeightPersists)
{
  double height = 342.3;
  yakovlev::Rectangle rect = { 12345.123, height, { -223.3, -93.2 } };
  rect.move(-954345.4, 354.532432);
  BOOST_CHECK_CLOSE(rect.getHeight(), height, eps);
}

BOOST_AUTO_TEST_CASE(To_ValidArguments_WidthPersists)
{
  double width = 5.323;
  yakovlev::Rectangle rect = { width, 952.43, { 2.3, -9433.2 } };
  rect.move({ 213.432, -2321.2 });
  BOOST_CHECK_CLOSE(rect.getWidth(), width, eps);
}

BOOST_AUTO_TEST_CASE(To_ValidArguments_HeightPersists)
{
  double height = 6754.2;
  yakovlev::Rectangle rect = { 17645.123, height, { 0.0, -93.2 } };
  rect.move({ -93.2, 0.0 });
  BOOST_CHECK_CLOSE(rect.getHeight(), height, eps);
}

BOOST_AUTO_TEST_CASE(By_ValidArguments_AreaPersists)
{
  yakovlev::Rectangle rect = { 543.1, 832.2, { 536.2, 953.2 } };
  double area = rect.getArea();
  rect.move(482.2, -645.5432);
  BOOST_CHECK_CLOSE(rect.getArea(), area, eps);
}

BOOST_AUTO_TEST_CASE(To_ValidArguments_AreaPersists)
{
  yakovlev::Rectangle rect = { 34.3, 9.2, { 536.2, 953.2 } };
  double area = rect.getArea();
  rect.move({ 432.3, 4523.21 });
  BOOST_CHECK_CLOSE(rect.getArea(), area, eps);
}

BOOST_AUTO_TEST_CASE(By_ValidArguments_FramePersists)
{
  yakovlev::Rectangle rect = { 34.3, 9.2, { 536.2, 953.2 } };
  yakovlev::rectangle_t frame = rect.getFrameRect();
  rect.move(3.2, 45.532);
  yakovlev::rectangle_t movedFrame = rect.getFrameRect();
  BOOST_CHECK_CLOSE(movedFrame.width, frame.width, eps);
  BOOST_CHECK_CLOSE(movedFrame.height, frame.height, eps);
  BOOST_CHECK_CLOSE(movedFrame.pos.x, rect.getCenter().x, eps);
  BOOST_CHECK_CLOSE(movedFrame.pos.y, rect.getCenter().y, eps);
}

BOOST_AUTO_TEST_CASE(To_ValidArguments_FramePersists)
{
  yakovlev::Rectangle rect = { 34.3, 9.2, { 536.2, 953.2 } };
  yakovlev::rectangle_t frame = rect.getFrameRect();
  rect.move({ -43.2, 5.532 });
  yakovlev::rectangle_t movedFrame = rect.getFrameRect();
  BOOST_CHECK_CLOSE(movedFrame.width, frame.width, eps);
  BOOST_CHECK_CLOSE(movedFrame.height, frame.height, eps);
  BOOST_CHECK_CLOSE(movedFrame.pos.x, rect.getCenter().x, eps);
  BOOST_CHECK_CLOSE(movedFrame.pos.y, rect.getCenter().y, eps);
}

BOOST_AUTO_TEST_SUITE_END()

//=====================================================================================

BOOST_AUTO_TEST_SUITE(RectangleRotate)

BOOST_AUTO_TEST_CASE(AnyAngle_AreaPersists)
{
  yakovlev::Rectangle rect = { 543.1, 832.2, { 536.2, 953.2 } };
  double area = rect.getArea();
  rect.rotate(12);
  BOOST_CHECK_CLOSE(rect.getArea(), area, eps);
}

BOOST_AUTO_TEST_CASE(AnyAngle_CenterPersists)
{
  yakovlev::Rectangle rect = { 543.1, 832.2, { 536.2, 953.2 } };
  yakovlev::point_t center = rect.getCenter();
  rect.rotate(-12);
  BOOST_CHECK_CLOSE(rect.getCenter().x, center.x, eps);
  BOOST_CHECK_CLOSE(rect.getCenter().y, center.y, eps);
}

BOOST_AUTO_TEST_CASE(AnyAngle_FrameUpdates)
{
  yakovlev::Rectangle rect = { 543.1, 832.2, { 536.2, 953.2 } };
  yakovlev::rectangle_t frame = rect.getFrameRect();
  rect.rotate(90);
  yakovlev::rectangle_t updatedFrame = rect.getFrameRect();
  BOOST_CHECK_CLOSE(updatedFrame.height, frame.width, eps);
  BOOST_CHECK_CLOSE(updatedFrame.width, frame.height, eps);
  BOOST_CHECK_CLOSE(updatedFrame.pos.x, frame.pos.x, eps);
  BOOST_CHECK_CLOSE(updatedFrame.pos.y, frame.pos.y, eps);
}

BOOST_AUTO_TEST_SUITE_END()

//=====================================================================================

BOOST_AUTO_TEST_SUITE(RectangleScale)

BOOST_AUTO_TEST_CASE(ValidCoefficient_AreaScales)
{
  yakovlev::Rectangle rect = { 423.12, 312.2, { -324.1, 93.2 } };
  double area = rect.getArea();
  double coef = 1.534;
  rect.scale(coef);
  BOOST_CHECK_CLOSE(rect.getArea(), area * coef * coef, eps);
}

BOOST_AUTO_TEST_CASE(InvalidCoefficient_ThrowsInvalidArgument)
{
  yakovlev::Rectangle rect = { 5.0, 3.2, { 0.0, 5.4 } };
  BOOST_CHECK_THROW(rect.scale(-3.4), std::invalid_argument);
  BOOST_CHECK_THROW(rect.scale(0.0), std::invalid_argument);
}

BOOST_AUTO_TEST_CASE(ValidCoefficient_FramePersists)
{
  yakovlev::Rectangle rect = { 5.0, 3.2, { 0.0, 5.4 } };
  yakovlev::rectangle_t frame = rect.getFrameRect();
  double coef = 432.32;
  rect.scale(coef);
  yakovlev::rectangle_t scaledFrame = rect.getFrameRect();
  BOOST_CHECK_CLOSE(scaledFrame.width, frame.width * coef, eps);
  BOOST_CHECK_CLOSE(scaledFrame.height, frame.height * coef, eps);
  BOOST_CHECK_CLOSE(scaledFrame.pos.x, frame.pos.x, eps);
  BOOST_CHECK_CLOSE(scaledFrame.pos.y, frame.pos.y, eps);
}

BOOST_AUTO_TEST_SUITE_END()

ext: cpp

max_stars_repo_name: NekoSilverFox/CPP

max_issues_repo_name: NekoSilverFox/CPP

max_forks_repo_name: NekoSilverFox/CPP


text:
#include "stdafx.h"
#include "Listeners/SessionStatusListener.h"
#include <IO2GSession.h>

#include <boost/log/trivial.hpp>

#if defined(_WIN32) || defined(WIN32)
#define __PRETTY_FUNCTION__ __FUNCTION__
#endif

using namespace boost::python;

class IO2GSessionDescriptorWrap : public IO2GSessionDescriptor, public wrapper < IO2GSessionDescriptor >
{
public:
	const char* getID(){ return this->get_override("getID")(); }
	const char* getName(){ return this->get_override("getName")(); }
	const char* getDescription(){ return this->get_override("getDescription")(); }
	bool requiresPin(){ return this->get_override("requiresPin")(); }
};

class IO2GSessionDescriptorCollectionWrap : public IO2GSessionDescriptorCollection, public wrapper < IO2GSessionDescriptorCollection >
{
public:
	int size(){ return this->get_override("size")(); }
	IO2GSessionDescriptor* get(int){ return this->get_override("get")(); }
};

class IO2GSessionStatusWrap : public IO2GSessionStatus, public wrapper < IO2GSessionStatus >
{
public:
	void onSessionStatusChanged(O2GSessionStatus status){ this->get_override("_on_session_status_changed")(); }
	void onLoginFailed(const char *error){ this->get_override("_on_login_failed")(); }
};

class IO2GTableManagerListenerWrap : public IO2GTableManagerListener, public wrapper < IO2GTableManagerListener >
{
public:
	void onStatusChanged(O2GTableManagerStatus status, IO2GTableManager *tableManager){ this->get_override("_on_status_changed")(); }
};

class IO2GSessionWrap : public IO2GSession, public wrapper < IO2GSession >
{
public:
	IO2GLoginRules* getLoginRules(){ return this->get_override("getLoginRules")();}
	void login(const char *user, const char *pwd, const char *url, const char *connection){  this->get_override("login")();}
	void logout(){ this->get_override("logout")();}
	void subscribeSessionStatus(IO2GSessionStatus* listener){ this->get_override("subscribeSessionStatus")(); }
	void unsubscribeSessionStatus(IO2GSessionStatus* listener){ this->get_override("unsubscribeSessionStatus")(); }
	IO2GSessionDescriptorCollection* getTradingSessionDescriptors(){ return this->get_override("getTradingSessionDescriptors")();}
	void setTradingSession(const char *sessionId, const char *pin){ this->get_override("setTradingSession")();}
	void subscribeResponse(IO2GResponseListener *listener){ this->get_override("subscribeResponse")();}
	void unsubscribeResponse(IO2GResponseListener *listener){ this->get_override("unsubscribeResponse")();}
	IO2GRequestFactory* getRequestFactory(){ return this->get_override("getRequestFactory")();}
	IO2GResponseReaderFactory* getResponseReaderFactory(){ return this->get_override("getResponseReaderFactory")();}
	void sendRequest(IO2GRequest *request){ this->get_override("sendRequest")();}
	IO2GTimeConverter* getTimeConverter(){ return this->get_override("getTimeConverter")();}
	void setPriceUpdateMode(O2GPriceUpdateMode mode){ this->get_override("setPriceUpdateMode")();}
	O2GPriceUpdateMode getPriceUpdateMode(){ return this->get_override("getPriceUpdateMode")();}
	DATE getServerTime(){ return this->get_override("getServerTime")();}
	int getReportURL(char* urlBuffer, int bufferSize, IO2GAccountRow* account, DATE dateFrom, DATE dateTo,
		const char* format, const char* reportType, const char* langID, long ansiCP){ return this->get_override("getReportURL")();}
	IO2GTableManager* getTableManager() { return this->get_override("getTableManager")(); }
	IO2GTableManager* getTableManagerByAccount(const char *accountID) { return this->get_override("getTableManagerByAccount")(); }
	void useTableManager(O2GTableManagerMode mode, IO2GTableManagerListener *tablesListener) { this->get_override("useTableManager")(); }
	IO2GSessionStatus::O2GSessionStatus getSessionStatus() { return this->get_override("getSessionStatus")(); }
};

class gil_lock
{
public:
  gil_lock()  { state_ = PyGILState_Ensure(); }

  ~gil_lock() { PyGILState_Release(state_);   }

private:
  PyGILState_STATE state_;
};

class SessionStatusListenerCallback : public SessionStatusListener
{
public:
	SessionStatusListenerCallback(PyObject *pyObject)
		: self(pyObject)
                {

                }

	SessionStatusListenerCallback(PyObject* pyObject, const SessionStatusListener& listener)
		: self(pyObject), SessionStatusListener(listener)
                {

                }

	void onSessionStatusChanged(O2GSessionStatus status)
	{
                gil_lock lock;
                try
                {
                        call_method<void>(self, "_on_session_status_changed", status);
                        //BOOST_LOG_TRIVIAL(trace) << __PRETTY_FUNCTION__;
                }
                catch(error_already_set)
                {
                        PyErr_Print();
                }
	}

	void onLoginFailed(const char* error)
	{
                gil_lock lock;
                try
                {
                        call_method<void>(self, "_on_login_failed", error);
                        //BOOST_LOG_TRIVIAL(trace) << __PRETTY_FUNCTION__;
                }
                catch(error_already_set)
                {
                        PyErr_Print();
                }
	}

	long addRef()
	{
                gil_lock lock;
                try
                {
                        long refCount = call_method<long>(self, "addRef");
                        //BOOST_LOG_TRIVIAL(trace) << __PRETTY_FUNCTION__ << refCount;
                        return refCount;
                }
                catch(error_already_set)
                {
                        PyErr_Print();
                }
	}

	long release()
	{
                gil_lock lock;
                try
                {
                        long refCount = call_method<long>(self, "release");
                        //BOOST_LOG_TRIVIAL(trace) << __PRETTY_FUNCTION__ << refCount;
                        return refCount;
                }
                catch(error_already_set)
                {
                        PyErr_Print();
                }
	}

private:
	PyObject* const self;
};

void export_IO2GSession()
{
	class_<IO2GSessionDescriptorWrap, bases<IAddRef>, boost::noncopyable>("IO2GSessionDescriptor", no_init)
		.def("getID", pure_virtual(&IO2GSessionDescriptor::getID))
		.def("getName", pure_virtual(&IO2GSessionDescriptor::getName))
		.def("getDescription", pure_virtual(&IO2GSessionDescriptor::getDescription))
		.def("requiresPin", pure_virtual(&IO2GSessionDescriptor::requiresPin))
		;

	class_<IO2GSessionDescriptorCollectionWrap, bases<IAddRef>, boost::noncopyable>("IO2GSessionDescriptorCollection", no_init)
		.def("size", pure_virtual(&IO2GSessionDescriptorCollection::size))
		.def("get", pure_virtual(&IO2GSessionDescriptorCollection::get), return_value_policy<reference_existing_object>())
		;

	object obj_IO2GSessionStatus = class_<IO2GSessionStatusWrap, bases<IAddRef>, boost::noncopyable>("IO2GSessionStatus", no_init)
		.def("_on_session_status_changed", pure_virtual(&IO2GSessionStatus::onSessionStatusChanged))
		.def("_on_login_failed", pure_virtual(&IO2GSessionStatus::onLoginFailed))
		;
	{
		scope in_IO2GSessionStatus(obj_IO2GSessionStatus);
		enum_<IO2GSessionStatus::O2GSessionStatus>("O2GSessionStatus")
			.value("Disconnected", IO2GSessionStatus::Disconnected)
			.value("Connecting", IO2GSessionStatus::Connecting)
			.value("TradingSessionRequested", IO2GSessionStatus::TradingSessionRequested)
			.value("Connected", IO2GSessionStatus::Connected)
			.value("Reconnecting", IO2GSessionStatus::Reconnecting)
			.value("Disconnecting", IO2GSessionStatus::Disconnecting)
			.value("SessionLost", IO2GSessionStatus::SessionLost)
			.value("PriceSessionReconnecting", IO2GSessionStatus::PriceSessionReconnecting)
			.value("ConnectedWithNeedToChangePassword", IO2GSessionStatus::ConnectedWithNeedToChangePassword)
			.export_values()
			;
	};

	class_<SessionStatusListener, SessionStatusListenerCallback, bases<IO2GSessionStatus>, boost::noncopyable >("SessionStatusListener", init<>())
		.def("_on_session_status_changed", &SessionStatusListenerCallback::onSessionStatusChanged)
		.def("_on_login_failed", &SessionStatusListenerCallback::onLoginFailed)
		.def("addRef", &SessionStatusListenerCallback::addRef)
		.def("release", &SessionStatusListenerCallback::release)
		;

	class_<IO2GTableManagerListenerWrap, bases<IAddRef>, boost::noncopyable>("IO2GTableManagerListener", no_init)
		.def("_on_Status_Changed", pure_virtual(&IO2GTableManagerListener::onStatusChanged))
		;

	class_<IO2GSessionWrap, bases<IAddRef>, boost::noncopyable>("IO2GSession", no_init)
		.def("getLoginRules", pure_virtual(&IO2GSession::getLoginRules), return_value_policy<reference_existing_object>())
		.def("login", pure_virtual(&IO2GSession::login))
		.def("logout", pure_virtual(&IO2GSession::logout))
		.def("subscribeSessionStatus", pure_virtual(&IO2GSession::subscribeSessionStatus))
		.def("unsubscribeSessionStatus", pure_virtual(&IO2GSession::unsubscribeSessionStatus))
		.def("getTradingSessionDescriptors", pure_virtual(&IO2GSession::getTradingSessionDescriptors), return_value_policy<reference_existing_object>())
		.def("setTradingSession", pure_virtual(&IO2GSession::setTradingSession))
		.def("subscribeResponse", pure_virtual(&IO2GSession::subscribeResponse))
		.def("unsubscribeResponse", pure_virtual(&IO2GSession::unsubscribeResponse))
		.def("getRequestFactory", pure_virtual(&IO2GSession::getRequestFactory), return_value_policy<reference_existing_object>())
		.def("getResponseReaderFactory", pure_virtual(&IO2GSession::getResponseReaderFactory), return_value_policy<reference_existing_object>())
		.def("sendRequest", pure_virtual(&IO2GSession::sendRequest))
		.def("getTimeConverter", pure_virtual(&IO2GSession::getTimeConverter), return_value_policy<reference_existing_object>())
		.def("setPriceUpdateMode", pure_virtual(&IO2GSession::setPriceUpdateMode))
		.def("getPriceUpdateMode", pure_virtual(&IO2GSession::getPriceUpdateMode))
		.def("getServerTime", pure_virtual(&IO2GSession::getServerTime))
		//.def("getReportURL", pure_virtual(&IO2GSession::getReportURL))
		.def("getTableManager", pure_virtual(&IO2GSession::getTableManager), return_value_policy<reference_existing_object>())
		.def("getTableManagerByAccount", pure_virtual(&IO2GSession::getTableManagerByAccount), return_value_policy<reference_existing_object>())
		.def("useTableManager", pure_virtual(&IO2GSession::useTableManager))
		.def("getSessionStatus", pure_virtual(&IO2GSession::getSessionStatus))
		;
};

max_stars_repo_name: JamesKBowler/fxcpy

max_issues_repo_name: JamesKBowler/fxcpy

max_forks_repo_name: JamesKBowler/fxcpy


text:
/*=============================================================================
    Copyright (c) 2001-2009 Hartmut Kaiser
    Copyright (c) 2001-2009 Joel de Guzman

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/

#include <boost/config/warning_disable.hpp>
#include <boost/detail/lightweight_test.hpp>

#include <boost/spirit/include/karma_char.hpp>
#include <boost/spirit/include/karma_numeric.hpp>
#include <boost/spirit/include/karma_action.hpp>
#include <boost/spirit/include/karma_generate.hpp>
#include <boost/spirit/include/karma_operator.hpp>
#include <boost/bind.hpp>
#include <boost/lambda/lambda.hpp>

#include <sstream>
#include "test.hpp"

using namespace spirit_test;
using boost::spirit::unused_type;

void read1(int& i)
{
    i = 42;
}

void read2(int& i, unused_type)
{
    i = 42;
}

void read3(int& i, unused_type, bool&)
{
    i = 42;
}

void read3_fail(int& i, unused_type, bool& pass)
{
    i = 42;
    pass = false;
}

struct read_action
{
    void operator()(int& i, unused_type, unused_type) const
    {
        i = 42;
    }
};

///////////////////////////////////////////////////////////////////////////////
int main()
{
    using boost::spirit::karma::int_;
    {
        BOOST_TEST(test("42", int_[&read1]));
        BOOST_TEST(test_delimited("42 ", int_[&read1], ' '));
        BOOST_TEST(test("42", int_[&read2]));
        BOOST_TEST(test_delimited("42 ", int_[&read2], ' '));
        BOOST_TEST(test("42", int_[&read3]));
        BOOST_TEST(test_delimited("42 ", int_[&read3], ' '));
        BOOST_TEST(!test("42", int_[&read3_fail]));
        BOOST_TEST(!test_delimited("42 ", int_[&read3_fail], ' '));
    }

    {
        BOOST_TEST(test("42", int_[read_action()]));
        BOOST_TEST(test_delimited("42 ", int_[read_action()], ' '));
    }

    {
        BOOST_TEST(test("42", int_[boost::bind(&read1, _1)]));
        BOOST_TEST(test_delimited("42 ", int_[boost::bind(&read1, _1)], ' '));
        BOOST_TEST(test("42", int_[boost::bind(&read2, _1, _2)]));
        BOOST_TEST(test_delimited("42 ", int_[boost::bind(&read2, _1, _2)], ' '));
        BOOST_TEST(test("42", int_[boost::bind(&read3, _1, _2, _3)]));
        BOOST_TEST(test_delimited("42 ", int_[boost::bind(&read3, _1, _2, _3)], ' '));
    }

    {
        namespace lambda = boost::lambda;
        {
            std::stringstream strm("42");
            BOOST_TEST(test("42", int_[strm >> lambda::_1]));
        }
        {
            std::stringstream strm("42");
            BOOST_TEST(test_delimited("42 ", int_[strm >> lambda::_1], ' '));
        }
    }

    {
        BOOST_TEST(test("{42}", '{' << int_[&read1] << '}'));
        BOOST_TEST(test_delimited("{ 42 } ", '{' << int_[&read1] << '}', ' '));
        BOOST_TEST(test("{42}", '{' << int_[&read2] << '}'));
        BOOST_TEST(test_delimited("{ 42 } ", '{' << int_[&read2] << '}', ' '));
        BOOST_TEST(test("{42}", '{' << int_[&read3] << '}'));
        BOOST_TEST(test_delimited("{ 42 } ", '{' << int_[&read3] << '}', ' '));
    }

    {
        BOOST_TEST(test("{42}", '{' << int_[read_action()] << '}'));
        BOOST_TEST(test_delimited("{ 42 } ", '{' << int_[read_action()] << '}', ' '));
    }

    {
        BOOST_TEST(test("{42}", '{' << int_[boost::bind(&read1, _1)] << '}'));
        BOOST_TEST(test_delimited("{ 42 } ",
            '{' << int_[boost::bind(&read1, _1)] << '}', ' '));
        BOOST_TEST(test("{42}", '{' << int_[boost::bind(&read2, _1, _2)] << '}'));
        BOOST_TEST(test_delimited("{ 42 } ",
            '{' << int_[boost::bind(&read2, _1, _2)] << '}', ' '));
        BOOST_TEST(test("{42}", '{' << int_[boost::bind(&read3, _1, _2, _3)] << '}'));
        BOOST_TEST(test_delimited("{ 42 } ",
            '{' << int_[boost::bind(&read3, _1, _2, _3)] << '}', ' '));
    }

    {
        namespace lambda = boost::lambda;
        {
            std::stringstream strm("42");
            BOOST_TEST(test("{42}", '{' << int_[strm >> lambda::_1] << '}'));
        }
        {
            std::stringstream strm("42");
            BOOST_TEST(test_delimited("{ 42 } ",
                '{' << int_[strm >> lambda::_1] << '}', ' '));
        }
    }

    return boost::report_errors();
}

max_stars_repo_name: boost-cmake/vintage

max_issues_repo_name: boost-cmake/vintage

max_forks_repo_name: boost-cmake/vintage


text:
//---------------------------------------------------------------------------//
// Copyright (c) 2018-2021 Mikhail Komarov <nemo@nil.foundation>
// Copyright (c) 2020-2021 Nikita Kaskov <nbering@nil.foundation>
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//---------------------------------------------------------------------------//
// @file Declaration of functionality that runs the R1CS ppzkSNARK for
// a given R1CS example.
//---------------------------------------------------------------------------//

#ifndef CRYPTO3_RUN_R1CS_PPZKSNARK_HPP
#define CRYPTO3_RUN_R1CS_PPZKSNARK_HPP

#include <boost/config.hpp>

#include <nil/crypto3/zk/snark/systems/ppzksnark/r1cs_ppzksnark.hpp>

#include "../r1cs_examples.hpp"

#include <nil/crypto3/zk/snark/algorithms/generate.hpp>
#include <nil/crypto3/zk/snark/algorithms/verify.hpp>
#include <nil/crypto3/zk/snark/algorithms/prove.hpp>

namespace nil {
    namespace crypto3 {
        namespace zk {
            namespace snark {

                /*template<typename CurveType>
                typename std::enable_if<CurveType::has_affine_pairing, void>::type
                test_affine_verifier(const typename r1cs_ppzksnark<CurveType>::verification_key_type &vk,
                                     const typename r1cs_ppzksnark<CurveType>::primary_input_type &primary_input,
                                     const typename r1cs_ppzksnark<CurveType>::proof_type &proof,
                                     const bool expected_answer) {
                    const bool answer = r1cs_ppzksnark_affine_verifier_weak_IC<CurveType>(vk, primary_input, proof);
                    BOOST_CHECK(answer == expected_answer);
                }

                template<typename CurveType>
                typename std::enable_if<!CurveType::has_affine_pairing, void>::type
                test_affine_verifier(const typename r1cs_ppzksnark<CurveType>::verification_key_type &vk,
                                     const typename r1cs_ppzksnark<CurveType>::primary_input_type &primary_input,
                                     const typename r1cs_ppzksnark<CurveType>::proof_type &proof,
                                     const bool expected_answer) {
                    BOOST_ATTRIBUTE_UNUSED(vk, primary_input, proof, expected_answer);
                }*/

                /**
                 * The code below provides an example of all stages of running a R1CS ppzkSNARK.
                 *
                 * Of course, in a real-life scenario, we would have three distinct entities,
                 * mangled into one in the demonstration below. The three entities are as follows.
                 * (1) The "generator", which runs the ppzkSNARK generator on input a given
                 *     constraint system CS to create a proving and a verification key for CS.
                 * (2) The "prover", which runs the ppzkSNARK prover on input the proving key,
                 *     a primary input for CS, and an auxiliary input for CS.
                 * (3) The "verifier", which runs the ppzkSNARK verifier on input the verification key,
                 *     a primary input for CS, and a proof.
                 */
                template<typename CurveType>
                bool run_r1cs_ppzksnark(const r1cs_example<typename CurveType::scalar_field_type> &example) {

                    using basic_proof_system = r1cs_ppzksnark<CurveType>;
                    using weak_proof_system = r1cs_ppzksnark<CurveType,
                                          r1cs_ppzksnark_generator<CurveType>,
                                          r1cs_ppzksnark_prover<CurveType>,
                                          r1cs_ppzksnark_verifier_weak_input_consistency<CurveType>>;

                    std::cout << "Starting generator" << std::endl;
                    typename basic_proof_system::keypair_type keypair =
                        generate<basic_proof_system>(example.constraint_system);

                    std::cout << "Starting verification key processing" << std::endl;

                    typename basic_proof_system::processed_verification_key_type pvk =
                        r1cs_ppzksnark_process_verification_key<CurveType>::process(keypair.second);

                    std::cout << "Starting prover" << std::endl;

                    typename basic_proof_system::proof_type proof =
                        prove<basic_proof_system>(keypair.first, example.primary_input, example.auxiliary_input);

                    std::cout << "Starting verifier" << std::endl;

                    const bool ans = verify<basic_proof_system>(keypair.second, example.primary_input, proof);

                    std::cout << "Verifier finished, result: " << ans << std::endl;

                    std::cout << "Starting online verifier" << std::endl;

                    const bool ans2 =
                        verify<basic_proof_system>(pvk, example.primary_input, proof);

                    std::cout << "Online verifier finished, result: " << ans2 << std::endl;

                    BOOST_CHECK(ans == ans2);

                    std::cout << "Starting weak verifier" << std::endl;

                    const bool ans3 = verify<weak_proof_system>(keypair.second,
                    example.primary_input, proof);

                    std::cout << "Weak verifier finished, result: " << ans3 << std::endl;

                    BOOST_CHECK(ans == ans3);

                    std::cout << "Starting online weak verifier" << std::endl;

                    const bool ans4 = verify<weak_proof_system>(pvk, example.primary_input, proof);

                    std::cout << "Online weak verifier finished, result: " << ans4 << std::endl;

                    BOOST_CHECK(ans == ans4);

                    /*test_affine_verifier<CurveType>(keypair.second, example.primary_input, proof, ans);*/

                    return ans;
                }
            }    // namespace snark
        }        // namespace zk
    }            // namespace crypto3
}    // namespace nil

#endif    // CRYPTO3_RUN_R1CS_PPZKSNARK_HPP

ext: hpp

max_stars_repo_name: NilFoundation/zk

max_issues_repo_name: NilFoundation/zk

max_forks_repo_name: NilFoundation/zk


text:
#ifndef BOOST_SMART_PTR_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED
#define BOOST_SMART_PTR_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED

//
//  enable_shared_from_this.hpp
//
//  Copyright 2002, 2009 Peter Dimov
//
//  Distributed under the Boost Software License, Version 1.0.
//  See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt
//
//  http://www.boost.org/libs/smart_ptr/enable_shared_from_this.html
//

#include <boost/smart_ptr/weak_ptr.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/detail/sp_noexcept.hpp>
#include <boost/assert.hpp>
#include <boost/config.hpp>

namespace boost
{

template<class T> class enable_shared_from_this
{
protected:

    enable_shared_from_this() BOOST_SP_NOEXCEPT
    {
    }

    enable_shared_from_this(enable_shared_from_this const &) BOOST_SP_NOEXCEPT
    {
    }

    enable_shared_from_this & operator=(enable_shared_from_this const &) BOOST_SP_NOEXCEPT
    {
        return *this;
    }

    ~enable_shared_from_this() BOOST_SP_NOEXCEPT // ~weak_ptr<T> newer throws, so this call also must not throw
    {
    }

public:

    shared_ptr<T> shared_from_this()
    {
        shared_ptr<T> p( weak_this_ );
        BOOST_ASSERT( p.get() == this );
        return p;
    }

    shared_ptr<T const> shared_from_this() const
    {
        shared_ptr<T const> p( weak_this_ );
        BOOST_ASSERT( p.get() == this );
        return p;
    }

    weak_ptr<T> weak_from_this() BOOST_NOEXCEPT
    {
        return weak_this_;
    }

    weak_ptr<T const> weak_from_this() const BOOST_NOEXCEPT
    {
        return weak_this_;
    }

public: // actually private, but avoids compiler template friendship issues

    // Note: invoked automatically by shared_ptr; do not call
    template<class X, class Y> void _internal_accept_owner( shared_ptr<X> const * ppx, Y * py ) const
    {
        if( weak_this_.expired() )
        {
            weak_this_ = shared_ptr<T>( *ppx, py );
        }
    }

private:

    mutable weak_ptr<T> weak_this_;
};

} // namespace boost

#endif  // #ifndef BOOST_SMART_PTR_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED

max_stars_repo_name: iProgramMC/proton

max_issues_repo_name: iProgramMC/proton

max_forks_repo_name: iProgramMC/proton