// 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.ChromeVoxHTMLTimeWidget');

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

/**
 * A class containing the information needed to speak
 * a text change event to the user.
 *
 * @constructor
 * @param {Element} timeElem The time widget element.
 * @param {cvox.TtsInterface} tts The TTS object from ChromeVox.
 */
cvox.ChromeVoxHTMLTimeWidget = function(timeElem, tts) {
  var self = this;
  this.timeElem_ = timeElem;
  this.timeTts_ = tts;
  this.pHours_ = -1;
  this.pMinutes_ = -1;
  this.pSeconds_ = 0;
  this.pMilliseconds_ = 0;
  this.pAmpm_ = '';
  this.pos_ = 0;
  this.maxPos_ = 2;
  this.keyListener_ = function(evt) {
    self.eventHandler_(evt);
  };
  this.blurListener_ = function(evt) {
    self.shutdown();
  };
  if (this.timeElem_.hasAttribute('step')) {
    var step = this.timeElem_.getAttribute('step');
    if (step > 0) { // 0 or invalid values show hh:mm AM/PM
      if (step >= 1) {
        this.maxPos_ = 3; // Anything larger than 1 shows hh:mm:ss AM/PM
      } else {
        this.maxPos_ = 4; // Anything less than 1 shows hh:mm:ss.ms AM/PM
      }
    }
  }

  // Ensure we have a reasonable value to start with.
  if (this.timeElem_.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.timeElem_.dispatchEvent(evt);
    evt = document.createEvent('KeyboardEvent');
    evt.initKeyboardEvent(
          'keyup', true, true, window, 'Left', 0, false, false, false, false);
    this.timeElem_.dispatchEvent(evt);
  }

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

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

/**
 * Initialize to midnight.
 * @private
 */
cvox.ChromeVoxHTMLTimeWidget.prototype.forceInitTime_ = function() {
  this.timeElem_.setAttribute('value', '12:00');
};

/**
 * Called when the position changes.
 * @private
 */
cvox.ChromeVoxHTMLTimeWidget.prototype.handlePosChange_ = function() {
  if (this.pos_ < 0) {
    this.pos_ = 0;
  }
  if (this.pos_ > this.maxPos_) {
    this.pos_ = this.maxPos_;
  }
  // Reset the cached state of the new field so that the field will be spoken
  // in the update.
  if (this.pos_ == this.maxPos_) {
    this.pAmpm_ = '';
    return;
  }
  switch (this.pos_) {
    case 0:
      this.pHours_ = -1;
      break;
    case 1:
      this.pMinutes_ = -1;
      break;
    case 2:
      this.pSeconds_ = -1;
      break;
    case 3:
      this.pMilliseconds_ = -1;
      break;
  }
};

/**
 * @param {boolean} shouldSpeakLabel True if the label should be spoken.
 * @private
 */
cvox.ChromeVoxHTMLTimeWidget.prototype.update_ = function(shouldSpeakLabel) {
  var splitTime = this.timeElem_.value.split(':');
  if (splitTime.length < 1) {
    this.forceInitTime_();
    return;
  }

  var hours = splitTime[0];
  var minutes = -1;
  var seconds = 0;
  var milliseconds = 0;
  var ampm = cvox.ChromeVox.msgs.getMsg('timewidget_am');
  if (splitTime.length > 1) {
    minutes = splitTime[1];
  }
  if (splitTime.length > 2) {
    var splitSecondsAndMilliseconds = splitTime[2].split('.');
    seconds = splitSecondsAndMilliseconds[0];
    if (splitSecondsAndMilliseconds.length > 1) {
      milliseconds = splitSecondsAndMilliseconds[1];
    }
  }
  if (hours > 12) {
    hours = hours - 12;
    ampm = cvox.ChromeVox.msgs.getMsg('timewidget_pm');
  }
  if (hours == 12) {
    ampm = cvox.ChromeVox.msgs.getMsg('timewidget_pm');
  }
  if (hours == 0) {
    hours = 12;
    ampm = cvox.ChromeVox.msgs.getMsg('timewidget_am');
  }

  var changeMessage = '';

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

  if (hours != this.pHours_) {
    changeMessage = changeMessage + hours + ' ' +
        cvox.ChromeVox.msgs.getMsg('timewidget_hours') + '\n';
    this.pHours_ = hours;
  }

  if (minutes != this.pMinutes_) {
    changeMessage = changeMessage + minutes + ' ' +
        cvox.ChromeVox.msgs.getMsg('timewidget_minutes') + '\n';
    this.pMinutes_ = minutes;
  }

  if (seconds != this.pSeconds_) {
    changeMessage = changeMessage + seconds + ' ' +
        cvox.ChromeVox.msgs.getMsg('timewidget_seconds') + '\n';
    this.pSeconds_ = seconds;
  }

  if (milliseconds != this.pMilliseconds_) {
    changeMessage = changeMessage + milliseconds + ' ' +
        cvox.ChromeVox.msgs.getMsg('timewidget_milliseconds') + '\n';
    this.pMilliseconds_ = milliseconds;
  }

  if (ampm != this.pAmpm_) {
    changeMessage = changeMessage + ampm;
    this.pAmpm_ = ampm;
  }

  if (changeMessage.length > 0) {
    this.timeTts_.speak(changeMessage, cvox.QueueMode.FLUSH, null);
  }
};

/**
 * @param {Object} evt The event to handle.
 * @private
 */
cvox.ChromeVoxHTMLTimeWidget.prototype.eventHandler_ = function(evt) {
  var shouldSpeakLabel = false;
  if (evt.type == 'keydown') {
    if (((evt.keyCode == 9) && !evt.shiftKey) || (evt.keyCode == 39)) {
      this.pos_++;
      this.handlePosChange_();
      shouldSpeakLabel = true;
    }
    if (((evt.keyCode == 9) && evt.shiftKey) || (evt.keyCode == 37)) {
      this.pos_--;
      this.handlePosChange_();
      shouldSpeakLabel = true;
    }
  }
  this.update_(shouldSpeakLabel);
};
