/* XXX: This whole damn thing needs to be rewritten; it's become a
   giant ball of mud. I should know better. */

/* Globals */
var cropArea;
var cropPreviewImg;
var cropPreviewBg1;
var cropPreviewBg2;
var cropPreviewArea;
var cropPreviewAreaInner;
var cropPreviewTitle;
var cropPreviewGlass;
var cropDragHere;
var cropBg;
var cropGlass;
var cropRect;
var cropRectBg;
var cropAreaInner;
var cropSplash;
var cropInstructions;
var cropAreaCropper;
var cropSlider;
var cropSliderHandle;
var cropPreviewLoading;
var cropBleedTop;
var cropBleedLeft;
var cropBleedRight;
var cropBleedBottom;
var cropBleedTopLeft;
var cropBleedTopRight;
var cropBleedBottomLeft;
var cropBleedBottomRight;
var cropOutsideWarning;
var cropForm;

var aspect_ratio; /* Aspect ratio of the thing we need to crop */

var warningDisplayed = false;

var box_info = new Object();
box_info.x = 0;
box_info.y = 0;
box_info.width = 0;
box_info.height = 0;
box_info.deltax = 0;
box_info.deltay = 0;
box_info.dpi = 0;

var minimum_dpi;
var maximum_dpi;

var initial_crop_dpi = 0;
var initial_crop_x;
var initial_crop_y;

MAX_IMAGE_PIXELS = 1280 * 960;

function msg(m) {
  /*
  if(!isIE) {
    try {
      opera.postError(m);		
    } catch(e) {
      if(netscape.security && netscape.security.PrivilegeManager) {
        netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
        var consoleService = Components.classes['@mozilla.org/consoleservice;1']
          .getService(Components.interfaces.nsIConsoleService);
        consoleService.logStringMessage(m);
      }
    }
  }
  */
}

function bg_px_to_slot_pt_x(px) {
  return px * phys_width / box_info.width;
}

function bg_px_to_slot_pt_y(px) {
  var h = phys_height + bleed.phys_top + bleed.phys_bottom;
  return px * phys_height / box_info.height;
}

function slot_pt_to_bg_px_x(pt) {
  return parseInt((pt * box_info.width) / phys_width);
}

function slot_pt_to_bg_px_y(pt) {
  return parseInt((pt * box_info.height) / phys_height);
}

function show_outside_warning() {
  cropOutsideWarning.style.visibility = "visible";
  warningDisplayed = true;
}

function hide_outside_warning() {
  cropOutsideWarning.style.visibility = "hidden";
  warningDisplayed = false;
}

/* Attached to onload */
function cropper_main(dpi,x,y) {
  /* Assign elements to javascript variables */
  cropForm = $("cropForm");

  cropArea = $("cropArea");
  cropPreviewImg = $("cropPreviewImg");
  cropPreviewBg1 = $("cropPreviewBg1");
  cropPreviewBg2 = $("cropPreviewBg2");
  cropPreviewLoading = $("cropPreviewLoading");
  cropPreviewGlass = $("cropPreviewGlass");
  cropBg = $("cropBg");
  cropGlass = $("cropGlass");
  cropRect = $("cropRect");
  cropRectBg = $("cropRectBg");
  cropAreaInner = $("cropAreaInner");
  cropSplash = $("cropSplash");
  cropPreviewArea = $("cropPreviewArea");
  cropPreviewAreaInner = $("cropPreviewAreaInner");
  cropPreviewTitle = $("cropPreviewTitle");
  cropInstructions = $("cropInstructions");
  cropAreaCropper = $("cropAreaCropper");
  cropSlider = $("cropSlider");
  cropSliderHandle = $("cropSliderHandle");
  cropDragHere = $("cropDragHere");

  cropBleedTop = $("cropBleedTop");
  cropBleedLeft = $("cropBleedLeft");
  cropBleedRight = $("cropBleedRight");
  cropBleedBottom = $("cropBleedBottom");
  cropBleedTopLeft = $("cropBleedTopLeft");
  cropBleedTopRight = $("cropBleedTopRight");
  cropBleedBottomLeft = $("cropBleedBottomLeft");
  cropBleedBottomRight = $("cropBleedBottomRight");
  
  cropOutsideWarning = $("cropOutsideWarning");
  
  initial_crop_dpi = dpi;
  initial_crop_x = x;
  initial_crop_y = y;

  // Since we don't get the bleed included in the width from the editor,
  // add it ourselves.
  phys_width += bleed.phys_left + bleed.phys_right;
  phys_height += bleed.phys_top + bleed.phys_bottom;

  cropPreviewArea.front_buf = cropPreviewBg1;
  cropPreviewArea.back_buf = cropPreviewBg2;

  cropRect.onmousedown = on_crop_drag_start;
  cropPreviewArea.isloading = false;

  cropPreviewImg.onload = on_crop_preview_loaded;
  cropPreviewImg.src = design_trans_url;

  var filterstr = "progid:DXImageTransform.Microsoft.AlphaImageLoader(" +
    "enabled=true,sizingMethod=image,src=" + design_trans_url +")";
  
  cropPreviewGlass.style.filter = filterstr;

  var make_transparent;
  
  if(cropPreviewGlass.filters) {
    make_transparent = function(y) {
      y.style.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=30)";
      y.style.backgroundImage = "url(" + media_url + "/images/checker-ie.gif)";
    }

    cropRectBg.style.backgroundColor = "white";
    cropRectBg.style.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=0)";
  } else {
    make_transparent = function(y) {
      y.style.backgroundImage = "url(" + media_url + "/images/checker.png)";
    }
  }

  make_transparent(cropBleedTop);
  make_transparent(cropBleedLeft);
  make_transparent(cropBleedRight);
  make_transparent(cropBleedBottom);
  make_transparent(cropBleedTopLeft);
  make_transparent(cropBleedTopRight);
  make_transparent(cropBleedBottomLeft);
  make_transparent(cropBleedBottomRight);

  msg(sprintf("main finished. phys_width: %f phys_height: %f phys_bg_width: %f phys_bg_height: %f",
      phys_width, phys_height, phys_bg_width, phys_bg_height));
}

// Returns the real on-screen dimensions of the given element
function get_elem_cs(elem) {
  var results = new Object();
  results.x = elem.offsetLeft;
  results.y = elem.offsetTop;
  
  if(document.defaultView) {
    var cs = document.defaultView.getComputedStyle(elem, null);
    results.width = parseInt(cs.width);
    results.height = parseInt(cs.height);
  } else {
    results.width = elem.offsetWidth;
    results.height = elem.offsetHeight;
  }

  return results;
}

function get_window_dimens() {
  var results = new Array();
  if(window.innerWidth)
  {
    results.width = window.innerWidth;
    results.height = window.innerHeight;
  }
  else if(document.documentElement && document.documentElement.clientWidth)
  {
    results.width = document.documentElement.clientWidth;
    results.height = document.documentElement.clientHeight;
  }
  else if(document.body && document.body.clientWidth)
  {
    results.width = document.body.clientWidth;
    results.height = document.body.clientHeight;
  } else {
    throw "Couldn't find a way to determine the window height";
  }
  
  return results;
}

function comp_elem_abs_offset(elem) {
  var ox = 0;
  var oy = 0;
  for(var current = elem; current && current != window;
      current = current.offsetParent)
  {
    ox += current.offsetLeft;
    oy += current.offsetTop;
  }

  var aoff = new Array;
  aoff.x = ox;
  aoff.y = oy;
  //msg("aoff.x: " + aoff.x + " aoff.y: " + aoff.y);
  return aoff;
}

function update_cropbox() {
  var cs = cropBg.cs;
  var off_x = cs.x;
  
  /* Fucking IE hack */
  if(browser.ie && !browser.ie7) {
    off_x = 0;
  }
  
  if(browser.ie7 && bleed.px_left != undefined) {
    off_x -= bleed.px_left;
  }
  
  /* Move the cropbox to the indicated location */
  cropRect.style.width = parseInt(box_info.width) + "px";
  cropRect.style.height = parseInt(box_info.height) + "px";
  cropRect.style.left = parseInt(box_info.x + off_x) + "px";
  cropRect.style.top = parseInt(box_info.y) + "px";
  
  var y_bottom = box_info.y + box_info.height;
  
  var box_width_points = ((box_info.width + 0.0) / cs.width) * phys_bg_width;
  var box_height_points = ((box_info.height + 0.0) / cs.height) * phys_bg_height;
  var xscale = (phys_width + 0.0)/ box_width_points;
  var yscale = (phys_height + 0.0)/ box_height_points;
  
  // Points from bottom-left of unscaled image
  var xoff = -((box_info.x + 0.0)/ cs.width) * phys_bg_width;
  var yoff = -((cs.height - y_bottom + 0.0) / cs.height) * phys_bg_height;
  
  var show_warning = false;
  if(box_info.x < 0) show_warning = true;
  if(box_info.y < 0) show_warning = true;
  if(box_info.x + box_info.width > cs.width) show_warning = true;
  if(box_info.y + box_info.height > cs.height) show_warning = true;
  
  if(show_warning != warningDisplayed) {
    if(show_warning) {
      show_outside_warning();
    } else {
      hide_outside_warning();
    }
  }
  
  /*
  msg(sprintf("x: %f y: %f yb: %f boxwidth: %f boxheight: %f cropBg.width: %f " +
      "cropBg.height: %f xoff: %f yoff: %f xscale: %f yscale: %f ",
      box_info.x, box_info.y, y_bottom, box_info.width, box_info.height,
      cs.width, cs.height, xoff, yoff, xscale, yscale));
  */
  
  cropForm.elements['xoff'].value = xoff;
  cropForm.elements['yoff'].value = yoff;
  cropForm.elements['xscale'].value = xscale;
  cropForm.elements['yscale'].value = yscale;
  
  cropForm.elements['boxinfo_x'].value = box_info.x;
  cropForm.elements['boxinfo_y'].value = box_info.y;
  cropForm.elements['boxinfo_dpi'].value = box_info.dpi;
  
  /* Fucking IE hack */
  if(browser.ie) {
    cropRectBg.style.width = cropRect.style.width;
    cropRectBg.style.height = cropRect.style.height;
  }
}

function calc_cropbox_dpi() {
  var proportion = (box_info.width + 0.0) / cropBg.width;
  return (proportion * bg_width_px * 72.0) / phys_width;
}

function set_cropbox_dpi(dpi) {
  /* The first question we need to ask is: how many pixels of the
   * source are needed to represent a phys_width line at dpi DPI?
   * DPI = Pixels / Inches
   * Inches = Points / 72
   * DPI = (Pixels * 72) / Points
   * (Points * DPI) / 72 = Pixels
   */
  var source_pixels = (phys_width * dpi) / 72.0;
  
  /* Now we need to convert from source pixels to the pixels we have */
  
  var bg_pixels = ((source_pixels + 0.0) / bg_width_px) * cropBg.width;
  
  var oldwidth = box_info.width;
  var oldheight = box_info.height;
  
  box_info.width = bg_pixels;
  box_info.height = bg_pixels / aspect_ratio;
  box_info.x -= ((box_info.width - oldwidth) / 2);
  box_info.y -= ((box_info.height - oldheight) / 2);
  
  box_info.dpi = dpi;

  // Update the bleed pixels, since the bleed is expressed in terms of
  // physical units of the slot, not the source image!
  bleed.px_top = slot_pt_to_bg_px_y(bleed.phys_top);
  bleed.px_left = slot_pt_to_bg_px_x(bleed.phys_left);
  bleed.px_right = slot_pt_to_bg_px_x(bleed.phys_right);
  bleed.px_bottom = slot_pt_to_bg_px_y(bleed.phys_bottom);

  // Width of a single border
  var border_adjust = 1;
  
  // Damn IE
  if(browser.quirks) {
    border_adjust = 0;
  }
  
  cropRectBg.style.top =  bleed.px_top + "px";
  cropRectBg.style.left = bleed.px_left + "px";
  cropRectBg.style.right =  bleed.px_right + "px";
  cropRectBg.style.bottom =  bleed.px_bottom + "px";
  
  // Adjust for top and bottom borders
  cropBleedTop.style.height = (bleed.px_top - border_adjust*2) + "px";
  cropBleedTop.style.left = (bleed.px_left) + "px";
  cropBleedTop.style.right = (bleed.px_right) + "px";

  // Left and right borders
  cropBleedLeft.style.width = (bleed.px_left - border_adjust*2) + "px";
  cropBleedLeft.style.top = (bleed.px_top) + "px";
  cropBleedLeft.style.bottom = (bleed.px_bottom) + "px";

  cropBleedRight.style.width = (bleed.px_right - border_adjust*2) + "px";
  cropBleedRight.style.top = (bleed.px_top) + "px";
  cropBleedRight.style.bottom = (bleed.px_bottom) + "px";
  
  cropBleedBottom.style.height = (bleed.px_bottom - border_adjust*2) + "px";
  cropBleedBottom.style.left = (bleed.px_left) + "px";
  cropBleedBottom.style.right = (bleed.px_right) + "px";

  // Only one border per dimension on the corners
  cropBleedTopLeft.style.height = (bleed.px_top - border_adjust) + "px";
  cropBleedTopLeft.style.width = (bleed.px_left - border_adjust) + "px";
  
  cropBleedTopRight.style.height = (bleed.px_top - border_adjust) + "px";
  cropBleedTopRight.style.width = (bleed.px_right - border_adjust) + "px";
  
  cropBleedBottomLeft.style.height = (bleed.px_bottom - border_adjust) + "px";
  cropBleedBottomLeft.style.width = (bleed.px_left - border_adjust) + "px";
  
  cropBleedBottomRight.style.height = (bleed.px_bottom - border_adjust) + "px";
  cropBleedBottomRight.style.width = (bleed.px_right - border_adjust) + "px";
  
  // IE's position:absolute support is terribly broken
  if(browser.ie) {
    cropBleedLeft.style.height = cropBleedRight.style.height =
      (box_info.height - bleed.px_bottom - bleed.px_top) + "px";
    cropBleedTop.style.width = cropBleedBottom.style.width =
      (box_info.width - bleed.px_left - bleed.px_right) + "px";
    cropRectBg.width = (box_info.width - bleed.px_left - bleed.px_right) + "px";
    cropRectBg.height = (box_info.height - bleed.px_left - bleed.px_right) + "px";
  }
  
  update_cropbox();
  update_preview();
}

function dump_box_info() {
  msg(sprintf("box info: x: %f y: %f width: %f height: %f dpi %f",
      box_info.x, box_info.y, box_info.width, box_info.height,
      box_info.dpi));
}

function update_preview() {
  /* Figure out how big the background image needs to be to scale properly
     to the size of the preview */
  
  // Note: we don't include the bleed area in the preview
  
  // The "inner" area is the size of the area bound by the _inner_
  // border of the cropbox, as opposed to its outer border
  var inner_x = box_info.x + bleed.px_left;
  var inner_y = box_info.y + bleed.px_top;
  var inner_w = box_info.width - bleed.px_left - bleed.px_right;
  var inner_h = box_info.height - bleed.px_top - bleed.px_bottom;
  
  var scale_factor = (cropPreviewImg.width + 0.0) / inner_w;
  
  // crop units * scale_factor = preview_units
  // preview units / scale_factor = crop units
  
  var bg_width = cropBg.width * scale_factor;
  var bg_height = cropBg.height * scale_factor;
  var total_pixels = bg_width * bg_height;

  var src = sprintf(cropimg_url, bg_width, bg_height);
  var front = cropPreviewArea.front_buf;
  var back = cropPreviewArea.back_buf;

  
  if(front.src != src) {
    if(total_pixels < MAX_IMAGE_PIXELS) {
      cropPreviewLoading.style.display = "block"; // Let user know we're loading
      cropPreviewArea.isloading = true;
      back.onload = on_preview_bg_loaded;
      back.src = src;
    }
    
    front.style.width = bg_width + "px";
    front.style.width = bg_width + "px";
  }
  
  var x = -inner_x * scale_factor;
  var y = -inner_y * scale_factor;
  
  front.style.left = parseInt(x) + "px";
  front.style.top = parseInt(y) + "px";
}

/* The _back_ buffer finished loading. Swap the buffers */
function on_preview_bg_loaded() {
  var front = cropPreviewArea.front_buf;
  var back = cropPreviewArea.back_buf;

  back.onload = null;
  back.style.width = front.style.width;
  back.style.height = front.style.height;
  back.style.left = front.style.left;
  back.style.top = front.style.top;

  cropPreviewArea.isloading = false;
  cropPreviewLoading.style.display = "none";

  back.style.display = "block";
  front.style.display = "none";

  cropPreviewArea.front_buf = back;
  cropPreviewArea.back_buf = front;
}

function on_crop_preview_loaded() {
  cropPreviewImg.onload = null;

  /* Make the preview area the same size as the card translucent
     image we've been given, and incorporate the size of the
     title. */
  cropPreviewAreaInner.style.width = cropPreviewImg.width + "px";
  cropPreviewAreaInner.style.height = cropPreviewImg.height + "px";

  /* Fucking IE hack */
  if(browser.ie) {
    cropPreviewGlass.style.width = cropPreviewAreaInner.style.width;
    cropPreviewGlass.style.height = cropPreviewAreaInner.style.height;
  }

  cropPreviewArea.style.width =
    Math.max(
      cropPreviewTitle.offsetWidth,
      cropPreviewAreaInner.offsetWidth)
    + "px";
  
  cropPreviewArea.style.height =
    (cropPreviewTitle.offsetHeight + cropPreviewAreaInner.offsetHeight)
    + "px";

  /* Stupid CSS */
  cropInstructions.style.marginLeft = cropPreviewArea.style.width;

  aspect_ratio = phys_width / phys_height;

  msg("Aspect ratio: " + aspect_ratio);

  /* Silly IE */
  if(browser.ie) {
    cropPreviewImg.style.display = "none";
  }
	
  /* Fit the width of the preview to the width of the window and
     adjust the height accordingly. However, don't let the height of
     the image exceed the height of the browser window. */
  var cs = get_elem_cs(cropAreaInner);

  var bg_width = cs.width;
  var bg_aspect = (bg_width_px + 0.0) / bg_height_px;
  var bg_height = bg_width / bg_aspect;

  var maxheight = get_window_dimens().height * 0.45;

  if(bg_height > maxheight) {
    var ratio = bg_height / maxheight;
    bg_width  /= ratio;
    bg_height /= ratio;
  }

  bg_width = parseInt(bg_width);
  bg_height = parseInt(bg_height);

  /* Load the cropper background */
  cropBg.onload = on_crop_bg_loaded;
  cropBg.src = sprintf(cropimg_url, bg_width, bg_height);
}

function on_crop_bg_loaded() {
  msg("crop bg loaded");
  box_info.width = cropBg.width;
  box_info.height = box_info.width / aspect_ratio;

  cropBg.cs = get_elem_cs(cropBg);

  update_cropbox();
  var natural_dpi = calc_cropbox_dpi();
  minimum_dpi = Math.max(200, natural_dpi / 20);
  var artifical_size = false;
  
  if(natural_dpi < minimum_dpi) {
    natural_dpi = minimum_dpi;
    artifical_size = true;
  }

  set_cropbox_dpi(natural_dpi);

  if(artifical_size) {
    box_info.y = 0;
    update_cropbox();
  }

  update_preview();
	
  maximum_dpi = 1.5 * natural_dpi;
  set_slider_pos(dpi_to_slider_pos(natural_dpi));

  cropSliderHandle.onmousedown = on_crop_slider_start;

  box_info.aoff = comp_elem_abs_offset(cropRect);
  
  /* If we got explicit sizing information, use that instead */
  if(initial_crop_dpi > 0.1) {
    set_cropbox_dpi(initial_crop_dpi);
    set_slider_pos(dpi_to_slider_pos(initial_crop_dpi));
    box_info.x = initial_crop_x;
    box_info.y = initial_crop_y;
    update_cropbox();
    update_preview();
  }

  /* Display the finished product to the user */
  cropArea.style.height = "auto";
  cropSplash.style.display = "None";
}

/* -- Crop box -- */

function on_crop_drag_start(e) {
  if(!e) e = window.event;
  document.onmousemove = on_crop_drag;
  document.onmouseup = on_crop_drag_stop;
  box_info.deltax = e.clientX - box_info.x;
  box_info.deltay = e.clientY - box_info.y;
  cropDragHere.style.display = "none";
  return false;
}

function on_crop_drag_stop(e) {
  if(!e) e = window.event;
  document.onmousemove = null;
  document.onmouseup = null;
  update_preview();
  return false;
}

var LOSSAGE = 20;
var SNAPDIST = 5;

function on_crop_drag(e) {
  if(!e) e = window.event;

  box_info.x = e.clientX - box_info.deltax;
  box_info.y = e.clientY - box_info.deltay;

  /* Snap to image boundaries */
  if(Math.abs(box_info.x) < SNAPDIST)
    box_info.x = 0;

  if(Math.abs(box_info.y) < SNAPDIST)
    box_info.y = 0;

  if(Math.abs(box_info.x + box_info.width - cropBg.width) < SNAPDIST)
    box_info.x = cropBg.width - box_info.width;

  if(Math.abs(box_info.y + box_info.height - cropBg.height) < SNAPDIST)
    box_info.y = cropBg.height - box_info.height;

  /* Prevent Owen from losing the box */
  var realx = box_info.aoff.x + box_info.x;
  var realy = box_info.aoff.y + box_info.y;

  if(realx < LOSSAGE - box_info.width) {
    box_info.x += LOSSAGE - box_info.width - realx;
  }

  if(realy < LOSSAGE - box_info.height) {
    box_info.y += LOSSAGE - box_info.height - realy;
  }

  update_cropbox();
  update_preview();
  return false;
}

/* -- Slider -- */

function slider_min() {
  return 0;
}

function slider_max() {
  return cropSlider.offsetWidth - cropSliderHandle.offsetWidth;
}

function set_slider_pos(val) {
  if(val < 0) val = 0;
  if(val > 1) val = 1;
	
  cropSliderHandle.style.left =
    ((val * (slider_max() - slider_min())) + slider_min()) + "px";

  dump_box_info();
}

function get_slider_pos() {
  return (cropSliderHandle.offsetLeft + 0.0) / (slider_max() - slider_min());
}

function slider_pos_to_dpi(pos) {
  return pos * (maximum_dpi - minimum_dpi) + minimum_dpi;
}

function dpi_to_slider_pos(dpi) {
  return (dpi - minimum_dpi + 0.0) / (maximum_dpi - minimum_dpi);
}

function on_crop_slider_start(e) {
  if(!e) e = window.event;
  document.onmousemove = on_crop_slider_drag;
  document.onmouseup = on_crop_slider_stop;
  box_info.deltax = e.clientX - cropSliderHandle.offsetLeft;
  cropSliderHandle.style.border = "2px inset #aaaaaa";
  cropDragHere.style.display = "none";
  return false;
}

function on_crop_slider_stop(e) {
  if(!e) e = window.event;
  
  document.onmousemove = null;
  document.onmouseup = null;
  
  // XXX: Opera doesn't want to refresh the style until the window
  // loses focus. How can we force it?
  cropSliderHandle.style.border = "2px outset #aaaaaa";

  /* Override the isloading flag so we always get the last DPI the user
     selected, instead of the one that happened to get loaded before the last
     timeout */
  cropPreviewArea.isloading = false;
  set_cropbox_dpi(slider_pos_to_dpi(get_slider_pos()));
  return false;
}

function on_crop_slider_drag(e) {
  if(!e) e = window.event;
  
  var newx = (e.clientX - box_info.deltax);
  if(newx < slider_min())
    newx = slider_min();

  if(newx > slider_max())
    newx = slider_max();

  cropSliderHandle.style.left = newx + "px";
  set_cropbox_dpi(slider_pos_to_dpi(get_slider_pos()));
  return false;
}

