/*******************************************************************************
 * Copyright (c) 2009, 2012 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    IBM Corporation - initial API and implementation
 *******************************************************************************/ 
//
// Plays video descriptions (including extended video descriptions and external audio fragments) on the draft HTML5 spec
//
(function () {
  var TEXT_RESOURCES = { playLabel: '\u518d\u751f', pauseLabel: '\u4e00\u6642\u505c\u6b62', stopLabel: '\u505c\u6b62', skipLabel: '10\u79d2\u9032\u3080', backLabel: '10\u79d2\u623b\u308b', volUpLabel: '\u97f3\u91cf\u5927', volDnLabel: '\u97f3\u91cf\u5c0f', timeFormat: '%m\u5206%s\u79d2', showTextLabel: '\u30c6\u30ad\u30b9\u30c8\u3092\u8868\u793a' };
  var FORMAT_REPLACE = { MSIE: 'm4a', AppleWebKit: 'm4a', _: 'oga' }; // formats for .* external audio resources (e.g., 'foo.*' is replaced with 'foo.m4a' on MSIE)

  // Parses external text track resources
  //
  var TextTrackParser = [];

  TextTrackParser.read = function (xhr) {
    for (var i = 0; i < this.length; ++i) {
      var r = this[i];
      if (r.isReadable(xhr)) {
        return r.readCues(xhr);
      }
    }

    return null;
  };

  TextTrackParser.register = function (name, isReadable, readCues) {
    this.push({ name: name, isReadable: isReadable, readCues: readCues });
  };

  // TTML (ignores layout and styles)
  //
  (function () {
    var TTML_NAMESPACE = 'http://www.w3.org/ns/ttml';
    var XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace';
    var TVD_NAMESPACE = 'http://www.eclipse.org/actf/ai/tvd';

    var frameRate = 30;
    var subFrameRate = 1;

    var timeToSecond = function (exp) {
      if (!exp) return null;
      var v = exp.match(/^(\d{2,}):(\d{2}):(\d{2})(?:\.(\d+)|:(\d{2})(?:\.(\d+))?)?$/);
      var h = v[1] * 60 * 60;
      var m = v[2] * 60;
      var s = parseFloat(v[3] + '.' + (v[4] || 0));
      var f = parseInt(v[5] || 0) / frameRate;
      var u = parseInt(v[6] || 0) / frameRate / subFrameRate;
      return h + m + s + f + u;
    };

    var wildcardToFormat = function (url) {
      if (!url) return null;
      var v = navigator.userAgent.match(/MSIE|AppleWebKit/) || ['_'];
      var f = FORMAT_REPLACE[v[0]];
      return url.replace('*', f);
    };
    
    var isTTML = function (xhr) {
      var xml = xhr.responseXML;
      if (!xml) return false;
      var tts = xml.getElementsByTagName('tt'); // xml.getElementsByTagNameNS(TTML_NAMESPACE, 'tt'); // for IE
      return tts.length == 1;
    };

    var readCues = function (xhr) {
      var xml = xhr.responseXML;
      var ps = xml.getElementsByTagName('p'); // xml.getElementsByTagNameNS(TTML_NAMESPACE, 'p'); // for IE
      var cues = [];

      for (var i = 0; i < ps.length; ++i) {
        var p = ps[i];
        var id = p.getAttribute('xml:id'); // p.getAttributeNS(XML_NAMESPACE, 'id'); // for IE
        var begin = timeToSecond(p.getAttribute('begin'));
        var end = timeToSecond(p.getAttribute('end'));
        var dur = timeToSecond(p.getAttribute('dur'));
        var text = p.textContent || p.firstChild.nodeValue;
        var extended = p.getAttribute('tvd:extended') == 'true'; // p.getAttributeNS(TVD_NAMESPACE, 'extended') == 'true'; // for IE
        var external = wildcardToFormat(p.getAttribute('tvd:external')); // p.getAttributeNS(TVD_NAMESPACE, 'external'); // for IE
        end = end || (begin + (extended ? 0 : dur));
        dur = dur || (end - begin);
        var cue = new TextTrackCue(id, begin, end, text, '', extended);
        cue.__duration__ = dur;
        cue.__external__ = external;
        cues.push(cue);
      }

      return cues;
    };

    TextTrackParser.register('TTML', isTTML, readCues);
  })();

  // WebVTT (ignores layout and styles)
  //
  (function () {
    var timeToSecond = function (exp) {
      if (!exp) return null;
      var v = exp.match(/^(?:(\d{2,}):)?(\d{2}):(\d{2}\.\d{3})$/);
      var h = (v[1] || 0) * 60 * 60;
      var m = v[2] * 60;
      var s = parseFloat(v[3]);
      return h + m + s;
    };

    var wildcardToFormat = function (url) {
      if (!url) return null;
      var v = navigator.userAgent.match(/MSIE|AppleWebKit/) || ['_'];
      var f = FORMAT_REPLACE[v[0]];
      return url.replace('*', f);
    };

    var isWebVTT = function (xhr) {
      var txt = xhr.responseText;
      return txt.match(/^WEBVTT\b/);
    };

    var readCues = function (xhr) {
      var txt = xhr.responseText.replace(/\r\n|\r/g, '\n');
      var elems = txt.split(/\n{2,}/);
      var cues = [];

      for (var i = 0; i < elems.length; ++i) {
        var e = elems[i];
        var m = e.match(/^(?:(\w+)\n)?((?:\d{2,})?:\d{2}:\d{2}\.\d{3})[ \t]+-->[ \t]+((?:\d{2,})?:\d{2}:\d{2}\.\d{3})(?:[ \t]+([^\n]+))?\n([\w\W]+)$/m);
        
        if (m) {
          var id = m[1] || '';
          var begin = timeToSecond(m[2]);
          var end = timeToSecond(m[3]);
          var settings = m[4] || '';
          var extended = settings.match(/\bextended\b/);
          var text = m[5];
          var m1 = settings.match(/\bduration=((?:\d{2,}:)?\d{2}:\d{2}\.\d{3})/);
          var dur = m1 ? timeToSecond(m1[1]) : 0;
          var m2 = settings.match(/\bexternal=([^,]+)/);
          var external = wildcardToFormat(m2 ? m2[1] : '');
          end = end || (begin + (extended ? 0 : dur));
          dur = dur || (end - begin);
          var cue = new TextTrackCue(id, begin, end, text, '', extended);
          cue.__duration__ = dur;
          cue.__external__ = external;
          cues.push(cue);
        }
      }

      return cues;
    };

    TextTrackParser.register('WebVTT', isWebVTT, readCues);
  })();

  // Shows/reads aloud captions/descriptions
  //
  var initCues = function (track, video) {
    var onenter = function (e) {
      var cue = e.target;

      if (cue.track.__kind__ == 'descriptions') {
        if (cue.__audio__) { // external resource exists
          cue.__audio__.currentTime = 0;
          cue.__audio__.play();
          video.__descriptions__.setAttribute('aria-live', 'off');
          showText(cue.text, video.__descriptions__);
        }
        else {               // uses screen reader
          video.__descriptions__.setAttribute('aria-live', 'assertive');
          showText(cue.text, video.__descriptions__);
        }
      }
      else if (cue.track.__kind__ == 'captions') {
        showText(cue.text, video.__captions__);
      }
    };

    var onexit = function (e) {
      var cue = e.target;

      if (cue.track.__kind__ == 'descriptions') {
        var isExtended = cue.pauseOnExit && video.paused;

        if (isExtended) {
          if (cue.__audio__) {
            if (cue.__audio__.ended) {
              showText('', video.__descriptions__);
              video.play();
            }
            else {
              cue.__audio__.addEventListener('ended', function () {
                showText('', video.__descriptions__);
                video.play();
              }, false);
            }
          }
          else {
            if (cue.endTime - cue.startTime >= cue.__duration__) {
              showText('', video.__descriptions__);
              video.play();
            }
            else {
              setTimeout(function () {
                showText('', video.__descriptions__);
                video.play();
              }, (cue.__duration__ - cue.endTime + cue.startTime) * 1000);
            }
          }
        }
        else {
          if (cue.__audio__) {
            cue.__audio__.pause();
            showText('', video.__descriptions__);
          }
          else {
            showText('', video.__descriptions__);
          }
        }
      }
      else if (cue.track.__kind__ == 'captions') {
        showText('', video.__captions__);
      }
    };

    var showText = function (text, container) {
      var oldText = container.firstChild;
      var newText = document.createElement('span');
      newText.appendChild(document.createTextNode(text));
      container.replaceChild(newText, oldText);
    };
    
    var cues = track.cues;

    for (var i = 0; i < cues.length; ++i) {
      var c = cues[i];

      if (c.__external__) {
        c.__audio__ = new Audio(c.__external__);
      }

      c.onenter = onenter;
      c.onexit = onexit;
    }
  };

  var loadCues = function (track, video) {
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function () {
      if (xhr.readyState == 4) {
        switch (xhr.status) {
        case 200:
        case 0: // for local files
          var cues = TextTrackParser.read(xhr);

          if (cues) {
            for (var i = 0; i < cues.length; ++i) {
              track.track.addCue(cues[i]);
            }

            track.track.mode = track['default'] ? TextTrack.SHOWING : TextTrack.DISABLED; // .default causes an error in IE
            initCues(track.track, video);
          }
          
          break;
        }
      }
    };

    xhr.open('GET', track.src, true);
    xhr.send(null);
  };

  // Sets up control buttons and caption/description display areas
  //
  var createVDControls = function (video) {
    var paused = video.paused;

    var e = document.createElement('div');
    e.className = 'vd-controls';
    video.__controls__ = video.parentNode.appendChild(e);

    var toggleButton = e.appendChild(document.createElement('div')).appendChild(document.createElement('button'));
    toggleButton.appendChild(document.createTextNode(''));
    toggleButton.accessKey = 'p';
    toggleButton.onclick = function () {
      if (paused) {
        this.__update__(paused = false);
        video.play();
      }
      else {
        this.__update__(paused = true);
        video.pause();
      }
    };
    toggleButton.__update__ = function () {
      this.firstChild.nodeValue = paused ? TEXT_RESOURCES.playLabel : TEXT_RESOURCES.pauseLabel;
    };
    toggleButton.__update__();

    var stopButton = e.appendChild(document.createElement('div')).appendChild(document.createElement('button'));
    stopButton.appendChild(document.createTextNode(TEXT_RESOURCES.stopLabel));
    stopButton.accessKey = 's';
    stopButton.onclick = function () {
      video.currentTime = 0;
      video.play();
      video.pause();
      toggleButton.__update__(paused = true);
    };

    var backButton = e.appendChild(document.createElement('div')).appendChild(document.createElement('button'));
    backButton.appendChild(document.createTextNode(TEXT_RESOURCES.backLabel));
    backButton.accessKey = 'b';
    backButton.onclick = function () {
      video.currentTime -= 10;
      video.play();
      toggleButton.__update__(paused = false);
    };

    var skipButton = e.appendChild(document.createElement('div')).appendChild(document.createElement('button'));
    skipButton.appendChild(document.createTextNode(TEXT_RESOURCES.skipLabel));
    skipButton.accessKey = 'f';
    skipButton.onclick = function () {
      video.currentTime += 10;
      video.play();
      toggleButton.__update__(paused = false);
    };

    var volDnButton = e.appendChild(document.createElement('div')).appendChild(document.createElement('button'));
    volDnButton.appendChild(document.createTextNode(TEXT_RESOURCES.volDnLabel));
    volDnButton.accessKey = 'n';
    volDnButton.onclick = function () {
      video.volume -= .1;
    };

    var volUpButton = e.appendChild(document.createElement('div')).appendChild(document.createElement('button'));
    volUpButton.appendChild(document.createTextNode(TEXT_RESOURCES.volUpLabel));
    volUpButton.accessKey = 'u';
    volUpButton.onclick = function () {
      video.volume += .1;
    };

    var formatTime = function (t) {
      var m = parseInt(t / 60);
      var s = parseInt(t % 60);
      return TEXT_RESOURCES.timeFormat.replace('%m', m).replace('%s', s);
    };

    var timeDisplay = e.appendChild(document.createElement('div')).appendChild(document.createElement('span'));
    timeDisplay.appendChild(document.createTextNode(formatTime(0)));

    video.addEventListener('timeupdate', function (e) {
      timeDisplay.firstChild.nodeValue = formatTime(e.target.currentTime);
    }, false);

    video.addEventListener('ended', function (e) {
      toggleButton.__update__(paused = true);
    }, false);

    video.addEventListener('play', function (e) {
      toggleButton.disabled = false;
      stopButton.disabled = false;
      backButton.disabled = false;
      skipButton.disabled = false;
    }, false);

    video.addEventListener('pause', function (e) {
      if (paused) { // paused by user (not for extended descriptions)
        return;
      }
      toggleButton.disabled = true;
      stopButton.disabled = true;
      backButton.disabled = true;
      skipButton.disabled = true;
    }, false);
  };

  var createVDDisplays = function (video) {
    var e1 = document.createElement('div');
    e1.className = 'vd-descriptions';
    e1.appendChild(document.createElement('span'));
    video.__descriptions__ = video.parentNode.appendChild(e1);
    
    var e2 = document.createElement('div');
    e2.className = 'vd-captions';
    e2.appendChild(document.createElement('span'));
    video.__captions__ = video.parentNode.appendChild(e2);

    var e1opt = document.createElement('div');
    e1opt.className = 'vd-descriptions-config';
    var l1 = document.createElement('label');
    var c1 = document.createElement('input');
    c1.type = 'checkbox';
    c1.onclick = function () {
      if (this.checked) {
        video.__descriptions__.className = [video.__descriptions__.className, 'vd-show'].join(' ');
      }
      else {
        video.__descriptions__.className = video.__descriptions__.className.replace(/\bvd-show\b/g, '');
      }
    };
    l1.appendChild(c1);
    l1.appendChild(document.createTextNode(TEXT_RESOURCES.showTextLabel));
    e1opt.appendChild(l1);
    video.parentNode.appendChild(e1opt);
  }

  var fixVideoElement = function (video) {
    var container = document.createElement('div');
    container.className = 'vd-container';
    container.appendChild(video.parentNode.replaceChild(container, video));
    createVDControls(video);
    createVDDisplays(video);
  };

  var videoElems = document.getElementsByTagName('video');

  for (var i = 0; i < videoElems.length; ++i) {
    var v = videoElems[i];
    fixVideoElement(v);
    var trackElems = v.getElementsByTagName('track');

    for (var j = 0; j < trackElems.length; ++j) {
      var t = trackElems[j];

      if (t.kind == 'descriptions' || t.kind == 'captions' || t.kind == 'metadata' && (t.dataset.kind == 'descriptions' || t.dataset.kind == 'captions')) {
        t.track.__kind__ = t.dataset.kind || t.kind;
        loadCues(t, v);
      }
    }
  }
})();

