#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:

        u64 _use_count;
        isize _block_index;
        std::deque<ReelBlock> _blocks;

    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:

        isize selectIndex(u64 ip);

        void growToFit(isize index);

        ReelBlock* selectBlock(isize index);

    public:

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

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

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

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

    public:

        void at(u64 ip, u8 dat);

        void at(u64 ip, u16 dat);

        void at(u64 ip, u32 dat);

        void at(u64 ip, u64 dat);

        /**
         * Appends instruction at location.
         */
        void append(u64 ip, u16 bc);

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

        /**
         * Removes instruction at location.
         */
        void remove(u64 ip);

    };

}
