//------------------------------------------------------------------------------
/*
    Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================

#ifndef OPERATION_H
#define OPERATION_H

namespace Script
{

// Contracts are non typed  have variable data types


class Operation
{
public:
    // returns false if there was an error
    virtual bool work (Interpreter* interpreter) = 0;

    virtual int getFee ();

    virtual ~Operation ()
    {
        ;
    }
};

// this is just an Int in the code
class IntOp : public Operation
{
public:
    bool work (Interpreter* interpreter)
    {
        Data::pointer data = interpreter->getIntData ();

        if (data->isInt32 ())
        {
            interpreter->pushStack ( data );
            return (true);
        }

        return (false);
    }
};

class FloatOp : public Operation
{
public:
    bool work (Interpreter* interpreter)
    {
        Data::pointer data = interpreter->getFloatData ();

        if (data->isFloat ())
        {
            interpreter->pushStack ( data );
            return (true);
        }

        return (false);
    }
};

class Uint160Op : public Operation
{
public:
    bool work (Interpreter* interpreter)
    {
        Data::pointer data = interpreter->getUint160Data ();

        if (data->isUint160 ())
        {
            interpreter->pushStack ( data );
            return (true);
        }

        return (false);
    }
};

class AddOp : public Operation
{
public:
    bool work (Interpreter* interpreter)
    {
        Data::pointer data1 = interpreter->popStack ();
        Data::pointer data2 = interpreter->popStack ();

        if ( (data1->isInt32 () || data1->isFloat ()) &&
                (data2->isInt32 () || data2->isFloat ()) )
        {
            if (data1->isFloat () || data2->isFloat ()) interpreter->pushStack (Data::pointer (new FloatData (data1->getFloat () + data2->getFloat ())));
            else interpreter->pushStack (Data::pointer (new IntData (data1->getInt () + data2->getInt ())));

            return (true);
        }
        else
        {
            return (false);
        }
    }
};

class SubOp : public Operation
{
public:
    bool work (Interpreter* interpreter)
    {
        Data::pointer data1 = interpreter->popStack ();
        Data::pointer data2 = interpreter->popStack ();

        if ( (data1->isInt32 () || data1->isFloat ()) &&
                (data2->isInt32 () || data2->isFloat ()) )
        {
            if (data1->isFloat () || data2->isFloat ()) interpreter->pushStack (Data::pointer (new FloatData (data1->getFloat () - data2->getFloat ())));
            else interpreter->pushStack (Data::pointer (new IntData (data1->getInt () - data2->getInt ())));

            return (true);
        }
        else
        {
            return (false);
        }
    }
};

class MulOp : public Operation
{
public:
    bool work (Interpreter* interpreter)
    {
        Data::pointer data1 = interpreter->popStack ();
        Data::pointer data2 = interpreter->popStack ();

        if ( (data1->isInt32 () || data1->isFloat ()) &&
                (data2->isInt32 () || data2->isFloat ()) )
        {
            if (data1->isFloat () || data2->isFloat ()) interpreter->pushStack (Data::pointer (new FloatData (data1->getFloat ()*data2->getFloat ())));
            else interpreter->pushStack (Data::pointer (new IntData (data1->getInt ()*data2->getInt ())));

            return (true);
        }
        else
        {
            return (false);
        }
    }
};

class DivOp : public Operation
{
public:
    bool work (Interpreter* interpreter)
    {
        Data::pointer data1 = interpreter->popStack ();
        Data::pointer data2 = interpreter->popStack ();

        if ( (data1->isInt32 () || data1->isFloat ()) &&
                (data2->isInt32 () || data2->isFloat ()) )
        {
            if (data1->isFloat () || data2->isFloat ()) interpreter->pushStack (Data::pointer (new FloatData (data1->getFloat () / data2->getFloat ())));
            else interpreter->pushStack (Data::pointer (new IntData (data1->getInt () / data2->getInt ())));

            return (true);
        }
        else
        {
            return (false);
        }
    }
};

class GtrOp : public Operation
{
public:
    bool work (Interpreter* interpreter)
    {
        Data::pointer data1 = interpreter->popStack ();
        Data::pointer data2 = interpreter->popStack ();

        if ( (data1->isInt32 () || data1->isFloat ()) &&
                (data2->isInt32 () || data2->isFloat ()) )
        {
            interpreter->pushStack (Data::pointer (new BoolData (data1->getFloat () > data2->getFloat ())));
            return (true);
        }
        else
        {
            return (false);
        }
    }
};

class LessOp : public Operation
{
public:
    bool work (Interpreter* interpreter)
    {
        Data::pointer data1 = interpreter->popStack ();
        Data::pointer data2 = interpreter->popStack ();

        if ( (data1->isInt32 () || data1->isFloat ()) &&
                (data2->isInt32 () || data2->isFloat ()) )
        {
            interpreter->pushStack (Data::pointer (new FloatData (data1->getFloat () < data2->getFloat ())));
            return (true);
        }
        else
        {
            return (false);
        }
    }
};

class ModOp : public Operation
{
public:
    bool work (Interpreter* interpreter)
    {
        Data::pointer data1 = interpreter->popStack ();
        Data::pointer data2 = interpreter->popStack ();

        if ( data1->isInt32 () && data2->isInt32 () )
        {
            interpreter->pushStack (Data::pointer (new IntData (data1->getInt () % data2->getInt ())));
            return (true);
        }
        else
        {
            return (false);
        }
    }
};


class StartBlockOp : public Operation
{
public:
    bool work (Interpreter* interpreter)
    {
        Data::pointer offset = interpreter->getIntData ();
        return (interpreter->startBlock (offset->getInt ()));
    }
};

class EndBlockOp : public Operation
{
public:
    bool work (Interpreter* interpreter)
    {
        return (interpreter->endBlock ());
    }
};

class StopOp : public Operation
{
public:
    bool work (Interpreter* interpreter)
    {
        interpreter->stop ();
        return (true);
    }
};

class AcceptDataOp : public Operation
{
public:
    bool work (Interpreter* interpreter)
    {
        Data::pointer data = interpreter->popStack ();

        if (data->isInt32 ())
        {
            interpreter->pushStack ( interpreter->getAcceptData (data->getInt ()) );
            return (true);
        }

        return (false);
    }
};

class JumpIfOp : public Operation
{
public:
    bool work (Interpreter* interpreter)
    {
        Data::pointer offset = interpreter->getIntData ();
        Data::pointer cond = interpreter->popStack ();

        if (cond->isBool () && offset->isInt32 ())
        {
            if (cond->isTrue ())
            {
                return (interpreter->jumpTo (offset->getInt ()));
            }

            return (true);
        }

        return (false);
    }
};

class JumpOp : public Operation
{
public:
    bool work (Interpreter* interpreter)
    {
        Data::pointer offset = interpreter->getIntData ();

        if (offset->isInt32 ())
        {
            return (interpreter->jumpTo (offset->getInt ()));
        }

        return (false);
    }
};

class SendXRPOp  : public Operation
{
public:
    bool work (Interpreter* interpreter)
    {
        Data::pointer sourceID = interpreter->popStack ();
        Data::pointer destID = interpreter->popStack ();
        Data::pointer amount = interpreter->popStack ();

        if (sourceID->isUint160 () && destID->isUint160 () && amount->isInt32 () && interpreter->canSign (sourceID->getUint160 ()))
        {
            // make sure:
            // source is either, this contract, issuer, or acceptor

            // TODO do the send
            //interpreter->pushStack( send result);

            return (true);
        }

        return (false);
    }
};

class GetDataOp : public Operation
{
public:
    bool work (Interpreter* interpreter)
    {
        Data::pointer index = interpreter->popStack ();

        if (index->isInt32 ())
        {
            interpreter->pushStack ( interpreter->getContractData (index->getInt ()));
            return (true);
        }

        return (false);
    }
};

}

#endif

// vim:ts=4

