// VectorTime.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2008

@import "Opid.h"
@import "Ceda/Core/cxObject/IObject.h"
#include "Ceda/Core/cxUtils/RelocatableMap.h"
#include <map>

namespace ceda
{

class Archive;
class DeltaVectorTime;
@if (mUseSiteIdMap)
{
    class SiteIdMap;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// TVectorTime
/*
template<typename T>
class TVectorTime : public std::map<T,ssize_t>
{
public:
    void Remove(const T& s)
    {
        iterator i = find(s);
        cxAssert(i != end());
        erase(i);
    }

    // Ensure that every t value in the map satisfies t >= 0
    bool IsNonNegative() const
    {
        for (const_iterator i = begin() ; i != end() ; ++i)
        {
            if (i->second < 0) return false;
        }
        return true;
    }

    ssize_t GetCount() const
    {
        ssize_t count = 0;
        for (const_iterator i = begin() ; i != end() ; ++i)
        {
            count += i->second;
        }
        return count;
    }
    
    void Serialise(Archive& ar) const
    {
        if (ar.IsStoring())
        {
            ar << (std::map<SiteNum,GSN>&) *this;
        }
        else
        {
            ar >> (std::map<SiteNum,GSN>&) *this;
        }
    }
};
*/

///////////////////////////////////////////////////////////////////////////////////////////////////
// BaseVectorTime

class @api BaseVectorTime : public RelocatableMap<std::map<SiteNum,GSN> >
{
public:
    void Remove(SiteNum s);

    // Ensure that every t value in the map satisfies t >= 0
    bool IsNonNegative() const;

    ssize_t GetCount() const;
    
    void Serialise(Archive& ar) const;
    InputArchive Deserialise(InputArchive ar);
    
    // deprecated
    void Deserialise(Archive& ar);
};

@api xostream& operator<<(xostream& os, const BaseVectorTime& v);

inline Archive& operator<<(Archive& ar, const BaseVectorTime& v)
{
    v.Serialise(ar);
    return ar;
}

inline InputArchive operator>>(InputArchive ar, BaseVectorTime& v)
{
    return v.Deserialise(ar);
}

// deprecated
inline Archive& operator>>(Archive& ar, BaseVectorTime& v)
{
    v.Deserialise(ar);
    return ar;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// VectorTime

$class+ VectorTime : public BaseVectorTime
{
public:
    void Add(SiteNum s, GSN t);
    VectorTime& operator+=(const DeltaVectorTime& dv);
    void IncrementGSN(SiteNum s);
    ssize_t operator()(SiteNum s) const;

    // Take the union of this vector time with the given rhs
    void UnionWith(const VectorTime& rhs);

    // Is the given (s,t) in the extent of this vector time?
    bool ExtentContains(SiteNum s, ssize_t t) const { return t < (*this)(s); }

    bool ExtentContains(Opid opid) const { return opid.t < (*this)(opid.s); }
};

@api int lexcompare(
    @if (mUseSiteIdMap) {const SiteIdMap& siteIdMap,} 
    const VectorTime& v1, const VectorTime& v2);

// Partial ordering. For vector times v1,v2 we write v1 <= v2 if X(v1) is a subset of X(v2).  
// I.e. for all s in v1,  v1(s) <= v2(s)
@api bool operator<=(const VectorTime& v1, const VectorTime& v2);

// Test whether X(v1) is a strict subset of X(v2)
// I.e. for all s in v1,  v1(s) <= v2(s),  and there exists s in v2 st v1(s) < v2(s)
@api bool operator<(const VectorTime& v1, const VectorTime& v2);


// Calculate vmin = min(v1,v2). Note that this takes the intersection of the extents 
@api void CedaMin(VectorTime& vmin, const VectorTime& v1, const VectorTime& v2);

// Calculate vmin = max(v1,v2).  Note that this takes the union of the extents
@api void CedaMax(VectorTime& vmax, const VectorTime& v1, const VectorTime& v2);

///////////////////////////////////////////////////////////////////////////////////////////////////
// DeltaVectorTime

class DeltaVectorTime : public BaseVectorTime
{
public:
	DeltaVectorTime() {}

    // Calculate dv satisfying v1 + dv = v2
	DeltaVectorTime(const VectorTime& v1, const VectorTime& v2);

    void Add(SiteNum s, GSN t);
    DeltaVectorTime& operator+=(const DeltaVectorTime& dv);
    void RemoveIfExists(SiteNum s, GSN t);
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// DeltaVectorTime2

/*
The functions below satisfy:

    for all v1,v2 with v1 <= v2,  v2 = v1 + (v2-v1)

*/

typedef xvector<Opid> DeltaVectorTime2;

// Assumes v1 <= v2.  Calculates d = v2 - v1
@api void Subtract(DeltaVectorTime2& d, const VectorTime& v2, const VectorTime& v1);

// v += d
@api void Add(VectorTime& v, const DeltaVectorTime2& d);

// Calculate v2 = v1 + d
@api void Add(VectorTime& v2, const VectorTime& v1, const DeltaVectorTime2& d);

///////////////////////////////////////////////////////////////////////////////////////////////////

@if (mUseSiteIdMap)
{
    $class+ FullVectorTime <<os>> : public RelocatableMap<std::map<SiteId,ssize_t> >
    {
    public:
        void Remove(const SiteId& s);
        bool IsNonNegative() const;

        void Write(xostream& os) const;

        void Serialise(Archive& ar) const;
        InputArchive Deserialise(InputArchive ar);

        // deprecated
        void Deserialise(Archive& ar);

        ssize_t GetCount() const;
        ssize_t operator()(SiteId s) const;
        void Add(SiteId s, GSN t);
        void UnionWith(const FullVectorTime& rhs);
    };

    inline Archive& operator<<(Archive& ar, const FullVectorTime& v)
    {
        v.Serialise(ar);
        return ar;
    }

    inline InputArchive operator>>(InputArchive ar, FullVectorTime& v)
    {
        return v.Deserialise(ar);
    }

    // deprecated
    inline Archive& operator>>(Archive& ar, FullVectorTime& v)
    {
        v.Deserialise(ar);
        return ar;
    }

    // Calculate vmin = min(v1,v2). Note that this takes the intersection of the extents 
    @api void CedaMin(FullVectorTime& vmin, const FullVectorTime& v1, const FullVectorTime& v2);

    // Calculate vmin = max(v1,v2).  Note that this takes the union of the extents
    @api void CedaMax(FullVectorTime& vmax, const FullVectorTime& v1, const FullVectorTime& v2);

    $function+ inline int64 Count(const FullVectorTime& v)
    {
        return v.GetCount();
    }
    $function+ inline FullVectorTime Union(const FullVectorTime& v1, const FullVectorTime& v2)
    {
        FullVectorTime v;
        CedaMax(v, v1, v2);
        return v;
    }
    $function+ inline FullVectorTime Intersection(const FullVectorTime& v1, const FullVectorTime& v2)
    {
        FullVectorTime v;
        CedaMin(v, v1, v2);
        return v;
    }

    void InitFullVectorTime(FullVectorTime& dst, const SiteIdMap& siteIdMap, const VectorTime& src);

    void InitVectorTime(VectorTime& dst, SiteIdMap& siteIdMap, const FullVectorTime& src);
}
@else
{
    $typedef VectorTime FullVectorTime;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// When we are interested in U = X(v2) \ X(v1) = { op(s,t) | v1(s) <= t < v2(s) }
// it is useful to compute P = { (s,v1(s)) | v1(s) < v2(s) } - because it represents a useful 
// summary of U.
// We use a delta vector time because P may contain (s,0) entries.
// This is useful when
// 1.   Calculating the upload position after exchanging HV's when two computers first connect
// 2.   Finding the left most operation that is not in the context of an operation received from 
//      a remote site 
@api void ComputeMissingSet(const VectorTime& v1, const VectorTime& v2, DeltaVectorTime& P);

} // namespace ceda
