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

goog.provide('cvox.ChromeVoxHTMLDateWidget');

/**
 * @fileoverview Gives the user spoken feedback as they interact with the date
 * widget (input type=date).
 *
 */

/**
 * A class containing the information needed to speak
 * a text change event to the user.
 *
 * @constructor
 * @param {Element} dateElem The time widget element.
 * @param {cvox.TtsInterface} tts The TTS object from ChromeVox.
 */
cvox.ChromeVoxHTMLDateWidget = function(dateElem, tts){
  var self = this;
  /**
   * Currently selected field in the widget.
   * @type {number}
   * @private
   */
  this.pos_ = 0;
  var maxpos = 2;
  if (dateElem.type == 'month' || dateElem.type == 'week') {
    maxpos = 1;
  }
  /**
   * The maximum number of fields in the widget.
   * @type {number}
   * @private
   */
  this.maxPos_ = maxpos;
  /**
   * The HTML node of the widget.
   * @type {Node}
   * @private
   */
  this.dateElem_ = dateElem;
  /**
   * A handle to the ChromeVox TTS object.
   * @type {Object}
   * @private
   */
  this.dateTts_ = tts;
  /**
   * The previous value of the year field.
   * @type {number}
   * @private
   */
  this.pYear_ = -1;
  /**
   * The previous value of the month field.
   * @type {number}
   * @private
   */
  this.pMonth_ = -1;
  /**
   * The previous value of the week field.
   * @type {number}
   * @private
   */
  this.pWeek_ = -1;
  /**
   * The previous value of the day field.
   * @type {number}
   * @private
   */
  this.pDay_ = -1;

  // Use listeners to make this work when running tests inside of ChromeVox.
  this.keyListener_ = function(evt) {
    self.eventHandler_(evt);
  }
  this.blurListener_ = function(evt) {
    self.shutdown();
  }

  // Ensure we have a reasonable value to start with.
  if (this.dateElem_.value.length == 0) {
    this.forceInitTime_();
  }

  // Move the cursor to the first position so that we are guaranteed to start
  // off at the hours position.
  for (var i = 0; i < this.maxPos_; i++) {
    var evt = document.createEvent('KeyboardEvent');
    evt.initKeyboardEvent(
          'keydown', true, true, window, 'Left', 0, false, false, false, false);
    this.dateElem_.dispatchEvent(evt);
    evt = document.createEvent('KeyboardEvent');
    evt.initKeyboardEvent(
          'keyup', true, true, window, 'Left', 0, false, false, false, false);
    this.dateElem_.dispatchEvent(evt);
  }

  this.dateElem_.addEventListener('keydown', this.keyListener_, false);
  this.dateElem_.addEventListener('keyup', this.keyListener_, false);
  this.dateElem_.addEventListener('blur', this.blurListener_, false);
  this.update_(true);
};

/**
 * Removes the key listeners for the time widget.
 *
 */
cvox.ChromeVoxHTMLDateWidget.prototype.shutdown = function() {
  this.dateElem_.removeEventListener('blur', this.blurListener_, false);
  this.dateElem_.removeEventListener('keydown', this.keyListener_, false);
  this.dateElem_.removeEventListener('keyup', this.keyListener_, false);
};

/**
 * Forces a sensible default value so that there is something there that can
 * be inspected with JS.
 * @private
 */
cvox.ChromeVoxHTMLDateWidget.prototype.forceInitTime_ = function() {
  var currentDate = new Date();
  var valueString = '';
  var yearString = currentDate.getFullYear() + '';
  // Date.getMonth starts at 0, but the value for the HTML5 date widget needs to
  // start at 1.
  var monthString = currentDate.getMonth() + 1 + '';
  if (monthString.length < 2) {
    monthString = '0' + monthString; // Month format is MM.
  }
  var dayString = currentDate.getDate() + '';

  switch (this.dateElem_.type) {
    case 'month':
      valueString = yearString + '-' + monthString;
      break;
    case 'week':
      // Based on info from: http://www.merlyn.demon.co.uk/weekcalc.htm#WNR
      currentDate.setHours(0,0,0);
      // Set to nearest Thursday: current date + 4 - current day number
      // Make Sunday's day number 7
      currentDate.setDate(
          currentDate.getDate() + 4 - (currentDate.getDay()||7));
      // Get first day of year
      var yearStart = new Date(currentDate.getFullYear(),0,1);
      // Calculate full weeks to nearest Thursday
      var weekString =
          Math.ceil(( ( (currentDate - yearStart) / 86400000) + 1)/7) + '';
      if (weekString.length < 2) {
        weekString = '0' + weekString; // Week format is WXX.
      }
      weekString = 'W' + weekString;
      valueString = yearString + '-' + weekString;
      break;
    default:
      valueString = yearString + '-' + monthString + '-' + dayString;
      break;
  }
  this.dateElem_.setAttribute('value', valueString);
};

/**
 * Ensure that the position stays within bounds.
 * @private
 */
cvox.ChromeVoxHTMLDateWidget.prototype.handlePosChange_ = function() {
  this.pos_ = Math.max(this.pos_, 0);
  this.pos_ = Math.min(this.pos_, this.maxPos_);
  // TODO (clchen, dtseng): Make this logic i18n once there is a way to
  // determine what the date format actually is. For now, assume that:
  // date  == mm/dd/yyyy
  // week  == ww/yyyy
  // month == mm/yyyy.
  switch (this.pos_) {
    case 0:
      if (this.dateElem_.type == 'week') {
        this.pWeek_ = -1;
      } else {
        this.pMonth_ = -1;
      }
      break;
    case 1:
      if (this.dateElem_.type == 'date') {
        this.pDay_ = -1;
      } else {
        this.pYear_ = -1;
      }
      break;
    case 2:
      this.pYear_ = -1;
      break;
  }
};

/**
 * Speaks any changes to the control.
 * @private
 * @param {boolean} shouldSpeakLabel Whether or not to speak the label.
 */
cvox.ChromeVoxHTMLDateWidget.prototype.update_ = function(shouldSpeakLabel) {
  var splitDate = this.dateElem_.value.split("-");
  if (splitDate.length < 1){
    this.forceInitTime_();
    return;
  }

  var year = -1;
  var month = -1;
  var week = -1;
  var day = -1;

  year = parseInt(splitDate[0], 10);

  if (this.dateElem_.type == 'week') {
    week = parseInt(splitDate[1].replace('W', ''), 10);
  } else if (this.dateElem_.type == 'date') {
    month = parseInt(splitDate[1], 10);
    day = parseInt(splitDate[2], 10);
  } else {
    month = parseInt(splitDate[1], 10);
  }

  var changeMessage = ''

  if (shouldSpeakLabel) {
    changeMessage = cvox.DomUtil.getName(this.dateElem_, true, true) + '\n';
  }

  if (week != this.pWeek_) {
    changeMessage = changeMessage +
        cvox.ChromeVox.msgs.getMsg('datewidget_week') + week + '\n';
    this.pWeek_ = week;
  }

  if (month != this.pMonth_) {
    var monthName = '';
    switch (month) {
      case 1:
        monthName = cvox.ChromeVox.msgs.getMsg('datewidget_january');
        break;
      case 2:
        monthName = cvox.ChromeVox.msgs.getMsg('datewidget_february');
        break;
      case 3:
        monthName = cvox.ChromeVox.msgs.getMsg('datewidget_march');
        break;
      case 4:
        monthName = cvox.ChromeVox.msgs.getMsg('datewidget_april');
        break;
      case 5:
        monthName = cvox.ChromeVox.msgs.getMsg('datewidget_may');
        break;
      case 6:
        monthName = cvox.ChromeVox.msgs.getMsg('datewidget_june');
        break;
      case 7:
        monthName = cvox.ChromeVox.msgs.getMsg('datewidget_july');
        break;
      case 8:
        monthName = cvox.ChromeVox.msgs.getMsg('datewidget_august');
        break;
      case 9:
        monthName = cvox.ChromeVox.msgs.getMsg('datewidget_september');
        break;
      case 10:
        monthName = cvox.ChromeVox.msgs.getMsg('datewidget_october');
        break;
      case 11:
        monthName = cvox.ChromeVox.msgs.getMsg('datewidget_november');
        break;
      case 12:
        monthName = cvox.ChromeVox.msgs.getMsg('datewidget_december');
        break;
    }
    changeMessage = changeMessage + monthName + '\n';
    this.pMonth_ = month;
  }

  if (day != this.pDay_) {
    changeMessage = changeMessage + day + '\n';
    this.pDay_ = day;
  }

  if (year != this.pYear_) {
    changeMessage = changeMessage + year + '\n';
    this.pYear_ = year;
  }

  if (changeMessage.length > 0) {
    this.dateTts_.speak(changeMessage, 0, null);
  }
};

/**
 * Handles user key events.
 * @private
 * @param {Event} evt The event to be handled.
 */
cvox.ChromeVoxHTMLDateWidget.prototype.eventHandler_ = function(evt) {
  var shouldSpeakLabel = false;
  if (evt.type == 'keydown') {
    // Handle tab/right arrow
    if (((evt.keyCode == 9) && !evt.shiftKey) || (evt.keyCode == 39)) {
      this.pos_++;
      this.handlePosChange_();
      shouldSpeakLabel = true;
    }
    // Handle shift+tab/left arrow
    if (((evt.keyCode == 9) && evt.shiftKey) || (evt.keyCode == 37)) {
      this.pos_--;
      this.handlePosChange_();
      shouldSpeakLabel = true;
    }
    // For all other cases, fall through and let update_ decide if there are any
    // changes that need to be spoken.
  }
  this.update_(shouldSpeakLabel);
};
