goog.provide('webfont.FontWatcher');
goog.require('webfont.FontWatchRunner');
goog.require('webfont.NativeFontWatchRunner');
/**
 * @typedef {Object.<string, Array.<string>>}
 */
webfont.FontTestStrings;
/**
 * @constructor
 * @param {webfont.DomHelper} domHelper
 * @param {webfont.EventDispatcher} eventDispatcher
 * @param {number=} opt_timeout
 */
webfont.FontWatcher = function(domHelper, eventDispatcher, opt_timeout) {
  this.domHelper_ = domHelper;
  this.eventDispatcher_ = eventDispatcher;
  this.currentlyWatched_ = 0;
  this.last_ = false;
  this.success_ = false;
  this.timeout_ = opt_timeout;
};
goog.scope(function () {
  var FontWatcher = webfont.FontWatcher,
	  FontWatchRunner = webfont.FontWatchRunner,
	  NativeFontWatchRunner = webfont.NativeFontWatchRunner;
  /**
   * @type {null|boolean}
   */
  FontWatcher.SHOULD_USE_NATIVE_LOADER = null;
  /**
   * @return {string}
   */
  FontWatcher.getUserAgent = function () {
	return window.navigator.userAgent;
  };
  /**
   * @return {string}
   */
  FontWatcher.getVendor = function () {
	return window.navigator.vendor;
  };
  /**
   * Returns true if this browser has support for
   * the CSS font loading API.
   *
   * @return {boolean}
   */
  FontWatcher.shouldUseNativeLoader = function () {
	if (FontWatcher.SHOULD_USE_NATIVE_LOADER === null) {
	  if (!!window.FontFace) {
		var match = /Gecko.*Firefox\/(\d+)/.exec(FontWatcher.getUserAgent());
		var safari10Match = /OS X.*Version\/10\..*Safari/.exec(FontWatcher.getUserAgent()) && /Apple/.exec(FontWatcher.getVendor());
		if (match) {
		  FontWatcher.SHOULD_USE_NATIVE_LOADER = parseInt(match[1], 10) > 42;
		} else if (safari10Match) {
		  FontWatcher.SHOULD_USE_NATIVE_LOADER = false;
		} else {
		  FontWatcher.SHOULD_USE_NATIVE_LOADER = true;
		}
	  } else {
		FontWatcher.SHOULD_USE_NATIVE_LOADER = false;
	  }
	}
	return FontWatcher.SHOULD_USE_NATIVE_LOADER;
  };
  /**
   * Watches a set of font families.
   * @param {Array.<webfont.Font>} fonts The fonts to watch.
   * @param {webfont.FontTestStrings} fontTestStrings The font test strings for
   *	 each family.
   * @param {Object.<String, boolean>} metricCompatibleFonts
   * @param {boolean} last True if this is the last set of fonts to watch.
   */
  FontWatcher.prototype.watchFonts = function(fonts,
	  fontTestStrings, metricCompatibleFonts, last) {
	var length = fonts.length,
		testStrings = fontTestStrings || {};
	if (length === 0 && last) {
	  this.eventDispatcher_.dispatchInactive();
	  return;
	}
	this.currentlyWatched_ += fonts.length;
	if (last) {
	  this.last_ = last;
	}
	var i, fontWatchRunners = [];
	for (i = 0; i < fonts.length; i++) {
	  var font = fonts[i],
		  testString = testStrings[font.getName()];
	  this.eventDispatcher_.dispatchFontLoading(font);
	  var fontWatchRunner = null;
	  if (FontWatcher.shouldUseNativeLoader()) {
		fontWatchRunner = new NativeFontWatchRunner(
			goog.bind(this.fontActive_, this),
			goog.bind(this.fontInactive_, this),
			this.domHelper_,
			font,
			this.timeout_,
			testString
		  );
	  } else {
		fontWatchRunner = new FontWatchRunner(
		  goog.bind(this.fontActive_, this),
		  goog.bind(this.fontInactive_, this),
		  this.domHelper_,
		  font,
		  this.timeout_,
		  metricCompatibleFonts,
		  testString
		);
	  }
	  fontWatchRunners.push(fontWatchRunner);
	}
	for (i = 0; i < fontWatchRunners.length; i++) {
	  fontWatchRunners[i].start();
	}
  };
  /**
   * Called by a FontWatchRunner when a font has been detected as active.
   * @param {webfont.Font} font
   * @private
   */
  FontWatcher.prototype.fontActive_ = function(font) {
	this.eventDispatcher_.dispatchFontActive(font);
	this.success_ = true;
	this.decreaseCurrentlyWatched_();
  };
  /**
   * Called by a FontWatchRunner when a font has been detected as inactive.
   * @param {webfont.Font} font
   * @private
   */
  FontWatcher.prototype.fontInactive_ = function(font) {
	this.eventDispatcher_.dispatchFontInactive(font);
	this.decreaseCurrentlyWatched_();
  };
  /**
   * @private
   */
  FontWatcher.prototype.decreaseCurrentlyWatched_ = function() {
	if (--this.currentlyWatched_ == 0 && this.last_) {
	  if (this.success_) {
		this.eventDispatcher_.dispatchActive();
	  } else {
		this.eventDispatcher_.dispatchInactive();
	  }
	}
  };
});
