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

#ifndef RIPPLE_LEDGER_H
#define RIPPLE_LEDGER_H

class Job;

enum LedgerStateParms
{
    lepNONE         = 0,    // no special flags

    // input flags
    lepCREATE       = 1,    // Create if not present

    // output flags
    lepOKAY         = 2,    // success
    lepMISSING      = 4,    // No node in that slot
    lepWRONGTYPE    = 8,    // Node of different type there
    lepCREATED      = 16,   // Node was created
    lepERROR        = 32,   // error
};

#define LEDGER_JSON_DUMP_TXRP   0x10000000
#define LEDGER_JSON_DUMP_STATE  0x20000000
#define LEDGER_JSON_EXPAND      0x40000000
#define LEDGER_JSON_FULL        0x80000000

class SqliteStatement;

// VFALCO TODO figure out exactly how this thing works.
//         It seems like some ledger database is stored as a global, static in the
//         class. But then what is the meaning of a Ledger object? Is this
//         really two classes in one? StoreOfAllLedgers + SingleLedgerObject?
//
class Ledger
    : public boost::enable_shared_from_this <Ledger>
    , public CountedObject <Ledger>
    , public Uncopyable
{
public:
    static char const* getCountedObjectName () { return "Ledger"; }

    typedef boost::shared_ptr<Ledger>           pointer;
    typedef const boost::shared_ptr<Ledger>&    ref;

    enum TransResult
    {
        TR_ERROR    = -1,
        TR_SUCCESS  = 0,
        TR_NOTFOUND = 1,
        TR_ALREADY  = 2,
        TR_BADTRANS = 3,    // the transaction itself is corrupt
        TR_BADACCT  = 4,    // one of the accounts is invalid
        TR_INSUFF   = 5,    // the sending(apply)/receiving(remove) account is broke
        TR_PASTASEQ = 6,    // account is past this transaction
        TR_PREASEQ  = 7,    // account is missing transactions before this
        TR_BADLSEQ  = 8,    // ledger too early
        TR_TOOSMALL = 9,    // amount is less than Tx fee
    };

    // ledger close flags
    static const uint32 sLCF_NoConsensusTime = 1;

public:
    Ledger (const RippleAddress & masterID, uint64 startAmount); // used for the starting bootstrap ledger

    Ledger (uint256 const & parentHash, uint256 const & transHash, uint256 const & accountHash,
            uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution,
            uint32 ledgerSeq, bool & loaded); // used for database ledgers

    Ledger (Blob const & rawLedger, bool hasPrefix);

    Ledger (const std::string & rawLedger, bool hasPrefix);

    Ledger (bool dummy, Ledger & previous); // ledger after this one

    Ledger (Ledger & target, bool isMutable); // snapshot

    static Ledger::pointer getSQL (const std::string & sqlStatement);
    static Ledger::pointer getSQL1 (SqliteStatement*);
    static void getSQL2 (Ledger::ref);
    static Ledger::pointer getLastFullLedger ();
    static uint32 roundCloseTime (uint32 closeTime, uint32 closeResolution);

    void updateHash ();
    void setClosed ()
    {
        mClosed = true;
    }
    void setValidated()
    {
        mValidated = true;
    }
    void setAccepted (uint32 closeTime, int closeResolution, bool correctCloseTime);
    void setAccepted ();
    void setImmutable ();
    bool isClosed ()
    {
        return mClosed;
    }
    bool isAccepted ()
    {
        return mAccepted;
    }
    bool isValidated ()
    {
        return mValidated;
    }
    bool isImmutable ()
    {
        return mImmutable;
    }
    bool isFixed ()
    {
        return mClosed || mImmutable;
    }
    void setFull ()
    {
        mTransactionMap->setLedgerSeq (mLedgerSeq);
        mAccountStateMap->setLedgerSeq (mLedgerSeq);
    }

    // ledger signature operations
    void addRaw (Serializer & s) const;
    void setRaw (Serializer & s, bool hasPrefix);

    uint256 getHash ();
    uint256 const& getParentHash () const
    {
        return mParentHash;
    }
    uint256 const& getTransHash () const
    {
        return mTransHash;
    }
    uint256 const& getAccountHash () const
    {
        return mAccountHash;
    }
    uint64 getTotalCoins () const
    {
        return mTotCoins;
    }
    void destroyCoins (uint64 fee)
    {
        mTotCoins -= fee;
    }
    uint32 getCloseTimeNC () const
    {
        return mCloseTime;
    }
    uint32 getParentCloseTimeNC () const
    {
        return mParentCloseTime;
    }
    uint32 getLedgerSeq () const
    {
        return mLedgerSeq;
    }
    int getCloseResolution () const
    {
        return mCloseResolution;
    }
    bool getCloseAgree () const
    {
        return (mCloseFlags & sLCF_NoConsensusTime) == 0;
    }

    // close time functions
    void setCloseTime (uint32 ct)
    {
        assert (!mImmutable);
        mCloseTime = ct;
    }
    void setCloseTime (boost::posix_time::ptime);
    boost::posix_time::ptime getCloseTime () const;

    // low level functions
    SHAMap::ref peekTransactionMap ()
    {
        return mTransactionMap;
    }
    SHAMap::ref peekAccountStateMap ()
    {
        return mAccountStateMap;
    }
    void dropCache ()
    {
        assert (isImmutable ());

        if (mTransactionMap)
            mTransactionMap->dropCache ();

        if (mAccountStateMap)
            mAccountStateMap->dropCache ();
    }

    // ledger sync functions
    void setAcquiring (void);
    bool isAcquiring (void);
    bool isAcquiringTx (void);
    bool isAcquiringAS (void);

    // Transaction Functions
    bool addTransaction (uint256 const & id, const Serializer & txn);
    bool addTransaction (uint256 const & id, const Serializer & txn, const Serializer & metaData);
    bool hasTransaction (uint256 const & TransID) const
    {
        return mTransactionMap->hasItem (TransID);
    }
    Transaction::pointer getTransaction (uint256 const & transID) const;
    bool getTransaction (uint256 const & transID, Transaction::pointer & txn, TransactionMetaSet::pointer & txMeta);
    bool getTransactionMeta (uint256 const & transID, TransactionMetaSet::pointer & txMeta);
    bool getMetaHex (uint256 const & transID, std::string & hex);

    static SerializedTransaction::pointer getSTransaction (SHAMapItem::ref, SHAMapTreeNode::TNType);
    SerializedTransaction::pointer getSMTransaction (SHAMapItem::ref, SHAMapTreeNode::TNType,
            TransactionMetaSet::pointer & txMeta);

    // high-level functions
    bool hasAccount (const RippleAddress & acctID);
    AccountState::pointer getAccountState (const RippleAddress & acctID);
    LedgerStateParms writeBack (LedgerStateParms parms, SLE::ref);
    SLE::pointer getAccountRoot (const uint160 & accountID);
    SLE::pointer getAccountRoot (const RippleAddress & naAccountID);
    void updateSkipList ();
    void visitAccountItems (const uint160 & acctID, FUNCTION_TYPE<void (SLE::ref)>);

    // database functions (low-level)
    static Ledger::pointer loadByIndex (uint32 ledgerIndex);
    static Ledger::pointer loadByHash (uint256 const & ledgerHash);
    static uint256 getHashByIndex (uint32 index);
    static bool getHashesByIndex (uint32 index, uint256 & ledgerHash, uint256 & parentHash);
    static std::map< uint32, std::pair<uint256, uint256> > getHashesByIndex (uint32 minSeq, uint32 maxSeq);
    bool pendSaveValidated (bool isSynchronous, bool isCurrent);

    // next/prev function
    SLE::pointer getSLE (uint256 const & uHash); // SLE is mutable
    SLE::pointer getSLEi (uint256 const & uHash); // SLE is immutable

    // VFALCO NOTE These seem to let you walk the list of ledgers
    //
    uint256 getFirstLedgerIndex ();
    uint256 getLastLedgerIndex ();
    uint256 getNextLedgerIndex (uint256 const & uHash);                         // first node >hash
    uint256 getNextLedgerIndex (uint256 const & uHash, uint256 const & uEnd);   // first node >hash, <end
    uint256 getPrevLedgerIndex (uint256 const & uHash);                         // last node <hash
    uint256 getPrevLedgerIndex (uint256 const & uHash, uint256 const & uBegin); // last node <hash, >begin

    // Ledger hash table function
    static uint256 getLedgerHashIndex ();
    static uint256 getLedgerHashIndex (uint32 desiredLedgerIndex);
    static int getLedgerHashOffset (uint32 desiredLedgerIndex);
    static int getLedgerHashOffset (uint32 desiredLedgerIndex, uint32 currentLedgerIndex);
    uint256 getLedgerHash (uint32 ledgerIndex);
    std::vector< std::pair<uint32, uint256> > getLedgerHashes ();

    static uint256 getLedgerFeatureIndex ();
    static uint256 getLedgerFeeIndex ();
    std::vector<uint256> getLedgerFeatures ();

    std::vector<uint256> getNeededTransactionHashes (int max, SHAMapSyncFilter * filter);
    std::vector<uint256> getNeededAccountStateHashes (int max, SHAMapSyncFilter * filter);

    // index calculation functions
    static uint256 getAccountRootIndex (const uint160 & uAccountID);

    static uint256 getAccountRootIndex (const RippleAddress & account)
    {
        return getAccountRootIndex (account.getAccountID ());
    }

    //
    // Generator Map functions
    //

    SLE::pointer getGenerator (const uint160 & uGeneratorID);

    static uint256 getGeneratorIndex (const uint160 & uGeneratorID);

    //
    // Nickname functions
    //

    static uint256 getNicknameHash (const std::string & strNickname)
    {
        Serializer s (strNickname);
        return s.getSHA256 ();
    }

    NicknameState::pointer getNicknameState (uint256 const & uNickname);
    NicknameState::pointer getNicknameState (const std::string & strNickname)
    {
        return getNicknameState (getNicknameHash (strNickname));
    }

    SLE::pointer getNickname (uint256 const & uNickname);
    SLE::pointer getNickname (const std::string & strNickname)
    {
        return getNickname (getNicknameHash (strNickname));
    }

    static uint256 getNicknameIndex (uint256 const & uNickname);

    //
    // Order book functions
    //

    // Order book dirs have a base so we can use next to step through them in quality order.
    static bool isValidBook (const uint160 & uTakerPaysCurrency, const uint160 & uTakerPaysIssuerID,
                             const uint160 & uTakerGetsCurrency, const uint160 & uTakerGetsIssuerID);

    static uint256 getBookBase (const uint160 & uTakerPaysCurrency, const uint160 & uTakerPaysIssuerID,
                                const uint160 & uTakerGetsCurrency, const uint160 & uTakerGetsIssuerID);

    //
    // Offer functions
    //

    SLE::pointer getOffer (uint256 const & uIndex);

    SLE::pointer getOffer (const uint160 & uAccountID, uint32 uSequence)
    {
        return getOffer (getOfferIndex (uAccountID, uSequence));
    }

    // The index of an offer.
    static uint256 getOfferIndex (const uint160 & uAccountID, uint32 uSequence);

    //
    // Owner functions
    //

    // VFALCO NOTE This is a simple math operation that converts the account ID
    //             into a 256 bit object (I think....need to research this)
    //
    // All items controlled by an account are here: offers
    static uint256 getOwnerDirIndex (const uint160 & uAccountID);

    //
    // Directory functions
    // Directories are doubly linked lists of nodes.

    // Given a directory root and and index compute the index of a node.
    static uint256 getDirNodeIndex (uint256 const & uDirRoot, const uint64 uNodeIndex = 0);
    static void ownerDirDescriber (SLE::ref, const uint160 & owner);

    // Return a node: root or normal
    SLE::pointer getDirNode (uint256 const & uNodeIndex);

    //
    // Quality
    //

    static uint256  getQualityIndex (uint256 const & uBase, const uint64 uNodeDir = 0);
    static uint256  getQualityNext (uint256 const & uBase);
    static uint64   getQuality (uint256 const & uBase);
    static void     qualityDirDescriber (SLE::ref,
                                         const uint160 & uTakerPaysCurrency, const uint160 & uTakerPaysIssuer,
                                         const uint160 & uTakerGetsCurrency, const uint160 & uTakerGetsIssuer,
                                         const uint64 & uRate);

    //
    // Ripple functions : credit lines
    //

    // Index of node which is the ripple state between two accounts for a currency.
    // VFALCO NOTE Rename these to make it clear they are simple functions that
    //             don't access global variables. e.g. "calculateKeyFromRippleStateAndAddress"
    //
    static uint256 getRippleStateIndex (const RippleAddress & naA, const RippleAddress & naB, const uint160 & uCurrency);
    static uint256 getRippleStateIndex (const uint160 & uiA, const uint160 & uiB, const uint160 & uCurrency)
    {
        return getRippleStateIndex (RippleAddress::createAccountID (uiA), RippleAddress::createAccountID (uiB), uCurrency);
    }

    SLE::pointer getRippleState (uint256 const & uNode);

    SLE::pointer getRippleState (const RippleAddress & naA, const RippleAddress & naB, const uint160 & uCurrency)
    {
        return getRippleState (getRippleStateIndex (naA, naB, uCurrency));
    }

    SLE::pointer getRippleState (const uint160 & uiA, const uint160 & uiB, const uint160 & uCurrency)
    {
        return getRippleState (getRippleStateIndex (RippleAddress::createAccountID (uiA), RippleAddress::createAccountID (uiB), uCurrency));
    }

    uint32 getReferenceFeeUnits ()
    {
        if (!mBaseFee) updateFees ();

        return mReferenceFeeUnits;
    }

    uint64 getBaseFee ()
    {
        if (!mBaseFee) updateFees ();

        return mBaseFee;
    }

    uint64 getReserve (int increments)
    {
        if (!mBaseFee) updateFees ();

        return scaleFeeBase (static_cast<uint64> (increments) * mReserveIncrement + mReserveBase);
    }

    uint64 getReserveInc ()
    {
        if (!mBaseFee) updateFees ();

        return mReserveIncrement;
    }

    uint64 scaleFeeBase (uint64 fee);
    uint64 scaleFeeLoad (uint64 fee, bool bAdmin);

    static std::set<uint32> getPendingSaves();

    Json::Value getJson (int options);
    void addJson (Json::Value&, int options);

    bool walkLedger ();
    bool assertSane ();

protected:
    SLE::pointer getASNode (LedgerStateParms & parms, uint256 const & nodeID, LedgerEntryType let);

    // returned SLE is immutable
    SLE::pointer getASNodeI (uint256 const & nodeID, LedgerEntryType let);

    void saveValidatedLedgerAsync(Job&, bool current)
    {
        saveValidatedLedger(current);
    }
    void saveValidatedLedger (bool current);

    void updateFees ();

private:
    // The basic Ledger structure, can be opened, closed, or synching
    // VFALCO TODO eliminate the need for friends
    friend class TransactionEngine;
    friend class Transactor;

    void initializeFees ();

private:
    uint256     mHash;
    uint256     mParentHash;
    uint256     mTransHash;
    uint256     mAccountHash;
    uint64      mTotCoins;
    uint32      mLedgerSeq;
    uint32      mCloseTime;         // when this ledger closed
    uint32      mParentCloseTime;   // when the previous ledger closed
    int         mCloseResolution;   // the resolution for this ledger close time (2-120 seconds)
    uint32      mCloseFlags;        // flags indicating how this ledger close took place
    bool        mClosed, mValidated, mValidHash, mAccepted, mImmutable;

    uint32      mReferenceFeeUnits;                 // Fee units for the reference transaction
    uint32      mReserveBase, mReserveIncrement;    // Reserve basse and increment in fee units
    uint64      mBaseFee;                           // Ripple cost of the reference transaction

    SHAMap::pointer mTransactionMap;
    SHAMap::pointer mAccountStateMap;

    mutable boost::recursive_mutex mLock;

    // ledgers not fully saved, validated ledger present but DB may not be correct yet
    static boost::mutex      sPendingSaveLock;
    static std::set<uint32>  sPendingSaves;
};

inline LedgerStateParms operator| (const LedgerStateParms& l1, const LedgerStateParms& l2)
{
    return static_cast<LedgerStateParms> (static_cast<int> (l1) | static_cast<int> (l2));
}

inline LedgerStateParms operator& (const LedgerStateParms& l1, const LedgerStateParms& l2)
{
    return static_cast<LedgerStateParms> (static_cast<int> (l1) & static_cast<int> (l2));
}

#endif
// vim:ts=4
