// MapOp.cpp
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2005

#include "StdAfx.h"
#include "Ceda/Core/cxUtils/ListToOStream.h"
#include "Ceda/Core/cxUtils/PseudoRandom.h"
#include "Ceda/Core/cxUtils/CedaAssert.h"
#include "Ceda/Core/cxUtils/Tracer.h"
#include "MapOp.h"


using namespace ceda;
extern bool gp_debug;

ssize_t GetRandomIntValueForMap()
{
    return GetUniformDistInteger_ssize_t(5, 10);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// MapDoc

void MapDoc::SetRandom(RandomInitSettings ris)
{
    ssize_t n = GetUniformDistInteger_ssize_t(ris.m_minChars, ris.m_maxChars+1);
    for (ssize_t j=0 ; j < n ; ++j)
    {
        Add(GetRandomIntValueForMap(), GetRandomIntValueForMap());
    }
}

void MapDoc::Add(ssize_t v1, ssize_t v2)
{
    m_value[v1] = v2;
}

void MapDoc::Sub(ssize_t v1)
{
    std::map<ssize_t,ssize_t>::iterator i = m_value.find(v1);
    if (i != m_value.end())
    {
        m_value.erase(i);
    }
}

ssize_t MapDoc::PickRandomKey() const
{
    cxAssert(m_value.size() > 0);
    ssize_t n = GetUniformDistInteger_ssize_t(0,m_value.size());

    std::map<ssize_t,ssize_t>::const_iterator p = m_value.begin();
    for (ssize_t i=0 ; i < n ; ++i)
    {
        cxAssert(p != m_value.end());
        ++p;
    }

    cxAssert(p != m_value.end());
    return p->first;
}

void MapDoc::Write(xostream& os) const
{
    os << m_value;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// MapDocMap

void MapDocMap::InitRandomDocs(RandomInitSettings ris)
{
    for (iterator i = begin() ; i != end() ; ++i)
    {
        i->SetRandom(ris);
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// MapOp

MapOp::MapOp() : 
    doc(-1),
    ins(false),
    dc(0),
    v1(-1),
    v2(-1)
{
}

bool MapOp::operator==(const MapOp& rhs) const
{ 
    return m_opid == rhs.m_opid &&
           doc == rhs.doc &&
           ins == rhs.ins &&
           dc == rhs.dc &&
           v1 == rhs.v1 &&
           v2 == rhs.v2;
}

void MapOp::SetRandom(RandomInitSettings ris, const MapDocMap& ds, Opid opid)
{
    cxAssert(opid.id >= 0);
    cxAssert(opid.t >= 0);
    m_opid = opid;

    ssize_t numDocs = ds.size();
    cxAlwaysAssert(numDocs > 0);

    doc = GetUniformDistInteger_ssize_t(0,numDocs);
    dc = 0;
    ins = ds[doc].size() == 0 || GetUniformDistInteger_ssize_t(0,2) > 0;
    if (ins)
    {
        v1 = GetRandomIntValueForMap();
        v2 = GetRandomIntValueForMap();
    }
    else
    {
        v1 = ds[doc].PickRandomKey();
        v2 = -1;
    }
}

void MapOp::Do(MapDocMap& ds)
{
    MapDoc& d = ds[doc];
    if (dc == 0)
    {
        if (ins)
        {
            d.Add(v1,v2);
        }
        else
        {
            d.Sub(v1);
        }
    }
}

void MapOp::Undo(MapDocMap& ds)
{
    // TODO
}

xostream& operator<<(xostream& os, const MapOp& O)
{
    for (ssize_t i=0 ; i < O.dc ; ++i)
    {
        os << '*';
    }
    if (O.ins) os << 'I'; else os << 'D';
    if (O.doc)
    {
        os << O.doc << ':';
    }
    os << O.v1;
    if (O.ins)
    {
        os << '=' << O.v2;
    }
    return os;
}

void IT(MapOp& O1, const MapOp& O2)
{
    cxAlwaysAssert(O1.GetId() != O2.GetId());
    if (O1.doc == O2.doc)
    {
        if (O1.v1 == O2.v1)
        {
            if (O2.dc < O1.dc || O2.dc == O1.dc && O2.GetId() < O1.GetId())
            {
                ++O1.dc;
            }
        }
    }
}

// [O2 O1]
void ET(MapOp& O1, const MapOp& O2)
{
    cxAlwaysAssert(O1.GetId() != O2.GetId());

    if (O1.doc == O2.doc)
    {
        if (O1.v1 == O2.v1)
        {
            if (O2.dc < O1.dc)
            {
                cxAlwaysAssert(O1.dc > 0);
                --O1.dc;
            }
        }
    }
}

void DualIT(MapOp& O1, MapOp& O2)
{
    MapOp copyO1 = O1;
    IT(O1,O2);
    IT(O2,copyO1);    
}

void Transpose(MapOp& O1, MapOp& O2)
{
    ET(O2,O1);
    IT(O1,O2);
}

void Merge(MapOp& O1, const MapOp& O2)
{
    cxAssert(0);
}

