#include "memory.h"

#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <string>

namespace fugg {
    namespace detail {
        template<typename I, typename O>
        struct ArrowProxy {
            I value;

            O* operator->() {
                return reinterpret_cast<O*>(&value);
            }

            O& operator*() {
                return *(operator->)();
            }
        };

        struct RawVector {
            uintptr_t begin;
            uintptr_t end;
            uintptr_t end_capacity;
        };

        template<typename T>
        struct RawUniquePointer {
            uintptr_t ptr;
        };

        template<typename T>
        struct RawHashTable {
            RawUniquePointer<uintptr_t[]> bucket_list;
            uintptr_t first_node;
            size_t capacity;
            float unknown;
        };

        struct RawBasicString {
            uintptr_t data;
            size_t size;
            size_t cap;
        };
        static_assert(
            sizeof(RawBasicString) == sizeof(std::string), 
            "RawBasicString needs to have same size of std::string"
        );

        template <class _CharT, size_t = sizeof(_CharT)>
        struct __padding
        {
            unsigned char __xx[sizeof(_CharT)-1];
        };

        template <class _CharT>
        struct __padding<_CharT, 1>
        {
        };

    }

    // template<typename T>
    // class String {
    //     using RawString = detail::RawBasicString<char>;

    // public:
    //     explicit String(const uintptr_t& offset) : offset_(offset) { }
    // };
     
    // static_assert(
    //     sizeof(String) == sizeof(std::string), 
    //     "String needs to have the same size of std::string"
    // );

    // A class that facilitates handling vectors that are allocated in a module.
    template<typename T>
    class Vector {
        using RawVector = detail::RawVector;

        ModulePointer<T> begin_;
        ModulePointer<T> end_;
        ModulePointer<T> end_capacity_;

    public:
        detail::ArrowProxy<RawVector, std::vector<T>> operator->() {
            auto temp = RawVector {
                .begin          = begin_.as_pointer(),
                .end            = end_.as_pointer(),
                .end_capacity   = end_capacity_.as_pointer(),
            };

            return { temp };
        }

        auto begin() {
            return operator->()->begin();
        }
        
        auto end() {
            return operator->()->end();
        }
    };

    static_assert(
        sizeof(Vector<int>) == sizeof(std::vector<int>), 
        "Vector needs to have the same size of std::vector"
    );

    template<class T>
    class BasicString {
        using RawBasicString = detail::RawBasicString;

        struct __long {
            ModulePointer<T> data;
            size_t size;
            size_t cap;
        };

        enum {__min_cap = (sizeof(__long) - 1) / sizeof(T) > 2 ?
                          (sizeof(__long) - 1) / sizeof(T) : 2};
        struct __short {
            T data[__min_cap];
            struct : detail::__padding<T>
            {
                unsigned char size;
            };
        };
        
        union {
            __long __l;
            __short __s;            
        };

    public:
        explicit BasicString() { 
            memset(&__l, 0, sizeof(__long));
        }

        explicit BasicString(const std::basic_string<T>& existing) {
            // Copy the data to the stack.
            auto temp = *reinterpret_cast<const RawBasicString*>(&existing);
            // If the string is using the __long representation, we need to
            // change the pointer to pointer into the module memory.
            auto is_small = -1 < (char)reinterpret_cast<__short*>(&temp)->size;

            if(!is_small) {
                temp.data = RuntimePointer<const char>(existing.c_str()).as_raw();
            }

            memcpy(&__l, &temp, sizeof(RawBasicString));
        }

        detail::ArrowProxy<RawBasicString, std::basic_string<T>> operator->() const {
            // Copy the data to the stack.
            auto temp = *reinterpret_cast<const RawBasicString*>(&__l);
            // If the string is using the __long representation, we need to
            // change the pointer to pointer into the module memory.
            auto is_small = -1 < (char)__s.size;

            if(!is_small) {
                temp.data = __l.data.as_pointer();
            }

            return { temp };
        }


        std::basic_string<T>& operator*() {
            return *(operator->)();
        }

    };

    using String = BasicString<char>;
    static_assert(sizeof(String) == sizeof(std::string), "String needs to be the same size as std::string.");

    template<typename T>
    class HashTable {
        // struct __hash_node_base {
        //     using node_base_pointer = uintptr_t;
        //     using __first_node = __hash_node_base;

        //     node_base_pointer next;
        // };

        // struct __hash_node : public __hash_node_base {
        //     typedef T __node_value_type;

        //     size_t __hash_;
        //     __node_value_type __value_;
        // };
        

        // using __node_pointer = __hash_node_base::node_base_pointer;
        // using __node_base_type = __hash_node_base;
        // using __next_pointer = __node_pointer;

        // using RawUniquePointer = detail::RawUniquePointer;
        // using BucketList = RawUniquePointer<__next_pointer[]>;
        // using __first_node = __hash_node_base::__first_node;

        // BucketList bucketlist;
        // __first_node first;



        // typedef T* _NodePtr;
        // typedef __hash_node_types<_NodePtr> type;
        // typedef typename __make_hash_node_types<value_type>::type _NodeTypes;
        // typedef typename _NodeTypes::__next_pointer __next_pointer;
        // typedef unique_ptr<__next_pointer[]> __bucket_list;
        // __bucket_list __bucket_list_;

        // ModulePointer<
            // RawUniquePointer<uintptr_t[]> bucket_list;

    };
}