// SingleCharEffectsMoveOp2.cpp
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2006

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

extern bool gp_debug;

using namespace ceda;


///////////////////////////////////////////////////////////////////////////////////////////////////
// SingleCharEffectsMoveOp2

SingleCharEffectsMoveOp2::SingleCharEffectsMoveOp2() : 
    e(-1),
    si(-1), sq(-1),
    di(-1), dq(-1),
    c('?')
{
}

bool SingleCharEffectsMoveOp2::operator==(const SingleCharEffectsMoveOp2& rhs) const
{ 
    return m_opid == rhs.m_opid &&
           e == rhs.e &&
           si == rhs.si &&
           sq == rhs.sq &&
           di == rhs.di &&
           dq == rhs.dq &&
           c == rhs.c;
}

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

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

    while(1)
    {
        // Choose a source document that is non empty
        si = GetUniformDistInteger_ssize_t(0,numDocs);
        if (ds[si].sizeq() > 0)
        {
            // Pick a source character
            sq = GetUniformDistInteger_ssize_t(0,ds[si].sizeq());

            if (ds[si].GetFlag(sq)) break;
        }
    }

    // Choose a destination document
    di = GetUniformDistInteger_ssize_t(0,numDocs);

    // Pick a destination position.  Note that there is no actual extraction so we don't have to
    // worry about self-interference.
    dq = GetUniformDistInteger_ssize_t(0,ds[di].sizeq() + 1);

    c = ds[si][sq];
    e = 0;

    // Extraction must be in post-insertion coords
    if (di == si && dq <= sq) ++sq;
}

void SingleCharEffectsMoveOp2::Do(EffectsDocSet& ds) const
{
    ds[di].Insert(dq,c,e == 0);

    cxAlwaysAssert(c == ds[si][sq]);
    cxAlwaysAssert(ds[si].GetFlag(sq));

    if (e == 0)
    {
        cxAlwaysAssert(c == ds[si].Remove(sq));
    }
}

void SingleCharEffectsMoveOp2::Undo(EffectsDocSet& ds) const
{
    cxAlwaysAssert(0);
}

xostream& operator<<(xostream& os, const SingleCharEffectsMoveOp2& O)
{
    os << '\'' << O.c << '\'';

    os << O.e << ';';

    if (O.si)
    {
        os << O.si << ':';
    }
    os << O.sq;
    
    os << " -> ";

    if (O.di)
    {
        os << O.di << ':';
    }
    os << O.dq;
    
    return os;
}

void IT(SingleCharEffectsMoveOp2& O1, const SingleCharEffectsMoveOp2& O2)
{
    cxAlwaysAssert(O1.GetId() != O2.GetId());

    SingleCharEffectsMoveOp2 copyO2 = O2;
    DualIT(O1, copyO2);

    /*
    ssize_t O2sq = O2.sq;
    ssize_t O2dq = O2.dq;

    if (O1.di == O2.di)
    {
        if (O2dq < O1.dq || O2dq == O1.dq && O2.GetId() < O1.GetId()) ++O1.dq; else ++O2dq;
    }

    if (O1.di == O2.si && O1.dq <= O2sq) ++O2sq;
    if (O2.di == O1.si && O2dq <= O1.sq) ++O1.sq;
    if (O1.si == O2.si && O1.sq == O2sq)
    {
        if (O2.e == 0) { O1.si = O2.di; O1.sq = O2dq; }
        if (O2.e < O1.e || O2.e == O1.e && O2.GetId() < O1.GetId()) ++O1.e;
    }
    */
}

void ET(SingleCharEffectsMoveOp2& O1, const SingleCharEffectsMoveOp2& O2)
{
    cxAlwaysAssert(O1.GetId() != O2.GetId());
    SingleCharEffectsMoveOp2 copyO2 = O2;
    Transpose(copyO2, O1);
}

void DualIT(SingleCharEffectsMoveOp2& O1, SingleCharEffectsMoveOp2& O2)
{
    cxAlwaysAssert(O1.GetId() != O2.GetId());

    /*
    SingleCharEffectsMoveOp2 copyO1 = O1;
    IT(O1,O2);
    IT(O2,copyO1);    
    */

    if (O1.di == O2.di)
    {
        if (O2.dq < O1.dq || O2.dq == O1.dq && O2.GetId() < O1.GetId()) ++O1.dq; else ++O2.dq;
    }
    if (O1.di == O2.si && O1.dq <= O2.sq) ++O2.sq;
    if (O2.di == O1.si && O2.dq <= O1.sq) ++O1.sq;
    if (O1.si == O2.si && O1.sq == O2.sq)
    {
        if (O1.e == 0) { O2.si = O1.di; O2.sq = O1.dq; }
        if (O2.e == 0) { O1.si = O2.di; O1.sq = O2.dq; }
        if (O2.e < O1.e || O2.e == O1.e && O2.GetId() < O1.GetId()) ++O1.e; else ++O2.e;
    }
}

void Transpose(SingleCharEffectsMoveOp2& O1, SingleCharEffectsMoveOp2& O2)
{
    cxAlwaysAssert(O1.GetId() != O2.GetId());
    ssize_t prev2dq = O2.dq;
    if (O2.di == O1.si && O2.dq <= O1.sq) ++O1.sq;
    if (O1.di == O2.di) { if (O1.dq < O2.dq) --O2.dq; else ++O1.dq; }
    if (O1.si == O2.si && O1.sq == O2.sq || O1.di == O2.si && O1.dq == O2.sq)
    {
        if (O1.e == 0) { O2.si = O1.si; O2.sq = O1.sq; }
        if (O1.e < O2.e) --O2.e; else ++O1.e;
        cxAlwaysAssert(O2.e >= 0);
        if (O2.e == 0) { O1.si = O2.di; O1.sq = prev2dq; }
    }
    if (O1.di == O2.si && O1.dq < O2.sq) --O2.sq;
}

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


