
/*
 * "$Id: p3face-server.cc,v 1.5 2007-04-15 18:45:23 rmf24 Exp $"
 *
 * RetroShare C++ Interface.
 *
 * Copyright 2004-2006 by Robert Fernie.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License Version 2 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA.
 *
 * Please report all bugs and problems to "retroshare@lunamutt.com".
 *
 */


#include "rsserver/p3face.h"
#include "retroshare/rsplugin.h"

#include "tcponudp/tou.h"
#include <unistd.h>

#include "pqi/authssl.h"
#include <sys/time.h>
#include <time.h>

#include "pqi/p3peermgr.h"
#include "pqi/p3linkmgr.h"
#include "pqi/p3netmgr.h"

int rsserverzone = 101;

#include "util/rsdebug.h"


/****
#define DEBUG_TICK 1
****/

#define WARN_BIG_CYCLE_TIME	(0.2)
#ifdef WINDOWS_SYS
#include <time.h>
#include <sys/timeb.h>
#endif

static double getCurrentTS()
{

#ifndef WINDOWS_SYS
        struct timeval cts_tmp;
        gettimeofday(&cts_tmp, NULL);
        double cts =  (cts_tmp.tv_sec) + ((double) cts_tmp.tv_usec) / 1000000.0;
#else
        struct _timeb timebuf;
        _ftime( &timebuf);
        double cts =  (timebuf.time) + ((double) timebuf.millitm) / 1000.0;
#endif
        return cts;
}

// These values should be tunable from the GUI, to offer a compromise between speed and CPU use.
// In some cases (VOIP) it's likely that we will need to set them temporarily to a very low 
// value, in order to favor a fast feedback

const double RsServer::minTimeDelta = 0.05; // 25;
const double RsServer::maxTimeDelta = 0.2;
const double RsServer::kickLimit = 0.15;


RsServer::RsServer()
	: coreMutex("RsServer")
{
	// This is needed asap.
	//
	mNotify = new p3Notify() ;
	rsNotify = mNotify ;

	mPeerMgr = NULL;
	mLinkMgr = NULL;
	mNetMgr = NULL;
	mHistoryMgr = NULL;

	pqih = NULL;

	mPluginsManager = NULL;

	/* services */
	mHeart = NULL;
	mDisc = NULL;
	msgSrv = NULL;
	chatSrv = NULL;
	mStatusSrv = NULL;
    mGxsTunnels = NULL;

    mMin = 0;
    mLoop = 0;


    mLastts = getCurrentTS();
    mLastSec = 0; /* for the slower ticked stuff */
    mTimeDelta = 0.25 ;

    mAvgTickRate = mTimeDelta;

    /* caches (that need ticking) */

	/* Config */
	mConfigMgr = NULL;
	mGeneralConfig = NULL;
}

RsServer::~RsServer()
{
	delete mGxsTrans;
}

        /* General Internal Helper Functions 
	  ----> MUST BE LOCKED! 
         */



        /* Thread Fn: Run the Core */
void 	RsServer::data_tick()
{
#ifndef WINDOWS_SYS
    usleep((int) (mTimeDelta * 1000000));
#else
    Sleep((int) (mTimeDelta * 1000));
#endif

    double ts = getCurrentTS();
    double delta = ts - mLastts;
    
    /* for the fast ticked stuff */
    if (delta > mTimeDelta)
    {
#ifdef	DEBUG_TICK
        std::cerr << "Delta: " << delta << std::endl;
        std::cerr << "Time Delta: " << mTimeDelta << std::endl;
        std::cerr << "Avg Tick Rate: " << mAvgTickRate << std::endl;
#endif

        mLastts = ts;

        /******************************** RUN SERVER *****************/
        lockRsCore();

        int moreToTick = pqih->tick();

#ifdef	DEBUG_TICK
        std::cerr << "RsServer::run() ftserver->tick(): moreToTick: " << moreToTick << std::endl;
#endif

        unlockRsCore();

        /* tick the Managers */
        mPeerMgr->tick();
        mLinkMgr->tick();
        mNetMgr->tick();
        /******************************** RUN SERVER *****************/

        /* adjust tick rate depending on whether there is more.
             */

        mAvgTickRate = 0.2 * mTimeDelta + 0.8 * mAvgTickRate;

        if (1 == moreToTick)
        {
            mTimeDelta = 0.9 * mAvgTickRate;
            if (mTimeDelta > kickLimit)
            {
                /* force next tick in one sec
                     * if we are reading data.
                     */
                mTimeDelta = kickLimit;
                mAvgTickRate = kickLimit;
            }
        }
        else
        {
            mTimeDelta = 1.1 * mAvgTickRate;
        }

        /* limiter */
        if (mTimeDelta < minTimeDelta)
        {
            mTimeDelta = minTimeDelta;
        }
        else if (mTimeDelta > maxTimeDelta)
        {
            mTimeDelta = maxTimeDelta;
        }

        /* Fast Updates */


        /* now we have the slow ticking stuff */
        /* stuff ticked once a second (but can be slowed down) */
        if ((int) ts > mLastSec)
        {
            mLastSec = (int) ts;

            // Every second! (UDP keepalive).
            //tou_tick_stunkeepalive();

            // every five loops (> 5 secs)
            if (mLoop % 5 == 0)
            {
                //	update_quick_stats();

                // Update All Every 5 Seconds.
                // These Update Functions do the locking themselves.
#ifdef	DEBUG_TICK
                std::cerr << "RsServer::run() Updates()" << std::endl;
#endif

                mConfigMgr->tick(); /* saves stuff */

            }

            // every 60 loops (> 1 min)
            if (++mLoop >= 60)
            {
                mLoop = 0;

                /* force saving FileTransferStatus TODO */
                //ftserver->saveFileTransferStatus();

                /* see if we need to resave certs */
                //AuthSSL::getAuthSSL()->CheckSaveCertificates();

                /* hour loop */
                if (++mMin >= 60)
                {
                    mMin = 0;
                }
            }

            /* Tick slow services */
            if(rsPlugins)
                rsPlugins->slowTickPlugins((time_t)ts);

            // slow update tick as well.
            // update();
        } // end of slow tick.

    } // end of only once a second.

#ifdef	DEBUG_TICK
    double endCycleTs = getCurrentTS();
    double cycleTime = endCycleTs - ts;
    if (cycleTime > WARN_BIG_CYCLE_TIME)
    {
        std::string out;
        rs_sprintf(out, "RsServer::run() WARNING Excessively Long Cycle Time: %g secs => Please DEBUG", cycleTime);
        std::cerr << out << std::endl;

        rslog(RSL_ALERT, rsserverzone, out);
    }
#endif
}
