// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#ifndef ATOM_BROWSER_NET_ATOM_NETWORK_DELEGATE_H_
#define ATOM_BROWSER_NET_ATOM_NETWORK_DELEGATE_H_

#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>

#include "base/callback.h"
#include "base/synchronization/lock.h"
#include "base/values.h"
#include "content/public/browser/resource_request_info.h"
#include "extensions/common/url_pattern.h"
#include "net/base/network_delegate.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"

class URLPattern;

namespace atom {

using URLPatterns = std::set<URLPattern>;

const char* ResourceTypeToString(content::ResourceType type);

class LoginHandler;

class AtomNetworkDelegate : public net::NetworkDelegate {
 public:
  using ResponseCallback = base::Callback<void(const base::DictionaryValue&)>;
  using SimpleListener = base::Callback<void(const base::DictionaryValue&)>;
  using ResponseListener = base::Callback<void(const base::DictionaryValue&,
                                               const ResponseCallback&)>;

  enum SimpleEvent {
    kOnSendHeaders,
    kOnBeforeRedirect,
    kOnResponseStarted,
    kOnCompleted,
    kOnErrorOccurred,
  };

  enum ResponseEvent {
    kOnBeforeRequest,
    kOnBeforeSendHeaders,
    kOnHeadersReceived,
  };

  struct SimpleListenerInfo {
    URLPatterns url_patterns;
    SimpleListener listener;

    SimpleListenerInfo(URLPatterns, SimpleListener);
    SimpleListenerInfo();
    ~SimpleListenerInfo();
  };

  struct ResponseListenerInfo {
    URLPatterns url_patterns;
    ResponseListener listener;

    ResponseListenerInfo(URLPatterns, ResponseListener);
    ResponseListenerInfo();
    ~ResponseListenerInfo();
  };

  AtomNetworkDelegate();
  ~AtomNetworkDelegate() override;

  void SetSimpleListenerInIO(SimpleEvent type,
                             URLPatterns patterns,
                             SimpleListener callback);
  void SetResponseListenerInIO(ResponseEvent type,
                               URLPatterns patterns,
                               ResponseListener callback);

 protected:
  // net::NetworkDelegate:
  int OnBeforeURLRequest(net::URLRequest* request,
                         net::CompletionOnceCallback callback,
                         GURL* new_url) override;
  int OnBeforeStartTransaction(net::URLRequest* request,
                               net::CompletionOnceCallback callback,
                               net::HttpRequestHeaders* headers) override;
  void OnBeforeSendHeaders(net::URLRequest* request,
                           const net::ProxyInfo& proxy_info,
                           const net::ProxyRetryInfoMap& proxy_retry_info,
                           net::HttpRequestHeaders* headers) override {}
  void OnStartTransaction(net::URLRequest* request,
                          const net::HttpRequestHeaders& headers) override;
  int OnHeadersReceived(
      net::URLRequest* request,
      net::CompletionOnceCallback callback,
      const net::HttpResponseHeaders* original_response_headers,
      scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
      GURL* allowed_unsafe_redirect_url) override;
  void OnBeforeRedirect(net::URLRequest* request,
                        const GURL& new_location) override;
  void OnResponseStarted(net::URLRequest* request, int net_error) override;
  void OnNetworkBytesReceived(net::URLRequest* request,
                              int64_t bytes_read) override {}
  void OnNetworkBytesSent(net::URLRequest* request,
                          int64_t bytes_sent) override {}
  void OnCompleted(net::URLRequest* request,
                   bool started,
                   int net_error) override;
  void OnURLRequestDestroyed(net::URLRequest* request) override;
  void OnPACScriptError(int line_number, const base::string16& error) override {
  }
  AuthRequiredResponse OnAuthRequired(
      net::URLRequest* request,
      const net::AuthChallengeInfo& auth_info,
      AuthCallback callback,
      net::AuthCredentials* credentials) override;
  bool OnCanGetCookies(const net::URLRequest& request,
                       const net::CookieList& cookie_list,
                       bool allowed_from_caller) override;
  bool OnCanSetCookie(const net::URLRequest& request,
                      const net::CanonicalCookie& cookie_line,
                      net::CookieOptions* options,
                      bool allowed_from_caller) override;
  bool OnCanAccessFile(const net::URLRequest& request,
                       const base::FilePath& original_path,
                       const base::FilePath& absolute_path) const override;
  bool OnForcePrivacyMode(const GURL& url,
                          const GURL& first_party_for_cookies) const override;
  bool OnCancelURLRequestWithPolicyViolatingReferrerHeader(
      const net::URLRequest& request,
      const GURL& target_url,
      const GURL& referrer_url) const override;
  bool OnCanQueueReportingReport(const url::Origin& origin) const override;
  void OnCanSendReportingReports(std::set<url::Origin> origins,
                                 base::OnceCallback<void(std::set<url::Origin>)>
                                     result_callback) const override;
  bool OnCanSetReportingClient(const url::Origin& origin,
                               const GURL& endpoint) const override;
  bool OnCanUseReportingClient(const url::Origin& origin,
                               const GURL& endpoint) const override;

 private:
  void OnErrorOccurred(net::URLRequest* request, bool started, int net_error);

  template <typename... Args>
  void HandleSimpleEvent(SimpleEvent type,
                         net::URLRequest* request,
                         Args... args);
  template <typename Out, typename... Args>
  int HandleResponseEvent(ResponseEvent type,
                          net::URLRequest* request,
                          net::CompletionOnceCallback callback,
                          Out out,
                          Args... args);

  // Deal with the results of Listener.
  template <typename T>
  void OnListenerResultInIO(uint64_t id,
                            T out,
                            std::unique_ptr<base::DictionaryValue> response);
  template <typename T>
  void OnListenerResultInUI(uint64_t id,
                            T out,
                            const base::DictionaryValue& response);

  std::map<uint64_t, scoped_refptr<LoginHandler>> login_handler_map_;
  std::map<SimpleEvent, SimpleListenerInfo> simple_listeners_;
  std::map<ResponseEvent, ResponseListenerInfo> response_listeners_;
  std::map<uint64_t, net::CompletionOnceCallback> callbacks_;
  std::vector<std::string> ignore_connections_limit_domains_;

  DISALLOW_COPY_AND_ASSIGN(AtomNetworkDelegate);
};

}  // namespace atom

#endif  // ATOM_BROWSER_NET_ATOM_NETWORK_DELEGATE_H_
