#pragma once

#include <spider/runtime/reel/InstrReel.hpp>

namespace spider {

    /**
     * Implements an instruction reel.
     */
    class InstrReelDyn : public InstrReel {
    private:

        struct ReelBlock {
            u8 data[256] = {};
        };

    private:

        deque<ReelBlock> _blocks;
        u64 _size;

    public:

        InstrReelDyn(u64 length);

        InstrReelDyn(const u8* data, u64 length);

        InstrReelDyn(const InstrReelDyn& copy);

        InstrReelDyn(InstrReelDyn&& move) noexcept;

        virtual ~InstrReelDyn();

    public:

        InstrReelDyn& operator=(const InstrReelDyn& copy);

        InstrReelDyn& operator=(InstrReelDyn&& move) noexcept;

    private:

        std::pair<u64, u8> indexOf(u64 ip);

        bool continous(u64 ip0, u64 ip1, u64* b_index, u16* s_index);

        void growTo(u64 ip);

    public:

        /**
         * Obtains a byte of data at
         * the specific location.
         * Reindexing may occur, continous access
         * may incurr in less penalties.
         */
        virtual u8 readU8(u64 ip) override;

        /**
         * Obtains a byte of data at
         * the specific location.
         * Reindexing may occur, continous access
         * may incurr in less penalties.
         */
        virtual u16 readU16(u64 ip) override;

        /**
         * Obtains a byte of data at
         * the specific location.
         * Reindexing may occur, continous access
         * may incurr in less penalties.
         */
        virtual u32 readU32(u64 ip) override;

        /**
         * Obtains a byte of data at
         * the specific location.
         * Reindexing may occur, continous access
         * may incurr in less penalties.
         */
        virtual u64 readU64(u64 ip) override;
        
        /**
         * Reads a range of data, and
         * outputs it.
         */
        virtual void readRange(u64 ip, u8* out, u64 length) override;

        /**
         * Current size of the instructions.
         */
        virtual u64 size() override;

    public:

        void writeU8(u64 ip, u8 dat);

        void writeU16(u64 ip, u16 dat);

        void writeU32(u64 ip, u32 dat);

        void writeU64(u64 ip, u64 dat);

        /**
         * Appends instruction at the end.
         */
        void append(u16 bc);

    };

}
