/*****************************************************************************
 * Author:   Valient Gough <vgough@pobox.com>
 *
 *****************************************************************************
 * Copyright (c) 2004, Valient Gough
 * 
 * This program is free software; you can distribute it and/or modify it under 
 * the terms of the GNU General Public License (GPL), as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program 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 General Public License for
 * more details.
 */
                             
#include "Interface.h"

#include "ConfigVar.h"

#include <rlog/rlog.h>
#include <rlog/RLogChannel.h>

using namespace rel;
using namespace rlog;

static RLogChannel * Info = DEF_CHANNEL( "info/iface", Log_Info );

Interface::Interface(const char *name_, int Current, int Revision, int Age)
    : _name( name_ )
    , _current( Current )
    , _revision( Revision )
    , _age( Age )
{
}

Interface::Interface(const std::string &name_, int Current, 
	int Revision, int Age)
    : _name( name_ )
    , _current( Current )
    , _revision( Revision )
    , _age( Age )
{
}

Interface::Interface(const Interface &src)
    : _name( src._name )
    , _current( src._current )
    , _revision( src._revision )
    , _age( src._age )
{
}

Interface::Interface()
    : _current( 0 )
    , _revision( 0 )
    , _age( 0 )
{
}

Interface &Interface::operator = (const Interface &src)
{
    _name = src._name;
    _current = src._current;
    _revision = src._revision;
    _age = src._age;
    return *this;
}

const std::string & Interface::name() const
{
    return _name;
}

std::string & Interface::name()
{
    return _name;
}

int Interface::current() const
{
    return _current;
}

int &Interface::current()
{
    return _current;
}

int Interface::revision() const
{
    return _revision;
}

int &Interface::revision()
{
    return _revision;
}

int Interface::age() const
{
    return _age;
}

int &Interface::age()
{
    return _age;
}

bool operator == (const Interface &A, const Interface &B)
{
    return ( A.name() == B.name() 
	    && A.current() == B.current() 
	    && A.revision() == B.revision()
	    && A.age() == B.age() );
}

bool operator != (const Interface &A, const Interface &B)
{
    return ( A.name() != B.name() 
	    || A.current() != B.current() 
	    || A.revision() != B.revision()
	    || A.age() != B.age() );
}

// zero branch method of getting comparison sign.. 
// tricky.. makes assumptions
#if 0
static int sign( int a, int b )
{
    unsigned int ab = ((unsigned int)(a - b)) >> 31;
    unsigned int ba = ((unsigned int)(b - a)) >> 31;

    return 1 + ba - ab;
}
#else
// simple, easy to check, unlikely to break due to unforseen events..
static int sign( int a, int b )
{
    if(a < b)
	return 0;
    else if(a == b)
	return 1;
    else
	return 2;
}
#endif

static int diffSum( const Interface &A, const Interface &B )
{
    int cS = sign( A.current() , B.current() );
    int aS = sign( A.age(), B.age() );
    int rS = sign( A.revision(), B.revision() );

    return (cS * 3 + aS) * 3 + rS;
}

const int EqualVersion = (1 * 3 + 1) * 3 + 1;

bool Interface::implements(const Interface &B) const
{
    rLog(Info, "checking if %s(%i:%i:%i) implements %s(%i:%i:%i)",
	    name().c_str(), current(), revision(), age(),
	    B.name().c_str(), B.current(), B.revision(), B.age());

    if( name() != B.name() )
	return false;

    int currentDiff = current() - B.current();
    return ( currentDiff >= 0 && currentDiff <= age() );
}


bool operator < (const Interface &A, const Interface &B)
{
    if( A.name() == B.name() )
    {
	return ( diffSum(A,B) < EqualVersion );
    } else
	return A.name() < B.name();
}

bool operator > (const Interface &A, const Interface &B)
{
    if( A.name() == B.name() )
    {
	return ( diffSum(A,B) > EqualVersion );
    } else
	return A.name() < B.name();
}

bool operator <= (const Interface &A, const Interface &B)
{
    if( A.name() == B.name() )
    {
	return ( diffSum(A,B) <= EqualVersion );
    } else
	return A.name() < B.name();
}

bool operator >= (const Interface &A, const Interface &B)
{
    if( A.name() == B.name() )
    {
	return ( diffSum(A,B) >= EqualVersion );
    } else
	return A.name() < B.name();
}


ConfigVar & operator << (ConfigVar &dst, const rel::Interface &iface)
{
    dst << iface.name() << iface.current() << iface.revision() << iface.age();
    return dst;
}

const ConfigVar & operator >> (const ConfigVar &src, Interface &iface)
{
    src >> iface.name();
    src >> iface.current();
    src >> iface.revision();
    src >> iface.age();
    return src;
}

