/* ====================================================================
 * The Vovida Software License, Version 1.0 
 * 
 * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
 *    and "Vovida Open Communication Application Library (VOCAL)" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact vocal@vovida.org.
 *
 * 4. Products derived from this software may not be called "VOCAL", nor
 *    may "VOCAL" appear in their name, without prior written
 *    permission of Vovida Networks, Inc.
 * 
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * 
 * ====================================================================
 * 
 * This software consists of voluntary contributions made by Vovida
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
 * Inc.  For more information on Vovida Networks, Inc., please see
 * <http://www.vovida.org/>.
 *
 */

static const char* const RoundRobinServers_cxx_Version =
    "$Id: RoundRobinServers.cxx,v 1.21 2002/11/12 20:25:43 veer Exp $";

#include "RoundRobinServers.hxx"
#include "HeartbeatCommon.h"
#include "ProvisionInterface.hxx"
#include "VRedirectServerData.hxx"
#include "ServerContainer.hxx"


// static data declarations
RoundRobinServers* RoundRobinServers::_theRoundRobinServers = 0;


RoundRobinServers::RoundRobinServers()
{}



RoundRobinServers::~RoundRobinServers()
{}


/**
 * Obtain the singleton occurrence of this object
 * @param  serverType  Type of server
 * @return  Pointer to the singleton
 */
RoundRobinServers&
RoundRobinServers::instance( const ServerType serverType )
{
    if ( _theRoundRobinServers == 0)
    {
        _theRoundRobinServers = new RoundRobinServers;
    }

    _theRoundRobinServers->updateServerContainer( serverType );
    return *_theRoundRobinServers;
}


/**
 * Store the server type and obtain the list of servers for that type
 * @param  serverType  Type of server
 */
void RoundRobinServers::updateServerContainer( const ServerType serverType )
{
    _serverType = serverType;
    _listOfServers = ServerContainer::instance().getListofServersRef( serverType );
}


/**
 * Copy Constructor
 * @param  rhs  Object from which to copy
 */
RoundRobinServers::RoundRobinServers( const RoundRobinServers& rhs )
{
    if ( this != &rhs )
    {
        copyRhsToThis( rhs );
    }
}


/**
 * Copy member data from given object into this one
 * @param  rhs  Object from which to copy
 * @return  Reference to this object with new data
 */
RoundRobinServers&
RoundRobinServers::operator=(const RoundRobinServers& rhs)
{
    if ( this != &rhs )
    {
        copyRhsToThis( rhs );
    }
    return *this;
}


/**
 * Copy member data from given object into this one
 * @param  rhs  Object from which to copy
 */
void
RoundRobinServers::copyRhsToThis( const RoundRobinServers& rhs )
{
    _pServer = rhs._pServer;
    _pServerPort = rhs._pServerPort;

    _listOfServers = rhs._listOfServers;
    _serverType = rhs._serverType;
}

/**
 * Display the list of servers stored in this object.
 */
void
RoundRobinServers::displayServerList() const
{
    switch ( _serverType )
    {
        case SERVER_RS:
	  cpLog (LOG_DEBUG, "Redirect server info:\n");
        case SERVER_POS:
	  cpLog (LOG_DEBUG, "Policy server info:\n");
        default:
	  cpLog (LOG_DEBUG, "Unknown server type info:\n");
    }

    for ( vector < Data > ::const_iterator itr = _listOfServers->begin();
            itr != _listOfServers->end(); itr++ )
    {
        cpLog (LOG_DEBUG, "      %s", itr->logData());
    }
}



/**
 * Obtain a server using a hash function and strip off the port before returning.
 * @param  hash_value  Value with which to hash the list of servers
 * @return  Host address of chosen server
 */
const Data
RoundRobinServers::getHost( const Data& hash_value )
{
    /// seperate address and port  (actually only need address)
    string newAddress, addr_port = hashIntoServer( hash_value );
    cpLog(LOG_DEBUG, "Hash %s into server: %s", hash_value.logData(), addr_port.c_str());

    string::size_type colonPos = addr_port.find(":");
    if (colonPos != string::npos)
    {
        newAddress = addr_port.substr(0, colonPos);
    }
    else
    {
        newAddress = addr_port;
    }

    return newAddress;
}


/**
 * Obtain a server port by hashing into the server list and stripping 
 * off the host before returning.
 * @param  hash_value  Value with which to hash the list of servers
 * @return  Port of chosen server
 */
int
RoundRobinServers::getPort( const Data& hash_value )
{
    /// seperate address and port  (actually only need port)
    string newPort, addr_port = hashIntoServer( hash_value );

    string::size_type colonPos = addr_port.find(":");
    if (colonPos != string::npos)
    {
        newPort = addr_port.substr(colonPos + 1);
    }
    else
    {
        newPort = "";
    }

    return atoi(newPort.c_str());
}

void 
RoundRobinServers::getHostNPort( const Data& hash_value , Data& host, int& port)
{
    /// seperate address and port  (actually only need port)
    string newPort, addr_port = hashIntoServer( hash_value );
    cpLog(LOG_DEBUG, "Hash %s into server: %s", hash_value.logData(), addr_port.c_str());

    string::size_type colonPos = addr_port.find(":");
    if (colonPos != string::npos)
    {
        host = addr_port.substr(0, colonPos);
        newPort = addr_port.substr(colonPos + 1);
    }
    else
    {
        host = addr_port;
        newPort = "";
    }
    port = atoi(newPort.c_str());
}



/**
 * Determine if the given host and port match a valid server
 * @param  host  Host portion of address to be verified
 * @param  port  Port portion of address to be verified
 * @return  True if server exists, false if not
 */
bool
RoundRobinServers::isExistingServer( const Data& host, const int port ) const
{
    Sptr < Data > searchString = new Data (host);
    *searchString += ":";
    *searchString += Data(port);

    return isExistingServer( searchString );
}


/**
 * Determine if given server matches an existing server in the list
 * @param  searchString  Server address
 * @return  True if server exists, false if not
 */
bool
RoundRobinServers::isExistingServer( Sptr < Data > searchString ) const
{
    unsigned int numOfServers = _listOfServers->size();
    for ( unsigned int i = 0; i < numOfServers; i++ )
    {
        if ( (*_listOfServers)[i] == *searchString )
	{
            return true;
	}
    }

    cpLog(LOG_ERR, "non exisiting server %s", searchString->logData());
    return false;
}



/**
 * Obtain a number using a hashing algorithm 
 * @param  hash_key  Key to be used in the hash
 * @return  Integer result of the hash
 */
unsigned
RoundRobinServers::hashingFunction( const Data& hash_key ) const
{
    unsigned int result = 0;
    LocalScopeAllocator lo;
    const char* p = hash_key.getData(lo);
    int len = hash_key.length();

    /// using bytes of key's representation
    assert (len);
    assert (p);
    while ( len-- )
    {
        result = (result << 1) ^ *p++;
    }

    return result;
}


/**
 * Obtain a server from the list using a hash function
 * @param  hash_key  Key to be used in the hash
 * @return  Server resulting from the hash
 */
string
RoundRobinServers::hashIntoServer( const Data& hash_key )
{
    LocalScopeAllocator lo;
    string result;
    unsigned int hashValue = hashingFunction( hash_key );

    if ( _listOfServers->size() == 0 )
    {
        cpLog(LOG_ERR, "No known servers");
        assert(0);
    }
    int modHashValue = hashValue % _listOfServers->size();


    /// comfirm server is up
    Data testServer((*_listOfServers)[modHashValue]);
    if ( ServerContainer::instance().getStatus( testServer ) != Active )
    {
        cpLog(LOG_DEBUG, "%s is down.  Rehashing to active server", testServer.logData());
        Sptr < vector < Data > > listOfActiveServers = new vector < Data > ;
        ServerContainer::instance().getListofActive( _serverType, listOfActiveServers );

        if ( listOfActiveServers->size() == 0 )
        {
            cpLog(LOG_ERR, "No known active servers, default to first server");
            result = (*_listOfServers)[0].getData(lo);
        }
        else
        {
            modHashValue = hashValue % listOfActiveServers->size();
            result = (*listOfActiveServers)[modHashValue].getData(lo);
        }
    }
    else
        result = testServer.getData(lo);


    return result;

}


syntax highlighted by Code2HTML, v. 0.9.1