#ifndef SafeWebsocketDeferredSend_h
#define SafeWebsocketDeferredSend_h

#include <string>
#include <memory>
#include <shared_mutex>
#include <App.h>
#include "common.h"

class SafeWebsocketDeferredSend {
public:
    class impl : public std::enable_shared_from_this<impl> {
    public:
        virtual void send(const std::string_view & data, uWS::OpCode op) = 0;
        virtual ~impl() { };
    };
    virtual ~SafeWebsocketDeferredSend() { };
    virtual std::shared_ptr<impl> get() = 0;
};

template<typename WebSocket>
class SafeWebsocketDeferredSendT final : public SafeWebsocketDeferredSend {
public:
    SafeWebsocketDeferredSendT(WebSocket * ws, uWS::Loop * loop)
    : pimpl(impl::make(ws, loop)) { }
    
    ~SafeWebsocketDeferredSendT() {
        pimpl->clear();
    }
    
    class impl final : public SafeWebsocketDeferredSend::impl {
        struct Private { };
        friend class SafeWebsocketDeferredSendT;
        std::shared_mutex cleanupMutex;
        WebSocket * ws;
        uWS::Loop * loop;
        platform_thread_t eventLoopThd;
        void clear() {
            std::unique_lock lock(cleanupMutex);
            loop = nullptr;
            ws = nullptr;
        }
        static std::shared_ptr<impl> make(WebSocket * ws, uWS::Loop * loop) {
            return std::make_shared<impl>(Private{}, ws, loop);
        }
    public:
        impl(Private p, WebSocket * ws, uWS::Loop * loop) : ws(ws), loop(loop) {
            eventLoopThd = platformGetCurrentThread();
        }
        impl(const impl&) = delete;
        
        virtual void send(const std::string_view & data, uWS::OpCode op) override {
            if(platformGetCurrentThread() == eventLoopThd) {
                if(!ws) {
                    return;
                }
                ws->send(data, op);
                return;
            }
            auto dataCopy = std::string(data);
            auto thisShared = std::dynamic_pointer_cast<impl>(this->shared_from_this());
            std::shared_lock lock(cleanupMutex);
            if(!loop) {
                return;
            }
            loop->defer([=,dataCopy=std::move(dataCopy)]{
                if(!thisShared->ws) {
                    return;
                }
                ws->send(dataCopy, op);
            });
        }
    };
    
    virtual std::shared_ptr<SafeWebsocketDeferredSend::impl> get() override {
        return pimpl;
    }
private:
    std::shared_ptr<impl> pimpl;
};


#endif /* SafeWebsocketDeferredSend_h */
