/* 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/. */
"use strict";

var FindHelper = {
  _finder: null,
  _targetTab: null,
  _initialViewport: null,
  _viewportChanged: false,
  _result: null,
  _limit: 0,

  observe: function(aMessage, aTopic, aData) {
    switch(aTopic) {
      case "FindInPage:Opened": {
        this._findOpened();
        break;
      }

      case "Tab:Selected": {
        // Allow for page switching.
        this._uninit();
        break;
      }

      case "FindInPage:Closed":
        this._uninit();
        this._findClosed();
        break;
    }
  },

  _findOpened: function() {
    try {
      this._limit = Services.prefs.getIntPref("accessibility.typeaheadfind.matchesCountLimit");
    } catch (e) {
      // Pref not available, assume 0, no match counting.
      this._limit = 0;
    }

    Messaging.addListener((data) => {
      this.doFind(data);
      return this._getMatchesCountResult(data);
    }, "FindInPage:Find");

    Messaging.addListener((data) => {
      this.findAgain(data, false);
      return this._getMatchesCountResult(data);
    }, "FindInPage:Next");

    Messaging.addListener((data) => {
      this.findAgain(data, true);
      return this._getMatchesCountResult(data);
    }, "FindInPage:Prev");

    // Initialize the finder component for the current page by performing a fake find.
    this._init();
    this._finder.requestMatchesCount("", 1);
  },

  _init: function() {
    // If there's no find in progress, start one.
    if (this._finder) {
      return;
    }

    this._targetTab = BrowserApp.selectedTab;
    try {
      this._finder = this._targetTab.browser.finder;
    } catch (e) {
      throw new Error("FindHelper: " + e + "\n" +
        "JS stack: \n" + (e.stack || Components.stack.formattedStack));
    }

    this._finder.addResultListener(this);
    this._initialViewport = JSON.stringify(this._targetTab.getViewport());
    this._viewportChanged = false;
  },

  _uninit: function() {
    // If there's no find in progress, there's nothing to clean up.
    if (!this._finder) {
      return;
    }

    this._finder.removeSelection();
    this._finder.removeResultListener(this);
    this._finder = null;
    this._targetTab = null;
    this._initialViewport = null;
    this._viewportChanged = false;
  },

  _findClosed: function() {
    Messaging.removeListener("FindInPage:Find");
    Messaging.removeListener("FindInPage:Next");
    Messaging.removeListener("FindInPage:Prev");
  },

  /**
   * Request, wait for, and return the current matchesCount results for a string.
   */
  _getMatchesCountResult: function(findString) {
    // Count matches up to any provided limit.
    if (this._limit <= 0) {
      return { total: 0, current: 0, limit: 0 };
    }

    // Sync call to Finder, results available immediately.
    this._finder.requestMatchesCount(findString, this._limit);
    return this._result;
  },

  /**
   * Pass along the count results to FindInPageBar for display.
   */
  onMatchesCountResult: function(result) {
    this._result = result;
    this._result.limit = this._limit;
  },

  doFind: function(searchString) {
    if (!this._finder) {
      this._init();
    }

    this._finder.fastFind(searchString, false);
  },

  findAgain: function(searchString, findBackwards) {
    // This always happens if the user taps next/previous after re-opening the
    // search bar, and not only forces _init() but also an initial fastFind(STRING)
    // before any findAgain(DIRECTION).
    if (!this._finder) {
      this.doFind(searchString);
      return;
    }

    this._finder.findAgain(findBackwards, false, false);
  },

  onFindResult: function(aData) {
    if (aData.result == Ci.nsITypeAheadFind.FIND_NOTFOUND) {
      if (this._viewportChanged) {
        if (this._targetTab != BrowserApp.selectedTab) {
          // this should never happen
          Cu.reportError("Warning: selected tab changed during find!");
          // fall through and restore viewport on the initial tab anyway
        }
        this._targetTab.setViewport(JSON.parse(this._initialViewport));
        this._targetTab.sendViewportUpdate();
      }
    } else {
      // Disabled until bug 1014113 is fixed
      // ZoomHelper.zoomToRect(aData.rect);
      this._viewportChanged = true;
    }
  }
};
