/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

/* globals ContentSearchUIController, ContentSearchHandoffUIController */
"use strict";

import {
  actionCreators as ac,
  actionTypes as at,
} from "common/Actions.sys.mjs";
import { connect } from "react-redux";
import { IS_NEWTAB } from "content-src/lib/constants";
import React from "react";

export class _Search extends React.PureComponent {
  constructor(props) {
    super(props);
    this.onSearchClick = this.onSearchClick.bind(this);
    this.onSearchHandoffClick = this.onSearchHandoffClick.bind(this);
    this.onSearchHandoffPaste = this.onSearchHandoffPaste.bind(this);
    this.onSearchHandoffDrop = this.onSearchHandoffDrop.bind(this);
    this.onInputMount = this.onInputMount.bind(this);
    this.onInputMountHandoff = this.onInputMountHandoff.bind(this);
    this.onSearchHandoffButtonMount =
      this.onSearchHandoffButtonMount.bind(this);
  }

  handleEvent(event) {
    // Also track search events with our own telemetry
    if (event.detail.type === "Search") {
      this.props.dispatch(ac.UserEvent({ event: "SEARCH" }));
    }
  }

  onSearchClick(event) {
    window.gContentSearchController.search(event);
  }

  doSearchHandoff(text) {
    this.props.dispatch(
      ac.OnlyToMain({ type: at.HANDOFF_SEARCH_TO_AWESOMEBAR, data: { text } })
    );
    this.props.dispatch({ type: at.FAKE_FOCUS_SEARCH });
    this.props.dispatch(ac.UserEvent({ event: "SEARCH_HANDOFF" }));
    if (text) {
      this.props.dispatch({ type: at.DISABLE_SEARCH });
    }
  }

  onSearchHandoffClick(event) {
    // When search hand-off is enabled, we render a big button that is styled to
    // look like a search textbox. If the button is clicked, we style
    // the button as if it was a focused search box and show a fake cursor but
    // really focus the awesomebar without the focus styles ("hidden focus").
    event.preventDefault();
    this.doSearchHandoff();
  }

  onSearchHandoffPaste(event) {
    event.preventDefault();
    this.doSearchHandoff(event.clipboardData.getData("Text"));
  }

  onSearchHandoffDrop(event) {
    event.preventDefault();
    let text = event.dataTransfer.getData("text");
    if (text) {
      this.doSearchHandoff(text);
    }
  }

  componentWillUnmount() {
    delete window.gContentSearchController;
  }

  onInputMount(input) {
    if (input) {
      // The "healthReportKey" and needs to be "newtab" or "abouthome" so that
      // BrowserUsageTelemetry.sys.mjs knows to handle events with this name, and
      // can add the appropriate telemetry probes for search. Without the correct
      // name, certain tests like browser_UsageTelemetry_content.js will fail
      // (See github ticket #2348 for more details)
      const healthReportKey = IS_NEWTAB ? "newtab" : "abouthome";

      // The "searchSource" needs to be "newtab" or "homepage" and is sent with
      // the search data and acts as context for the search request (See
      // nsISearchEngine.getSubmission). It is necessary so that search engine
      // plugins can correctly atribute referrals. (See github ticket #3321 for
      // more details)
      const searchSource = IS_NEWTAB ? "newtab" : "homepage";

      // gContentSearchController needs to exist as a global so that tests for
      // the existing about:home can find it; and so it allows these tests to pass.
      // In the future, when activity stream is default about:home, this can be renamed
      window.gContentSearchController = new ContentSearchUIController(
        input,
        input.parentNode,
        healthReportKey,
        searchSource
      );
      addEventListener("ContentSearchClient", this);
    } else {
      window.gContentSearchController = null;
      removeEventListener("ContentSearchClient", this);
    }
  }

  onInputMountHandoff(input) {
    if (input) {
      // The handoff UI controller helps us set the search icon and reacts to
      // changes to default engine to keep everything in sync.
      this._handoffSearchController = new ContentSearchHandoffUIController();
    }
  }

  getDefaultEngineName() {
    // _handoffSearchController will manage engine names once it is initialized.
    return this.props.Prefs.values["urlbar.placeholderName"];
  }

  getHandoffInputL10nAttributes() {
    let defaultEngineName = this.getDefaultEngineName();
    return defaultEngineName
      ? {
          "data-l10n-id": "newtab-search-box-handoff-input",
          "data-l10n-args": `{"engine": "${defaultEngineName}"}`,
        }
      : {
          "data-l10n-id": "newtab-search-box-handoff-input-no-engine",
        };
  }

  getHandoffTextL10nAttributes() {
    let defaultEngineName = this.getDefaultEngineName();
    return defaultEngineName
      ? {
          "data-l10n-id": "newtab-search-box-handoff-text",
          "data-l10n-args": `{"engine": "${defaultEngineName}"}`,
        }
      : {
          "data-l10n-id": "newtab-search-box-handoff-text-no-engine",
        };
  }

  onSearchHandoffButtonMount(button) {
    // Keep a reference to the button for use during "paste" event handling.
    this._searchHandoffButton = button;
  }

  /*
   * Do not change the ID on the input field, as legacy newtab code
   * specifically looks for the id 'newtab-search-text' on input fields
   * in order to execute searches in various tests
   */
  render() {
    const wrapperClassName = [
      "search-wrapper",
      this.props.disable && "search-disabled",
      this.props.fakeFocus && "fake-focus",
    ]
      .filter(v => v)
      .join(" ");

    return (
      <div className={wrapperClassName}>
        {this.props.showLogo && (
          <div className="logo-and-wordmark">
            <div className="logo" />
            <div className="wordmark" />
          </div>
        )}
        {!this.props.handoffEnabled && (
          <div className="search-inner-wrapper">
            <input
              id="newtab-search-text"
              data-l10n-id="newtab-search-box-input"
              maxLength="256"
              ref={this.onInputMount}
              type="search"
            />
            <button
              id="searchSubmit"
              className="search-button"
              data-l10n-id="newtab-search-box-search-button"
              onClick={this.onSearchClick}
            />
          </div>
        )}
        {this.props.handoffEnabled && (
          <div className="search-inner-wrapper">
            <button
              className="search-handoff-button"
              {...this.getHandoffInputL10nAttributes()}
              ref={this.onSearchHandoffButtonMount}
              onClick={this.onSearchHandoffClick}
              tabIndex="-1"
            >
              <div
                className="fake-textbox"
                {...this.getHandoffTextL10nAttributes()}
              />
              <input
                type="search"
                className="fake-editable"
                tabIndex="-1"
                aria-hidden="true"
                onDrop={this.onSearchHandoffDrop}
                onPaste={this.onSearchHandoffPaste}
                ref={this.onInputMountHandoff}
              />
              <div className="fake-caret" />
            </button>
          </div>
        )}
      </div>
    );
  }
}

export const Search = connect(state => ({
  Prefs: state.Prefs,
}))(_Search);
