#include "TestBlock.h"
#include <retesteth/helpers/TestHelper.h>
using namespace std;

namespace test::blockchainfiller
{
// Raw block RLP, that cann not be generated by retesteth
TestBlock::TestBlock(BYTES const& _rlp, string const& _chainName, FORK const& _chainNet, VALUE const& _number)
  : m_chainName(_chainName), m_doNotExport(false)
{
    m_blockNumber = spVALUE(_number.copy());
    m_chainNet = spFORK(new FORK(_chainNet.asString()));
    m_rawRLP = spBYTES(_rlp.copy());
}

void TestBlock::exportRLPDecodedToData(BYTES const& _rawRLP, DataObject& _res)
{
    dev::bytes const decodeRLP = sfromHex(_rawRLP.asString());
    dev::RLP const rlp(decodeRLP, dev::RLP::VeryStrict);
    spBlockHeader blockH = readBlockHeader(rlp[0]);

    _res["rlp_decoded"].atKeyPointer("blockHeader") = blockH->asDataObject();
    _res["rlp_decoded"].atKeyPointer("transactions") = sDataObject(DataType::Array);
    for (auto const& trRLP : rlp[1].toList())
    {
        spTransaction spTr = readTransaction(trRLP);
        _res["rlp_decoded"]["transactions"].addArrayObject(spTr->asDataObject());
    }
    _res["rlp_decoded"].atKeyPointer("uncleHeaders") = sDataObject(DataType::Array);
    for (auto const& unRLP : rlp[2].toList())
    {
        spBlockHeader spUn = readBlockHeader(unRLP);
        _res["rlp_decoded"]["uncleHeaders"].addArrayObject(spUn->asDataObject());
    }
    _res["rlp_decoded"].atKeyPointer("withdrawals") = sDataObject(DataType::Array);
    if (rlp.itemCount() > 3)
    {
        for (auto const& wtRLP : rlp[3].toList())
        {
            spWithdrawal wt(new Withdrawal(wtRLP));
            _res["rlp_decoded"]["withdrawals"].addArrayObject(wt->asDataObject());
        }
    }
}

spDataObject TestBlock::asDataObject() const
{
    spDataObject _res;
    DataObject& res = _res.getContent();
    res["chainname"] = m_chainName;
    // res["chainnetwork"] = m_chainNet->asString();
    res["blocknumber"] = m_blockNumber->asDecString();
    if (!m_expectException.empty())
        res["expectException"] = m_expectException;

    // No test objects was registered
    if (!m_block.isEmpty())
    {
        res.atKeyPointer("uncleHeaders") = spDataObject(new DataObject(DataType::Array));
        for (auto const& un : m_block->uncles())
            res["uncleHeaders"].addArrayObject(un->asDataObject());
        res.atKeyPointer("blockHeader") = m_block->header()->asDataObject();
        res.atKeyPointer("transactions") = spDataObject(new DataObject(DataType::Array));
        for (auto const& tr : m_block->transactions())
            res["transactions"].addArrayObject(tr->asDataObject());

        if (isBlockExportWithdrawals(m_block->header()))
            res.atKeyPointer("withdrawals") = spDataObject(new DataObject(DataType::Array));

        // Print withdrawals anyway if it's present
        for (auto const& wt : m_block->withdrawals())
            res["withdrawals"].addArrayObject((wt->asDataObject()));

        for (auto const& trSequence : m_transactionExecOrder)
        {
            spDataObject _trInfo;
            DataObject& trInfo = _trInfo.getContent();
            BYTES const& b = std::get<0>(trSequence);
            string const& v = std::get<1>(trSequence);
            trInfo["rawBytes"] = b.asString();
            if (v.empty())
                trInfo["valid"] = "true";
            else
            {
                trInfo["valid"] = "false";
                trInfo["exception"] = v;
            }
            res["transactionSequence"].addArrayObject(_trInfo);
        }
    }
    else
    {
        try
        {
            TestBlock::exportRLPDecodedToData(m_rawRLP, res);
        }
        catch (std::exception const& _ex)
        {
            ETH_ERROR_MESSAGE(string() + "Failed to parse invalid block's RLP, make sure the RLP structure is valid! " + _ex.what());
        }
    }

    res["rlp"] = m_rawRLP->asString();
    if (m_hasBigInt)
        res["hasBigInt"] = "true";
    return _res;
}
}  // namespace test::blockchainfiller
