#include <wasm-rt.h>

#include <iostream>

namespace fugg {
    namespace pointer {
        using BasePointer = unsigned char*;

        // Absolute pointer.
        template <typename Type, typename ModulePointerType>
        class Pointer {
            const ModulePointerType offset_;       

            // `read` interface of this library.
            Type* get() const { 
                return offset_ ? reinterpret_cast<Type*>(offset_) : nullptr; 
            }

        public:
            explicit Pointer(ModulePointerType offset) : offset_(offset) { }
            explicit Pointer(Type *const &offset) : offset_(reinterpret_cast<ModulePointerType>(offset)) { }

            Type *operator->() {
                return this->get();
            }

            // Type &operator*() const {
            //     return *this->get();
            // }
            operator bool() {
                return this->get() != nullptr;
            }

            operator Type*() const { 
                return this->get();
            }
        };

        // A pointer that is located in a `wasm_rt_memory_t`.
        template <typename Type, typename ModulePointerType, wasm_rt_memory_t *const *memory>
        class OffsetPointer {
            const ModulePointerType offset_;       

            Type* get() const { 
                if(this->offset_) {
                    const auto start = reinterpret_cast<ModulePointerType>((*memory)->data);

                    return reinterpret_cast<Type*>(start + this->offset_);
                } else {
                    return nullptr;
                }
            }
        
        public:
            explicit OffsetPointer(const ModulePointerType& offset) : offset_(offset) { }
            explicit OffsetPointer(Type *const &offset) : offset_(reinterpret_cast<ModulePointerType>(offset)) { }

            // FIXME: This is specificly for sending pointers to the module.
            ModulePointerType as_raw() const {
                return this->offset_;
            }

            ModulePointerType as_pointer() const {
                return reinterpret_cast<ModulePointerType>(this->get());
            }

            // Type *operator->() {
            //     return this->get();
            // }

            // Type &operator*() const {
            //     return *this->get();
            // }

            operator Type*() const { 
                return this->get();
            }
        };

        template <typename Type, typename ModulePointerType, wasm_rt_memory_t *const *memory>
        class RelativePointer {
            const ModulePointerType offset_;       

            Type* get() const {
                if(this->offset_) {
                    const auto start = reinterpret_cast<ModulePointerType>((*memory)->data);

                    return reinterpret_cast<Type*>(this->offset_ - start);
                } else {
                    return nullptr;
                }
            }

        public:
            explicit RelativePointer(const ModulePointerType& offset) : offset_(offset) { }
            explicit RelativePointer(Type *const &offset) : offset_(reinterpret_cast<ModulePointerType>(offset)) { }

            // FIXME: This is specificly for sending pointers to the module.
            ModulePointerType as_raw() const {
                const auto start = reinterpret_cast<ModulePointerType>((*memory)->data);

                return this->offset_ - start;
            }


            Type *operator->() {
                return this->get();
            }

            // Type &operator*() const {
            //     return *this->get();
            // }

            operator Type*() const { 
                return this->get();
            }
        };



    }
}