// Copyright 2014 The Chromium OS 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 LIBBRILLO_BRILLO_HTTP_CURL_API_H_
#define LIBBRILLO_BRILLO_HTTP_CURL_API_H_

#include <curl/curl.h>

#include <string>

#include <base/macros.h>
#include <brillo/brillo_export.h>

namespace brillo {
namespace http {

// Abstract wrapper around libcurl C API that allows us to mock it out in tests.
class CurlInterface {
 public:
  CurlInterface() = default;
  virtual ~CurlInterface() = default;

  // Wrapper around curl_easy_init().
  virtual CURL* EasyInit() = 0;

  // Wrapper around curl_easy_cleanup().
  virtual void EasyCleanup(CURL* curl) = 0;

  // Wrappers around curl_easy_setopt().
  virtual CURLcode EasySetOptInt(CURL* curl, CURLoption option, int value) = 0;
  virtual CURLcode EasySetOptStr(CURL* curl,
                                 CURLoption option,
                                 const std::string& value) = 0;
  virtual CURLcode EasySetOptPtr(CURL* curl,
                                 CURLoption option,
                                 void* value) = 0;
  virtual CURLcode EasySetOptCallback(CURL* curl,
                                      CURLoption option,
                                      intptr_t address) = 0;
  virtual CURLcode EasySetOptOffT(CURL* curl,
                                  CURLoption option,
                                  curl_off_t value) = 0;

  // A type-safe wrapper around function callback options.
  template<typename R, typename... Args>
  inline CURLcode EasySetOptCallback(CURL* curl,
                                     CURLoption option,
                                     R(*callback)(Args...)) {
    return EasySetOptCallback(
        curl, option, reinterpret_cast<intptr_t>(callback));
  }

  // Wrapper around curl_easy_perform().
  virtual CURLcode EasyPerform(CURL* curl) = 0;

  // Wrappers around curl_easy_getinfo().
  virtual CURLcode EasyGetInfoInt(CURL* curl,
                                  CURLINFO info,
                                  int* value) const = 0;
  virtual CURLcode EasyGetInfoDbl(CURL* curl,
                                  CURLINFO info,
                                  double* value) const = 0;
  virtual CURLcode EasyGetInfoStr(CURL* curl,
                                  CURLINFO info,
                                  std::string* value) const = 0;
  virtual CURLcode EasyGetInfoPtr(CURL* curl,
                                  CURLINFO info,
                                  void** value) const = 0;

  // Wrapper around curl_easy_strerror().
  virtual std::string EasyStrError(CURLcode code) const = 0;

  // Wrapper around curl_multi_init().
  virtual CURLM* MultiInit() = 0;

  // Wrapper around curl_multi_cleanup().
  virtual CURLMcode MultiCleanup(CURLM* multi_handle) = 0;

  // Wrapper around curl_multi_info_read().
  virtual CURLMsg* MultiInfoRead(CURLM* multi_handle, int* msgs_in_queue) = 0;

  // Wrapper around curl_multi_add_handle().
  virtual CURLMcode MultiAddHandle(CURLM* multi_handle, CURL* curl_handle) = 0;

  // Wrapper around curl_multi_remove_handle().
  virtual CURLMcode MultiRemoveHandle(CURLM* multi_handle,
                                      CURL* curl_handle) = 0;

  // Wrapper around curl_multi_setopt(CURLMOPT_SOCKETFUNCTION/SOCKETDATA).
  virtual CURLMcode MultiSetSocketCallback(
      CURLM* multi_handle,
      curl_socket_callback socket_callback,
      void* userp) = 0;

  // Wrapper around curl_multi_setopt(CURLMOPT_TIMERFUNCTION/TIMERDATA).
  virtual CURLMcode MultiSetTimerCallback(
      CURLM* multi_handle,
      curl_multi_timer_callback timer_callback,
      void* userp) = 0;

  // Wrapper around curl_multi_assign().
  virtual CURLMcode MultiAssign(CURLM* multi_handle,
                                curl_socket_t sockfd,
                                void* sockp) = 0;

  // Wrapper around curl_multi_socket_action().
  virtual CURLMcode MultiSocketAction(CURLM* multi_handle,
                                      curl_socket_t s,
                                      int ev_bitmask,
                                      int* running_handles) = 0;

  // Wrapper around curl_multi_strerror().
  virtual std::string MultiStrError(CURLMcode code) const = 0;

  // Wrapper around curl_multi_perform().
  virtual CURLMcode MultiPerform(CURLM* multi_handle,
                                 int* running_handles) = 0;

  // Wrapper around curl_multi_wait().
  virtual CURLMcode MultiWait(CURLM* multi_handle,
                              curl_waitfd extra_fds[],
                              unsigned int extra_nfds,
                              int timeout_ms,
                              int* numfds) = 0;

 private:
  DISALLOW_COPY_AND_ASSIGN(CurlInterface);
};

class BRILLO_EXPORT CurlApi : public CurlInterface {
 public:
  CurlApi();
  ~CurlApi() override;

  // Wrapper around curl_easy_init().
  CURL* EasyInit() override;

  // Wrapper around curl_easy_cleanup().
  void EasyCleanup(CURL* curl) override;

  // Wrappers around curl_easy_setopt().
  CURLcode EasySetOptInt(CURL* curl, CURLoption option, int value) override;
  CURLcode EasySetOptStr(CURL* curl,
                         CURLoption option,
                         const std::string& value) override;
  CURLcode EasySetOptPtr(CURL* curl, CURLoption option, void* value) override;
  CURLcode EasySetOptCallback(CURL* curl,
                              CURLoption option,
                              intptr_t address) override;
  CURLcode EasySetOptOffT(CURL* curl,
                          CURLoption option,
                          curl_off_t value) override;

  // Wrapper around curl_easy_perform().
  CURLcode EasyPerform(CURL* curl) override;

  // Wrappers around curl_easy_getinfo().
  CURLcode EasyGetInfoInt(CURL* curl, CURLINFO info, int* value) const override;
  CURLcode EasyGetInfoDbl(CURL* curl,
                          CURLINFO info,
                          double* value) const override;
  CURLcode EasyGetInfoStr(CURL* curl,
                          CURLINFO info,
                          std::string* value) const override;
  CURLcode EasyGetInfoPtr(CURL* curl,
                          CURLINFO info,
                          void** value) const override;

  // Wrapper around curl_easy_strerror().
  std::string EasyStrError(CURLcode code) const override;

  // Wrapper around curl_multi_init().
  CURLM* MultiInit() override;

  // Wrapper around curl_multi_cleanup().
  CURLMcode MultiCleanup(CURLM* multi_handle) override;

  // Wrapper around curl_multi_info_read().
  CURLMsg* MultiInfoRead(CURLM* multi_handle, int* msgs_in_queue) override;

  // Wrapper around curl_multi_add_handle().
  CURLMcode MultiAddHandle(CURLM* multi_handle, CURL* curl_handle) override;

  // Wrapper around curl_multi_remove_handle().
  CURLMcode MultiRemoveHandle(CURLM* multi_handle, CURL* curl_handle) override;

  // Wrapper around curl_multi_setopt(CURLMOPT_SOCKETFUNCTION/SOCKETDATA).
  CURLMcode MultiSetSocketCallback(
      CURLM* multi_handle,
      curl_socket_callback socket_callback,
      void* userp) override;

  // Wrapper around curl_multi_setopt(CURLMOPT_TIMERFUNCTION/TIMERDATA).
  CURLMcode MultiSetTimerCallback(
      CURLM* multi_handle,
      curl_multi_timer_callback timer_callback,
      void* userp) override;

  // Wrapper around curl_multi_assign().
  CURLMcode MultiAssign(CURLM* multi_handle,
                        curl_socket_t sockfd,
                        void* sockp) override;

  // Wrapper around curl_multi_socket_action().
  CURLMcode MultiSocketAction(CURLM* multi_handle,
                              curl_socket_t s,
                              int ev_bitmask,
                              int* running_handles) override;

  // Wrapper around curl_multi_strerror().
  std::string MultiStrError(CURLMcode code) const override;

  // Wrapper around curl_multi_perform().
  CURLMcode MultiPerform(CURLM* multi_handle,
                         int* running_handles) override;

  // Wrapper around curl_multi_wait().
  CURLMcode MultiWait(CURLM* multi_handle,
                      curl_waitfd extra_fds[],
                      unsigned int extra_nfds,
                      int timeout_ms,
                      int* numfds) override;

 private:
  DISALLOW_COPY_AND_ASSIGN(CurlApi);
};

}  // namespace http
}  // namespace brillo

#endif  // LIBBRILLO_BRILLO_HTTP_CURL_API_H_