#pragma once
#include "starlight/_global.h"

#include <string>
#include <memory>

#include "starlight/ThemeManager.h"

namespace starlight {
    namespace gfx {
        template <class T> class ThemeRef;
        template <class T>
        class ThemeRefContainer {
            friend class starlight::ThemeManager;
            friend class ThemeRef<T>;
        protected:
            const std::string name;
            std::shared_ptr<T> ptr = nullptr;
            ThemeRefContainer* redir = nullptr;
            unsigned int lastAccess = 0; // how many gc sweeps since last use
            volatile int refCount = 0;
            void Unload(bool full = false) {
                ptr.reset();
                if (full) redir = nullptr;
            }
            
            ThemeRefContainer(std::string name, std::shared_ptr<T> ptr) : name(name), ptr(ptr) { }
            ThemeRefContainer(std::string name, T* ptr) : name(name), ptr(ptr) { }
            ThemeRefContainer(std::string name) : name(name) { }
            
            inline std::shared_ptr<T>& _getptr() {
                lastAccess = 0;
                if (!redir && !ptr)  ThemeManager::Fulfill(*this); // call thememanager to grab things
                if (redir) return redir->_getptr();
                return ptr;
            }
            inline std::shared_ptr<T>& getptr() const { return const_cast<ThemeRefContainer<T>&>(*this)._getptr(); }
        public:
            ~ThemeRefContainer() { }
            
            T* operator ->() const {
                return &*(getptr());
            }
            
            /*T& operator *() const {
                if (ptr == nullptr) {
                    ThemeManager::Fulfill(const_cast<ThemeRef<T>&>(*this)); // call thememanager to grab things
                }
                return *ptr;
            }*/
            
        };
        
        template <class T>
        class ThemeRef {
            friend class starlight::ThemeManager;
        protected:
            ThemeRefContainer<T>* cptr;
        public:
            ThemeRef() : cptr(nullptr) { }
            ThemeRef(ThemeRefContainer<T>* c) : cptr(c) { if (cptr) cptr->refCount++; }
            ThemeRef(ThemeRef<T>& o) : cptr(o.cptr) { if (cptr) cptr->refCount++; }
            ThemeRef(ThemeRef<T>&& o) : cptr(o.cptr) { if (cptr) cptr->refCount++; }
            ~ThemeRef() { if (cptr) cptr->refCount--; }
            
            ThemeRef<T>& operator =(const ThemeRef<T>& o) {
                if (cptr) cptr->refCount--;
                cptr = o.cptr;
                if (cptr) cptr->refCount++;
                return *this;
            }
            ThemeRef<T>& operator =(const ThemeRef<T>&& o) {
                if (cptr) cptr->refCount--;
                cptr = o.cptr;
                if (cptr) cptr->refCount++;
                return *this;
            }
            
            inline const ThemeRefContainer<T>& operator ->() const { return const_cast<const ThemeRefContainer<T>&>(*cptr); }
            inline explicit operator bool() const { return cptr != nullptr; }
            inline std::shared_ptr<T> GetShared() const { return (*cptr).getptr(); }
            inline const std::string& GetName() const { return (*cptr).name; }
        };
    }
}
