// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef {{"_".join(config.protocol.namespace)}}_DispatcherBase_h
#define {{"_".join(config.protocol.namespace)}}_DispatcherBase_h

//#include "Collections.h"
//#include "ErrorSupport.h"
//#include "Forward.h"
//#include "Values.h"

{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
{% endfor %}

class WeakPtr;

class {{config.lib.export_macro}} DispatchResponse {
public:
    enum Status {
        kSuccess = 0,
        kError = 1,
        kFallThrough = 2,
        kAsync = 3
    };

    enum ErrorCode {
        kParseError = -32700,
        kInvalidRequest = -32600,
        kMethodNotFound = -32601,
        kInvalidParams = -32602,
        kInternalError = -32603,
        kServerError = -32000,
    };

    Status status() const { return m_status; }
    const String& errorMessage() const { return m_errorMessage; }
    ErrorCode errorCode() const { return m_errorCode; }
    bool isSuccess() const { return m_status == kSuccess; }

    static DispatchResponse OK();
    static DispatchResponse Error(const String&);
    static DispatchResponse InternalError();
    static DispatchResponse InvalidParams(const String&);
    static DispatchResponse FallThrough();

private:
    Status m_status;
    String m_errorMessage;
    ErrorCode m_errorCode;
};

class {{config.lib.export_macro}} DispatcherBase {
    PROTOCOL_DISALLOW_COPY(DispatcherBase);
public:
    static const char kInvalidParamsString[];
    class {{config.lib.export_macro}} WeakPtr {
    public:
        explicit WeakPtr(DispatcherBase*);
        ~WeakPtr();
        DispatcherBase* get() { return m_dispatcher; }
        void dispose() { m_dispatcher = nullptr; }

    private:
        DispatcherBase* m_dispatcher;
    };

    class {{config.lib.export_macro}} Callback {
    public:
        Callback(std::unique_ptr<WeakPtr> backendImpl, int callId, int callbackId);
        virtual ~Callback();
        void dispose();

    protected:
        void sendIfActive(std::unique_ptr<protocol::DictionaryValue> partialMessage, const DispatchResponse& response);
        void fallThroughIfActive();

    private:
        std::unique_ptr<WeakPtr> m_backendImpl;
        int m_callId;
        int m_callbackId;
    };

    explicit DispatcherBase(FrontendChannel*);
    virtual ~DispatcherBase();

    static bool getCommandName(const String& message, String* result);

    virtual DispatchResponse::Status dispatch(int callId, const String& method, std::unique_ptr<protocol::DictionaryValue> messageObject) = 0;

    void sendResponse(int callId, const DispatchResponse&, std::unique_ptr<protocol::DictionaryValue> result);
    void sendResponse(int callId, const DispatchResponse&);

    void reportProtocolError(int callId, DispatchResponse::ErrorCode, const String& errorMessage, ErrorSupport* errors);
    void clearFrontend();

    std::unique_ptr<WeakPtr> weakPtr();

    int nextCallbackId();
    void markFallThrough(int callbackId);
    bool lastCallbackFallThrough() { return m_lastCallbackFallThrough; }

private:
    FrontendChannel* m_frontendChannel;
    protocol::HashSet<WeakPtr*> m_weakPtrs;
    int m_lastCallbackId;
    bool m_lastCallbackFallThrough;
};

class {{config.lib.export_macro}} UberDispatcher {
    PROTOCOL_DISALLOW_COPY(UberDispatcher);
public:
    explicit UberDispatcher(FrontendChannel*);
    void registerBackend(const String& name, std::unique_ptr<protocol::DispatcherBase>);
    DispatchResponse::Status dispatch(std::unique_ptr<Value> message);
    FrontendChannel* channel() { return m_frontendChannel; }
    bool fallThroughForNotFound() { return m_fallThroughForNotFound; }
    void setFallThroughForNotFound(bool);
    virtual ~UberDispatcher();

private:
    FrontendChannel* m_frontendChannel;
    bool m_fallThroughForNotFound;
    protocol::HashMap<String, std::unique_ptr<protocol::DispatcherBase>> m_dispatchers;
};

class InternalResponse : public Serializable {
    PROTOCOL_DISALLOW_COPY(InternalResponse);
public:
    static std::unique_ptr<InternalResponse> createResponse(int callId, std::unique_ptr<Serializable> params);
    static std::unique_ptr<InternalResponse> createNotification(const String& notification, std::unique_ptr<Serializable> params = nullptr);

    String serialize() override;

    ~InternalResponse() override {}

private:
    InternalResponse(int callId, const String& notification, std::unique_ptr<Serializable> params);

    int m_callId;
    String m_notification;
    std::unique_ptr<Serializable> m_params;
};

class InternalRawNotification : public Serializable {
public:
    static std::unique_ptr<InternalRawNotification> create(const String& notification)
    {
        return std::unique_ptr<InternalRawNotification>(new InternalRawNotification(notification));
    }
    ~InternalRawNotification() override {}

    String serialize() override
    {
        return m_notification;
    }

private:
  explicit InternalRawNotification(const String& notification)
    : m_notification(notification)
  {
  }

  String m_notification;
};

{% for namespace in config.protocol.namespace %}
} // namespace {{namespace}}
{% endfor %}

#endif // !defined({{"_".join(config.protocol.namespace)}}_DispatcherBase_h)