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

#import "ios/web/navigation/navigation_context_impl.h"

#import <Foundation/Foundation.h>

#import "base/memory/ptr_util.h"
#import "ios/web/common/features.h"
#import "ios/web/navigation/navigation_item_impl.h"
#import "ios/web/public/web_state.h"
#import "net/http/http_response_headers.h"

namespace web {

namespace {

// Returns a new unique ID for a NavigationContext during construction.
// The returned ID is guaranteed to be nonzero (zero is the "no ID" indicator).
int64_t CreateUniqueContextId() {
  static int64_t unique_id_counter = 0;
  return ++unique_id_counter;
}

}  // namespace

// static
std::unique_ptr<NavigationContextImpl>
NavigationContextImpl::CreateNavigationContext(
    WebState* web_state,
    const GURL& url,
    bool has_user_gesture,
    ui::PageTransition page_transition,
    bool is_renderer_initiated) {
  std::unique_ptr<NavigationContextImpl> result(
      new NavigationContextImpl(web_state, url, has_user_gesture,
                                page_transition, is_renderer_initiated));
  return result;
}

#ifndef NDEBUG
NSString* NavigationContextImpl::GetDescription() const {
  return [NSString
      stringWithFormat:
          @"web::WebState: %ld, url: %s, "
           "is_same_document: %@, error: %@ is_loading_error_page: %@",
          reinterpret_cast<long>(web_state_.get()), url_.spec().c_str(),
          is_same_document_ ? @"true" : @"false", error_,
          is_loading_error_page_ ? @"true" : @"false"];
}
#endif  // NDEBUG

WebState* NavigationContextImpl::GetWebState() {
  return web_state_.get();
}

int64_t NavigationContextImpl::GetNavigationId() const {
  return navigation_id_;
}

const GURL& NavigationContextImpl::GetUrl() const {
  return url_;
}

bool NavigationContextImpl::HasUserGesture() const {
  return has_user_gesture_;
}

ui::PageTransition NavigationContextImpl::GetPageTransition() const {
  return page_transition_;
}

bool NavigationContextImpl::IsSameDocument() const {
  return is_same_document_;
}

bool NavigationContextImpl::HasCommitted() const {
  return has_committed_;
}

bool NavigationContextImpl::IsDownload() const {
  return is_download_;
}

bool NavigationContextImpl::IsPost() const {
  return is_post_;
}

NSError* NavigationContextImpl::GetError() const {
  return error_;
}

net::HttpResponseHeaders* NavigationContextImpl::GetResponseHeaders() const {
  return response_headers_.get();
}

bool NavigationContextImpl::IsRendererInitiated() const {
  return is_renderer_initiated_;
}

void NavigationContextImpl::SetUrl(const GURL& url) {
  url_ = url;
}

void NavigationContextImpl::SetIsSameDocument(bool is_same_document) {
  is_same_document_ = is_same_document;
}

void NavigationContextImpl::SetHasCommitted(bool has_committed) {
  has_committed_ = has_committed;
}

void NavigationContextImpl::SetIsDownload(bool is_download) {
  is_download_ = is_download;
}

void NavigationContextImpl::SetIsPost(bool is_post) {
  is_post_ = is_post;
}

void NavigationContextImpl::SetError(NSError* error) {
  error_ = error;
}

void NavigationContextImpl::SetResponseHeaders(
    const scoped_refptr<net::HttpResponseHeaders>& response_headers) {
  response_headers_ = response_headers;
}

HttpsUpgradeType NavigationContextImpl::GetFailedHttpsUpgradeType() const {
  return failed_https_upgrade_type_;
}

void NavigationContextImpl::SetFailedHttpsUpgradeType(HttpsUpgradeType type) {
  failed_https_upgrade_type_ = type;
}

int NavigationContextImpl::GetNavigationItemUniqueID() const {
  return navigation_item_unique_id_;
}

void NavigationContextImpl::SetNavigationItemUniqueID(int unique_id) {
  navigation_item_unique_id_ = unique_id;
}

void NavigationContextImpl::SetWKNavigationType(
    WKNavigationType wk_navigation_type) {
  wk_navigation_type_ = wk_navigation_type;
}

WKNavigationType NavigationContextImpl::GetWKNavigationType() const {
  return wk_navigation_type_;
}

bool NavigationContextImpl::IsLoadingErrorPage() const {
  return is_loading_error_page_;
}

void NavigationContextImpl::SetLoadingErrorPage(bool is_loading_error_page) {
  is_loading_error_page_ = is_loading_error_page;
}

bool NavigationContextImpl::IsLoadingHtmlString() const {
  return is_loading_html_string_;
}

void NavigationContextImpl::SetLoadingHtmlString(bool is_loading_html_string) {
  is_loading_html_string_ = is_loading_html_string;
}

void NavigationContextImpl::SetMimeType(NSString* mime_type) {
  mime_type_ = mime_type;
}

NSString* NavigationContextImpl::GetMimeType() const {
  return mime_type_;
}

NavigationItemImpl* NavigationContextImpl::GetItem() {
  return item_.get();
}

std::unique_ptr<NavigationItemImpl> NavigationContextImpl::ReleaseItem() {
  return std::move(item_);
}

void NavigationContextImpl::SetItem(std::unique_ptr<NavigationItemImpl> item) {
  DCHECK(!item_);
  if (item) {
    // `item` can be null for same-docuemnt navigations and reloads, where
    // navigation item is committed and should not be stored in
    // NavigationContext.
    DCHECK_EQ(GetNavigationItemUniqueID(), item->GetUniqueID());
  }
  item_ = std::move(item);
}

base::TimeDelta NavigationContextImpl::GetElapsedTimeSinceCreation() const {
  return elapsed_timer_.Elapsed();
}

base::WeakPtr<NavigationContextImpl> NavigationContextImpl::GetWeakPtr() {
  return weak_factory_.GetWeakPtr();
}

NavigationContextImpl::NavigationContextImpl(WebState* web_state,
                                             const GURL& url,
                                             bool has_user_gesture,
                                             ui::PageTransition page_transition,
                                             bool is_renderer_initiated)
    : web_state_(web_state ? web_state->GetWeakPtr() : nullptr),
      navigation_id_(CreateUniqueContextId()),
      url_(url),
      has_user_gesture_(has_user_gesture),
      page_transition_(page_transition),
      is_same_document_(false),
      error_(nil),
      response_headers_(nullptr),
      is_renderer_initiated_(is_renderer_initiated),
      elapsed_timer_(base::ElapsedTimer()) {}

NavigationContextImpl::~NavigationContextImpl() = default;

}  // namespace web
