/* 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/. */

// Progress heartbeat timer duration (ms)
const kHeartbeatDuration = 1000;
// Start and end progress screen css margins as percentages
const kProgressMarginStart = 30;
const kProgressMarginEnd = 70;

const WebProgress = {
  _progressActive: false,

  init: function init() {
    messageManager.addMessageListener("Content:StateChange", this);
    messageManager.addMessageListener("Content:LocationChange", this);
    messageManager.addMessageListener("Content:SecurityChange", this);
    Elements.progress.addEventListener("transitionend", this._progressTransEnd, true);
    return this;
  },

  receiveMessage: function receiveMessage(aMessage) {
    let json = aMessage.json;
    let tab = Browser.getTabForBrowser(aMessage.target);

    switch (aMessage.name) {
      case "Content:StateChange": {
        if (json.stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
          if (json.stateFlags & Ci.nsIWebProgressListener.STATE_START)
            this._windowStart(json, tab);
          else if (json.stateFlags & Ci.nsIWebProgressListener.STATE_STOP)
            this._windowStop(json, tab);
        }

        if (json.stateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
          if (json.stateFlags & Ci.nsIWebProgressListener.STATE_START)
            this._networkStart(json, tab);
          else if (json.stateFlags & Ci.nsIWebProgressListener.STATE_STOP)
            this._networkStop(json, tab);
        }

        this._progressStep();
        break;
      }

      case "Content:LocationChange": {
        this._locationChange(json, tab);
        this._progressStep();
        break;
      }

      case "Content:SecurityChange": {
        this._securityChange(json, tab);
        this._progressStep();
        break;
      }
    }
  },

  _securityChange: function _securityChange(aJson, aTab) {
    // Don't need to do anything if the data we use to update the UI hasn't changed
    if (aTab.state == aJson.state && !aTab.hostChanged)
      return;

    aTab.hostChanged = false;
    aTab.state = aJson.state;

    if (aTab == Browser.selectedTab) {
      IdentityUI.checkIdentity();
    }
  },

  _locationChange: function _locationChange(aJson, aTab) {
    let spec = aJson.location;
    let location = spec.split("#")[0]; // Ignore fragment identifier changes.

    if (aTab == Browser.selectedTab)
      BrowserUI.updateURI();

    let locationHasChanged = (location != aTab.browser.lastLocation);
    if (locationHasChanged) {
      Browser.getNotificationBox(aTab.browser).removeTransientNotifications();
      aTab.resetZoomLevel();
      aTab.hostChanged = true;
      aTab.browser.lastLocation = location;
      aTab.browser.userTypedValue = "";
      aTab.browser.appIcon = { href: null, size:-1 };

#ifdef MOZ_CRASHREPORTER
      if (CrashReporter.enabled)
        CrashReporter.annotateCrashReport("URL", spec);
#endif
      this._waitForLoad(aTab);
    }

    let event = document.createEvent("UIEvents");
    event.initUIEvent("URLChanged", true, false, window, locationHasChanged);
    aTab.browser.dispatchEvent(event);
  },

  _waitForLoad: function _waitForLoad(aTab) {
    let browser = aTab.browser;

    aTab._firstPaint = false;

    browser.messageManager.addMessageListener("Browser:FirstPaint", function firstPaintListener(aMessage) {
      browser.messageManager.removeMessageListener(aMessage.name, arguments.callee);
      aTab._firstPaint = true;
      aTab.scrolledAreaChanged(true);
      aTab.updateThumbnailSource();
    });
  },

  _networkStart: function _networkStart(aJson, aTab) {
    aTab.startLoading();

    if (aTab == Browser.selectedTab) {
      BrowserUI.update(TOOLBARSTATE_LOADING);

      // We should at least show something in the URLBar until
      // the load has progressed further along
      if (aTab.browser.currentURI.spec == "about:blank")
        BrowserUI.updateURI({ captionOnly: true });
    }
  },

  _networkStop: function _networkStop(aJson, aTab) {
    aTab.endLoading();

    if (aTab == Browser.selectedTab) {
      BrowserUI.update(TOOLBARSTATE_LOADED);
    }
  },

  _windowStart: function _windowStart(aJson, aTab) {
    this._progressStart(aJson, aTab);
  },

  _windowStop: function _windowStop(aJson, aTab) {
    this._progressStop(aJson, aTab);
  },

  _progressStart: function _progressStart(aJson, aTab) {
    // We will get multiple calls from _windowStart, so
    // only process once.
    if (this._progressActive)
      return;

    this._progressActive = true;

    // 'Whoosh' in
    this._progressCount = kProgressMarginStart;
    Elements.progress.style.width = this._progressCount + "%"; 
    Elements.progress.removeAttribute("fade");

    // Create a pulse timer to keep things moving even if we don't
    // collect any state changes.
    setTimeout(function() {
      WebProgress._progressStepTimer();
    }, kHeartbeatDuration, this);
  },

  _stepProgressCount: function _stepProgressCount() {
    // Step toward the end margin in smaller slices as we get closer
    let left = kProgressMarginEnd - this._progressCount;
    let step = left * .05;
    this._progressCount += Math.ceil(step);

    // Don't go past the 'whoosh out' margin.
    if (this._progressCount > kProgressMarginEnd) {
      this._progressCount = kProgressMarginEnd;
    }
  },

  _progressStep: function _progressStep() {
    if (!this._progressActive)
      return;
    this._stepProgressCount();
    Elements.progress.style.width = this._progressCount + "%";
  },

  _progressStepTimer: function _progressStepTimer() {
    if (!this._progressActive)
      return;
    this._progressStep();

    setTimeout(function() {
      WebProgress._progressStepTimer();
    }, kHeartbeatDuration, this);
  },

  _progressStop: function _progressStop(aJson, aTab) {
    this._progressActive = false;
    // 'Whoosh out' and fade
    Elements.progress.style.width = "100%"; 
    Elements.progress.setAttribute("fade", true);
  },

  _progressTransEnd: function _progressTransEnd(data) {
    if (!Elements.progress.hasAttribute("fade"))
      return;
    // Close out fade finished, reset
    if (data.propertyName == "opacity") {
      Elements.progress.style.width = "0px"; 
    }
  },
};


