
#include "SequenceNumber.h"
#include <ctime>
#include <limits>

namespace ariba {
namespace overlay {

/** static initializers **/
// DISABLED_SEQNUM const
const SequenceNumber SequenceNumber::DISABLED;
    
// seed the RNG
boost::mt19937 SequenceNumber::rng_32bit(std::time(NULL));

boost::uniform_int<uint32_t> SequenceNumber::seqnum_distribution_32bit(
    MIN_SEQ_NUM, std::numeric_limits<uint32_t>::max());

boost::uniform_int<uint32_t> SequenceNumber::distribution_full32bit(
    0, std::numeric_limits<uint32_t>::max());



/** class implementation **/
SequenceNumber::SequenceNumber():
    seqnum_32(SEQ_NUM_DISABLED),
    seqnum_64(SEQ_NUM_DISABLED)
{
}
SequenceNumber::SequenceNumber(uint32_t seqnum):
    seqnum_32(seqnum),
    seqnum_64(SEQ_NUM_DISABLED)
{
}
SequenceNumber::SequenceNumber(uint64_t seqnum):
    seqnum_32(SEQ_NUM_DISABLED),
    seqnum_64(seqnum)
{
}


SequenceNumber SequenceNumber::createRandomSeqNum_Short() 
{
    uint32_t num = seqnum_distribution_32bit(rng_32bit);
    return SequenceNumber(num);
}

SequenceNumber SequenceNumber::createRandomSeqNum_Long() 
{
    uint64_t num = distribution_full32bit(rng_32bit);
    num << 32;
    num += seqnum_distribution_32bit(rng_32bit);
    
    return SequenceNumber(num);
}

bool SequenceNumber::isShortSeqNum() const 
{
    return seqnum_32 >= MIN_SEQ_NUM && seqnum_64 == SEQ_NUM_DISABLED;
}

bool SequenceNumber::isLongSeqNum() const 
{
    return seqnum_32 == SEQ_NUM_DISABLED && seqnum_64 >= MIN_SEQ_NUM;
}

bool SequenceNumber::isDisabled() const 
{
    return seqnum_32 == SEQ_NUM_DISABLED && seqnum_64 == SEQ_NUM_DISABLED;
}

bool SequenceNumber::isValid() const 
{
    return isShortSeqNum() || isLongSeqNum();
}

void SequenceNumber::increment() 
{
    // BRANCH: short seqnum
    if ( isShortSeqNum() )
    {
        seqnum_32++;
        
        // wrap overflow
        if ( seqnum_32 < MIN_SEQ_NUM )
        {
            seqnum_32 = MIN_SEQ_NUM;
        }
    }
    
    // BRANCH: long seqnum
    else if ( isLongSeqNum() )
    {
        seqnum_64++;
        
        // wrap overflow
        if ( seqnum_64 < MIN_SEQ_NUM )
        {
            seqnum_64 = MIN_SEQ_NUM;
        }
    }
}

bool SequenceNumber::operator==(const SequenceNumber& rhs) const 
{
    return seqnum_32 + seqnum_64 == rhs.seqnum_32 + rhs.seqnum_64;
}

bool SequenceNumber::operator<(const SequenceNumber& rhs) const 
{
    return seqnum_32 + seqnum_64 < rhs.seqnum_32 + rhs.seqnum_64;
}

bool SequenceNumber::isSuccessor(const SequenceNumber& rhs) 
{
    // TODO implement
    
    return false;
}

bool SequenceNumber::isPredecessor(const SequenceNumber& rhs) 
{
    // TODO implement
    
    return false;
}

uint32_t SequenceNumber::getShortSeqNum() const 
{
    return seqnum_32;
}

uint64_t SequenceNumber::getLongSeqNum() const 
{
    return seqnum_64;
}


std::ostream& operator<<(std::ostream& stream, const SequenceNumber& rhs) 
{
    if ( rhs.isDisabled() )
    {
        return stream << "DISABLED";
    }
    
    else if ( ! rhs.isValid() )
    {
        return stream << "INVALID";
    }
    
    else if ( rhs.isShortSeqNum() )
    {
        return stream << rhs.seqnum_32;
    }
    
    else if ( rhs.isLongSeqNum() )
    {
        return stream << rhs.seqnum_64;
    }
    
    else
    {
        return stream << "ERROR";
    }
}

}} // [namespace ariba::overlay]
