#pragma once
#include <json/value.h>
#include <memory>
#include <string>
#include <drogon/HttpTypes.h>
#include <string_view>
#include <trantor/net/InetAddress.h>
#include <trantor/utils/NonCopyable.h>
namespace drogon
{
enum class CloseCode
{
    kNormalClosure = 1000,
    kEndpointGone = 1001,
    kProtocolError = 1002,
    kInvalidMessage = 1003,
    kNone = 1005,
    kAbnormally = 1006,
    kWrongMessageContent = 1007,
    kViolation = 1008,
    kMessageTooBig = 1009,
    kNeedMoreExtensions = 1010,
    kUnexpectedCondition = 1011,
    kTLSFailed = 1015
};
class WebSocketConnection
{
  public:
    WebSocketConnection() = default;
    virtual ~WebSocketConnection(){};
    virtual void send(
        const char *msg,
        uint64_t len,
        const WebSocketMessageType type = WebSocketMessageType::Text) = 0;
    virtual void send(
        std::string_view msg,
        const WebSocketMessageType type = WebSocketMessageType::Text) = 0;
    virtual void sendJson(
        const Json::Value &json,
        const WebSocketMessageType type = WebSocketMessageType::Text) = 0;
    virtual const trantor::InetAddress &localAddr() const = 0;
    virtual const trantor::InetAddress &peerAddr() const = 0;
    virtual bool connected() const = 0;
    virtual bool disconnected() const = 0;
    virtual void shutdown(const CloseCode code = CloseCode::kNormalClosure,
                          const std::string &reason = "") = 0;
    virtual void forceClose() = 0;
    void setContext(const std::shared_ptr<void> &context)
    {
        contextPtr_ = context;
    }
    void setContext(std::shared_ptr<void> &&context)
    {
        contextPtr_ = std::move(context);
    }
    template <typename T>
    std::shared_ptr<T> getContext() const
    {
        return std::static_pointer_cast<T>(contextPtr_);
    }
    template <typename T>
    T &getContextRef() const
    {
        return *(static_cast<T *>(contextPtr_.get()));
    }
    bool hasContext()
    {
        return (bool)contextPtr_;
    }
    void clearContext()
    {
        contextPtr_.reset();
    }
    virtual void setPingMessage(
        const std::string &message,
        const std::chrono::duration<double> &interval) = 0;
    virtual void disablePing() = 0;
  private:
    std::shared_ptr<void> contextPtr_;
};
using WebSocketConnectionPtr = std::shared_ptr<WebSocketConnection>;
}  