/* setup controls for resizing (i.e. zooming) video */

(function() {
  'use strict';

  function get_video_number(video) {
    if ((!video) || video.id === undefined) { return null; }
    var match = /\d+$/.exec(video.id);
    if (!match) { return null; }
    return match[0];
  }

  // TODO: cleanup
  function setup_zoom_controls(video) {
    var video_num = get_video_number(video);
    if (!video_num) { return; }
    var video_container = document.getElementById('video_container'+video_num);
    var footer = document.getElementById('footer');
    var zoom_bar = document.getElementById('zoom_bar'+video_num);
    var zoom_value = document.getElementById('zoom_value'+video_num);

    var transforms = ['transform', 'WebkitTransform', 'MozTransform', 'msTransform', 'OTransform'];
    var i, transform;
    for (i=0; i < transforms.length; i++) {
      if(video.style[transforms[i]] !== undefined){
        transform = transforms[i];
        break;
      }
    }

    /* get initial scale from the html */
    var start_scale = 1;
    var new_scale = 1;
    if (zoom_value) {
      start_scale = new_scale = zoom_value.innerHTML.replace('%', '')/100;
    }

    var translate_x = 0;
    var translate_y = 0;

    // to be set during mouse down
    var initial_scroll_left = 0;
    var initial_scroll_top = 0;

    // higher mouse_video_delta_multiplier means faster scaling.
    // (rouhly, mouse_distance_moved*mouse_video_delta_multiplier = change_in_video_dimensions)
    var mouse_video_delta_multiplier = 3;

    function get_page_height(scale) {
      /* return page height at a certain scale:
       * scaled video height + footer height + margins + scrollbar */
      var scrollbar = 0;
      if (video.videoWidth*scale > window.innerWidth) {
        scrollbar = 17;
      }
      if (footer) {
        return Math.round(video.videoHeight*scale + footer.clientHeight + scrollbar);
      }
      return Math.round(video.videoHeight*scale + scrollbar);
    }

    function scale_and_position(is_jump) {
      /* To keep the video centered at all times, we compensate its movement
       * during scaling with an appropriate translation to keep its top-left
       * corner in place, and we set the container's left in order to center it
       * while the video is still smaller than the window (its top remanining
       * always in place). After a dimension grows bigger than the window's, we
       * center the window instead, via scrolling.
       *
       * Notice that we need the video container in order to have the
       * document's body only see an element the size of the scaled video.
       * Without the container and with a large downscaled video, the body will
       * grow enough to contain the original video's dimensions, leaving blank
       * space to its right and/or bottom. Since the container's dimensions
       * follow the scaled video's and its overflow is hidden, we get the exact
       * same behavior from it as we would get from a small video the size of
       * the downscaled video, which is precisely what we want. */

      if (zoom_value) {
        zoom_value.innerHTML = Math.round(new_scale*100)+'%';
      }
      if (zoom_bar && zoom_bar.value !== new_scale*100) {
        // enforce minimum and maximum values
        if (new_scale > 2 && zoom_bar.value < 200) {
          zoom_bar.value = 200;
        }
        else if (new_scale < 0.1 && zoom_bar.value > 10) {
          zoom_bar.value = 10;
        }
        else {
          zoom_bar.value = new_scale*100;
        }
      }
      var new_width = video.videoWidth*new_scale;
      var new_height = video.videoHeight*new_scale;

      /* keep video's top-left corner in place (at container's top-left)
       * during scaling */
      translate_x = Math.round(video.videoWidth*(1-1/new_scale)/2);
      translate_y = Math.round(video.videoHeight*(1-1/new_scale)/2);

      video.style[transform] = 'scale('+new_scale+','+new_scale+') translate('+translate_x+'px,'+translate_y+'px)';

      // fit container to scaled video
      video_container.style.width = Math.round(new_width)+'px';
      video_container.style.height = Math.round(new_height)+'px';

      /* center container, but only until it hits the window's boundaries;
       * after that, center window instead */
      var scroll_left = initial_scroll_left;
      var scroll_top = initial_scroll_top;
      if (new_width <= window.innerWidth) {
        // center container by offsetting its dimension by half of how
        // bigger the window's is
        video_container.style.left = Math.round((window.innerWidth - new_width)/2)+'px';

        scroll_left = 0;
      }
      else {
        video_container.style.left = '0px';

        /* when scaling to 100% or coming from a scaled dimension
         * smaller than the window, center window instead of maintaing
         * the current scroll, otherwise, scroll window from current
         * position */
        if (is_jump || video.videoWidth*start_scale <= window.innerWidth) {
          scroll_left = Math.round((new_width - window.innerWidth)/2);
        }
        else {
          // scroll window by half of how much the video grew
          var width_delta = new_width - video.videoWidth*start_scale;
          scroll_left = initial_scroll_left + Math.round(width_delta/2);
        }
      }

      var new_page_height = get_page_height(new_scale);
      if (new_page_height <= window.innerHeight) {
        // center container by offsetting its dimension by half of how
        // bigger the window's is
        video_container.style.top = Math.round((window.innerHeight - new_page_height)/2)+'px';

        scroll_top = 0;
      }
      else {
        video_container.style.top = '0px';

        /* when scaling to 100% or coming from a scaled dimension
         * smaller than the window, center window instead of maintaing
         * the current scroll, otherwise, scroll window from current
         * position */
        if (is_jump || video.videoHeight*start_scale <= window.innerHeight) {
          scroll_top = Math.round((new_page_height - window.innerHeight)/2);
        }
        else {
          // scroll window by half of how much the video grew
          var height_delta = new_page_height - video.videoHeight*start_scale;
          scroll_top = initial_scroll_top + Math.round(height_delta/2);
        }
      }

      // don't try to scroll to a negative position
      if (scroll_left < 0) { scroll_left = 0; }
      if (scroll_top < 0) { scroll_top = 0; }

      window.scrollTo(scroll_left, scroll_top);
    }

    /* keep track of whether the user is performing some other action that
     * involves clicking and dragging, so we can avoid zooming or scolling then */
    var doing_something_else = false;
    video.addEventListener('seeking', function() {
      // ignore the automatic seek event at every loop
      if (this.currentTime > 0) {
        doing_something_else = true;
      }
    });
    video.addEventListener('volumechange', function() {
      doing_something_else = true;
    });

    function scale_on_mouse_drag(event) {
      //starting coordinates
      var start_point = [event.clientX, event.clientY];

      // starting window position
      initial_scroll_left = window.pageXOffset;
      initial_scroll_top = window.pageYOffset;

      var might_be_click = true; // mouse hasn't moved enough, might be a click
      doing_something_else = false;

      function scale_mouse_move_handler(event) {
        /* Scale video according to how farther away on the Y axis from the
         * mousedown position is the mouse now. Down grows the video and up
         * shrinks it. */

        // don't zoom if seeking or changing volume
        if (doing_something_else) { return; }

        var current_point = [event.clientX, event.clientY];
        // don't zoom further if mouse went above the window's top
        if (current_point[1] < 0) {
          current_point[1] = 0;
        }

        var mouse_delta = current_point[1] - start_point[1];

        // arbitrarily chosen amount of pixels the user might accidentally move
        // when actually trying to only click
        var mouse_delta_tolerance = 3;
        if (Math.abs(current_point[0]-start_point[0]) > mouse_delta_tolerance ||
            Math.abs(current_point[1]-start_point[1]) > mouse_delta_tolerance) {
              might_be_click = false;
            }

        // perform zoom
        if (!might_be_click) {

          // scale multiplies the video's dimensions, so we calculate the
          // new scale based on how many pixels we want to add (or
          // subtract) to the video's diagonal
          var video_diagonal = Math.sqrt(Math.pow(video.videoWidth, 2) + Math.pow(video.videoHeight, 2));
          var new_scale_candidate = start_scale + (mouse_delta*mouse_video_delta_multiplier)/video_diagonal;

          // enforce minimum scale (chosen arbitrarily)
          var minimum_dimension = 20;
          if (video.videoWidth*new_scale_candidate >= minimum_dimension &&
              video.videoHeight*new_scale_candidate >= minimum_dimension) {
            new_scale = new_scale_candidate;
          }

          scale_and_position(false);

          event.preventDefault();
          return false;
        }
      }

      function scale_mouse_up_handler() {
        start_scale = new_scale;
        document.removeEventListener('mousemove', scale_mouse_move_handler);
        document.removeEventListener('mouseup', scale_mouse_up_handler);
      }

      function scale_mouse_click_handler(event) {
        // stop click's default action unless the mouse was not moved
        video.removeEventListener('click', scale_mouse_click_handler);
        if ((!might_be_click) && (!doing_something_else)) {
          event.preventDefault();
          return false;
        }
      }

      document.addEventListener('mousemove', scale_mouse_move_handler);
      document.addEventListener('mouseup', scale_mouse_up_handler);
      video.addEventListener('click', scale_mouse_click_handler);
    }

    function scroll_on_mouse_drag(event) {
      //starting coordinates
      var start_point = [event.clientX, event.clientY];

      // starting window position
      initial_scroll_left = window.pageXOffset;
      initial_scroll_top = window.pageYOffset;

      doing_something_else = false;
      var scroll_mouse_delta_multiplier = 2;

      function scroll_mouse_move_handler(event) {
        /* scroll page in the opposite direction of mouse movement (so the user
         * is dragging the video in the direction of mouse movement) */

        // don't scroll if seeking or changing volume
        if (doing_something_else) { return; }

        var current_point = [event.clientX, event.clientY];
        var mouse_delta_x = current_point[0] - start_point[0];
        var mouse_delta_y = current_point[1] - start_point[1];

        // a little tolerance to allow seeking and changing volume before we
        // start moving the page around
        var mouse_delta_tolerance = 3;
        if (Math.abs(current_point[0]-start_point[0]) <= mouse_delta_tolerance &&
            Math.abs(current_point[1]-start_point[1]) <= mouse_delta_tolerance) {
              return;
            }

        var scroll_left = initial_scroll_left - mouse_delta_x*scroll_mouse_delta_multiplier;
        if (scroll_left < 0) {
          scroll_left = 0;
        }
        var scroll_top = initial_scroll_top - mouse_delta_y*scroll_mouse_delta_multiplier;
        if (scroll_top < 0) {
          scroll_top = 0;
        }

        window.scrollTo(scroll_left, scroll_top);
      }

      function scroll_mouse_up_handler() {
        document.removeEventListener('mousemove', scroll_mouse_move_handler);
        document.removeEventListener('mouseup', scroll_mouse_up_handler);
      }

      document.addEventListener('mousemove', scroll_mouse_move_handler);
      document.addEventListener('mouseup', scroll_mouse_up_handler);
    }

    video.addEventListener('mousedown', function(event) {
      // left button: scale
      if (event.button === 0) {
        scale_on_mouse_drag(event);
      }
      // middle button: scroll
      else if (event.button === 1) {
        scroll_on_mouse_drag(event);
      }
    });

    var zoom_normal = document.getElementById('zoom_normal'+video_num);
    if (zoom_normal) {
      zoom_normal.addEventListener('click', function() {
        // back to 100%
        new_scale = start_scale = 1;
        scale_and_position(true);
      });
    }

    function scale_to_fit() {
      //scale to fit horizontally
      new_scale = window.innerWidth/(video.videoWidth);

      //scale to fit vertically
      if (get_page_height(new_scale) > window.innerHeight) {
        /* calculate scale based on whatever height is left after we discount
         * the overhead */
        var available_height = window.innerHeight - get_page_height(0);
        // chosen arbitrarily
        var minimum_video_height = 20;
        if (available_height < minimum_video_height) {
          available_height = minimum_video_height;
        }
        new_scale = available_height/video.videoHeight;
      }

      start_scale = new_scale;
      scale_and_position(true);
    }

    var zoom_to_fit = document.getElementById('zoom_to_fit'+video_num);
    if (zoom_to_fit) {
      zoom_to_fit.addEventListener('click', function() {
        // fit to window size
        scale_to_fit();
      });
    }

    /* when going full screen, we must reset the scale to 1, otherwise the user
     * will get a video that is either too small or too big for his screen */
    video.addEventListener('dblclick', function() {
        new_scale = start_scale = 1;
        scale_and_position(true);
    });

    /* apply starting values immediately if the video is already loaded (for
     * example, if it is already in cache) or sets event listener to do so
     * once it gets loaded */
    function apply_starting_values() {
      scale_and_position();
      window.addEventListener('resize', function() {
        scale_and_position(true);
      });

      /* video container will center the video during loading until we have
       * it's dimensions available so we can do it ourselves */
      video_container.classList.remove('waiting_for_video');

      // fit window on load (only if video is big enough and user has not
      // specified a particular zoom)
      if (video.clientWidth > window.innerWidth || get_page_height(1) > window.innerHeight) {
        if (!(zoom_value && zoom_value.classList.contains('set_by_user'))) {
          scale_to_fit();
        }
      }
    }

    function can_play_handler() {
        video.removeEventListener('canplay', can_play_handler);
        apply_starting_values();
    }
    if (video.readyState >= 2) {
      apply_starting_values();
    }
    else {
      video.addEventListener('canplay', can_play_handler);
    }

    function zoom_bar_input_handler() {
      if (zoom_bar && zoom_bar.value !== new_scale*100) {
        start_scale = new_scale = zoom_bar.value/100;
        scale_and_position(true);
      }
    }

    if (zoom_bar) {
      zoom_bar.addEventListener('input', zoom_bar_input_handler);
      zoom_bar.addEventListener('change', zoom_bar_input_handler);
    }
  }

  function setup() {
    setup_zoom_controls(document.getElementById('video0'));
  }

  document.addEventListener('DOMContentLoaded', setup);
  window.addEventListener('newVideoCreated', setup);
}());
