/**
 * Copyright: (c) 2006-2009 The LogiLogi Foundation <foundation@logilogi.org>
 *
 * License:
 *   This file is part of the Magick Corners Rails Plugin. Magick
 *   Corners is Free Software. You can run/distribute/modify Magick
 *   Corners under the terms of the GNU Affero General Public License
 *   version 3. The Affero GPL states that running a modified version
 *   or a derivative work also requires you to make the source code of
 *   that work available to everyone that can interact with it. We
 *   chose the Affero GPL to ensure that Magick Corners remains open
 *   and libre (doc/LICENSE contains the full text of the legally
 *   binding license).
 **
 * cbb function by Roger Johansson, http://www.456bereastreet.com/
 */

Prototype.Browser.IE6 = Prototype.Browser.IE && 
    parseInt(navigator.userAgent.substring(navigator.userAgent.indexOf("MSIE") + 5)) == 6;

var MagickCorners = Class.create({
  // // Constants
  Emsize: 12,
  DefaultCornerSize: 20,
  PaddingSides: ['paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft'],
  Defined: false,
  FirstRun: true,
  
  // // Attributes
  runs: {},
  defers: [],

  // // Constructors

  initialize: function(args) {
    options = new Hash(args);
    if (options != null) {
      this.path_prefix = options.unset('path_prefix');
    }
    if (options.keys.size > 0) {
      alert('Misspelled option given');
    }
    this.path_prefix = this.path_prefix != null ? this.path_prefix + '/' : ''
  },

  // // Public Methods

  add: function(selector, args) {
    // not (yet) working in Opera and Safari
    if (Prototype.Browser.Opera || Prototype.Browser.MobileSafari) {
      return;
    }
  
    // // Harvest config variables
    var options = this.harvest_args(args);
  
    this.add_run('mcdone', [selector, options]);
  
    // No running before definition
    this.Defined = true;
  },
  
  defer: function(done_tag_post, pre_selector, selector) {
    this.defers.push([done_tag_post, pre_selector, selector]);
  },
  
  prepare_defered: function() {
    this.defers.each(function(done_tag_post_pre_selector_selector) {
      var done_tag = this.get_done_tag(done_tag_post_pre_selector_selector[0]);
      var std_done_tag = this.get_done_tag();
      var selector = done_tag_post_pre_selector_selector[2];
      var deferred_selector = done_tag_post_pre_selector_selector[1] + ' ' + selector;
      // // Iterate over all elements with the class name
      $$(deferred_selector).each(function(element) {
        if (this.not_done(element, std_done_tag)) {
          element.className = element.className + ' ' + std_done_tag;
        }
        this.runs[std_done_tag].each(function(selector_and_options) {
          if (selector_and_options[0] == selector) {
            this.add_run(done_tag, [deferred_selector, selector_and_options[1]]);
          }
        }.bind(this));
      }.bind(this));
    }.bind(this));
  },
  
  run: function(done_tag_post) {
    var done_tag = this.get_done_tag(done_tag_post);
    if (this.Defined) {
      if (this.FirstRun) {
        Event.observe(window, 'load', this.run_runners.bind(this,done_tag));
        this.FirstRun = false;
      } else {
        this.run_runners(done_tag);
      }
    }
  },

  // // Protected Methods
  
  initialize_runs: function(done_tag) {
    if (Object.isUndefined(this.runs[done_tag])) {
      this.runs[done_tag] = [];
    }
  },

  add_run: function(done_tag, selector_options) {
    this.initialize_runs(done_tag);
    this.runs[done_tag].push(selector_options);
  },
  
  run_runners: function(done_tag) {
    this.initialize_runs(done_tag);
    this.runs[done_tag].each(function(selector_and_options) {
      this.run_runner(selector_and_options[0], selector_and_options[1], done_tag);
    }.bind(this));
  },
  
  run_runner: function(selector,options,done_tag) {
    // // Iterate over all elements with the class name
    $$(selector).each(function(element) {
      this.now_to_element(element,options,done_tag);
    }.bind(this));
  },
  
  now_to_element: function(element, options, done_tag) {
    if (this.not_done(element, done_tag)) {
      var element_vars = this.get_element_vars(element, options);
  
      // // Prepare the options
      var css_options = new Hash();
      //css_options.set('overflow', 'auto');
      css_options.set('backgroundRepeat', 'no-repeat');
  
      var image_size = this.get_image_size(options, element_vars);
  
      if (options.stretch_side != 'none') {
        var outer_css_options = new Hash();
        var stretch_css_options = new Hash();
        this.prepare_options_for_stretch(css_options, outer_css_options, stretch_css_options, 
            options, element_vars, image_size);
      }
  
      // // Set the image url
      var format = 'png';
      var image_url;
      if (options.scaled_image != null ) {
        image_url = 'url(/' + this.path_prefix + 'magick_corners/scaled_image.' + 
            image_size.limit + '.' + options.scaled_dimension + 
            '.' + options.scaled_image + '.' + format + ')';
      } else {
        if (options.image != null) {
          image_url = 'url(/' + this.path_prefix + 'magick_corners/image.' + image_size.width + '.' + image_size.height + 
              '.' + options.image + '.' + options.corner_size + '.' + 
              options.image_start_corner + '.' + format + ')';
        } else {
          image_url = 'url(/' + this.path_prefix + 'magick_corners/box.' + image_size.width + '.' + image_size.height + 
              '.' + options.corner_size + '.' + element_vars.border_width + '.' + 
              element_vars.background_color + '.' + element_vars.border_color + '.' + format + ')';
        }
      }
  
      css_options.set('backgroundImage', image_url);
      if (options.stretch_side != 'none') {
        stretch_css_options.set('backgroundImage', image_url);
      }
  
      if (options.stretch_side != 'none') {
        // // Create new elements
        var outer_element, stretch_element;
  
        // Create the new outer-element
        outer_element = new Element('div');
        if (element.getAttribute('id')) {
          tempId = element.id;
          element.removeAttribute('id');
          outer_element.setAttribute('id','');
          outer_element.id = tempId;
        }
        ['onclick','onkeydown','tabindex'].each(function(transfer) {
          if (element.getAttribute(transfer)) {
            tempTr = element.getAttribute(transfer);
            element.removeAttribute(transfer);
            outer_element.setAttribute(transfer,tempTr);
            // needed for IE
            if (transfer == 'onclick') { element.onclick = null; }
            if (transfer == 'onkeydown') { element.onkeydown = null; }
          }
        });

        var tempClassName = element.className;
        element.setAttribute('class','');
        element.className = '';
        // The mcdone is needed to prevent double borders
        outer_element.className = tempClassName + ' ' + done_tag;
        element.parentNode.replaceChild(outer_element, element);
        outer_element.appendChild(element);
  
        // Create the stretch-div
        stretch_element = new Element('div');
        if (options.stretch_side == 'top' || options.stretch_side == 'left') {
          outer_element.insert({top: stretch_element});
        } else {
          outer_element.insert(stretch_element);
        }
      }
  
  //  alert(outer_css_options.inspect());
  //  alert(css_options.inspect());
  //  alert(stretch_css_options.inspect());
  
      // // Set the css options
      if (options.stretch_side != 'none') {
        outer_element.setStyle2(outer_css_options);
      }
      element.setStyle2(css_options);
      if (options.stretch_side != 'none') {
        stretch_element.setStyle2(stretch_css_options)
      }
    }
  },
  
  prepare_options_for_stretch: function(css_options, outer_css_options, stretch_css_options,
        options, element_vars, image_size) {
    // block and overflow
    outer_css_options.set('display', 'block');
    //outer_css_options.set('overflow', 'auto');
    //stretch_css_options.set('overflow', 'auto');
    outer_css_options.set('backgroundRepeat', 'no-repeat');
    stretch_css_options.set('backgroundRepeat', 'no-repeat');
  
    // no background and border
    outer_css_options.set('background', 'none');
    outer_css_options.set('border', 'none');
    this.PaddingSides.each(function(setting) {
      outer_css_options.set(setting, '0px');
      stretch_css_options.set(setting, '0px');
    });
  
    // now update the size of the outer_element
    outer_css_options.set('width', element_vars.full_width + 'px');
    outer_css_options.set('height', element_vars.full_height + 'px');
  
    // // Settings dependent on side
    
    // padding adjustment
    var remaining_adjustment = 0;
    var stretch_sides = ['top', 'right', 'bottom', 'left']; // same order as padding sides
    var stretch_side_index = stretch_sides.indexOf(options.stretch_side);
  
    var new_stretch_padding = element_vars.full_padding_values[stretch_side_index] - image_size.corner_size;
    if (new_stretch_padding < 0) {
      remaining_adjustment = new_stretch_padding * -1;
      new_stretch_padding = 0;
    }
    css_options.set(this.PaddingSides[stretch_side_index], new_stretch_padding + 'px');
  
    [0, 1, 2, 3].each(function(index) { // now the other paddings
      if (index != stretch_side_index) {
        css_options.set(this.PaddingSides[index], element_vars.full_padding_values[index] + 'px');
      }
    }.bind(this));
  
    // the height / width of the element and stretch_element
    if (options.stretch_side == 'top' || options.stretch_side == 'bottom') {
      css_options.set('width', element_vars.width + 'px');
      css_options.set('height', (element_vars.height - remaining_adjustment) + 'px');
      stretch_css_options.set('width', element_vars.full_width + 'px');
      stretch_css_options.set('height', image_size.corner_size + 'px');
    } else { // right or left
      css_options.set('width', (element_vars.width - remaining_adjustment) + 'px');
      css_options.set('height', element_vars.height + 'px');
      stretch_css_options.set('width', image_size.corner_size + 'px');
      stretch_css_options.set('height', element_vars.full_height + 'px');
      // Floats are needed for left & right
      css_options.set('cssFloat', 'left');
      stretch_css_options.set('cssFloat', 'left');
    }
    if (options.stretch_side == 'bottom' || options.stretch_side == 'right') {
      css_options.set('backgroundPosition', 'left top');
      stretch_css_options.set('backgroundPosition', 'bottom right');
    } else {
      css_options.set('backgroundPosition', 'bottom right');
      stretch_css_options.set('backgroundPosition', 'left top');
    }
  },
  
  get_image_size: function(options, element_vars) {
    var width, height, limit, corner_size;
    if (options.stretch_side != 'none') {
      if (options.stretch_side == 'top' || options.stretch_side == 'bottom') {
        width = element_vars.full_width;
        height = (Math.ceil(element_vars.full_height / 400) * 400);
      } else { // right or left
        width = (Math.ceil(element_vars.full_width / 400) * 400);
        height = element_vars.full_height;
      }
    } else { // no stretch
      width = element_vars.full_width;
      height = element_vars.full_height;
    }
    if (options.scaled_image != null) {
      if (options.scaled_dimension == 'height') { 
        limit = height
      } else {
        limit = width
      }
      corner_size = Math.floor((limit / options.reference_size) * options.corner_size);
    } else {
      corner_size = options.corner_size;
    }
    return { width : width, 
             height : height,
             limit : limit,
             corner_size : corner_size};
  },
  
  harvest_args: function(args) {
    var corner_size, stretch_side, image, image_start_corner;
    options = new Hash(args);
    if (options != null) {
      // image and box settings
      corner_size = options.unset('corner_size');
      stretch_side = options.unset('stretch_side');
  
      // image settings
      image = options.unset('image');
      image_start_corner = options.unset('image_start_corner');
  
      // scaled image settings
      reference_size = options.unset('reference_size')
      scaled_image = options.unset('scaled_image');
      scaled_dimension = options.unset('scaled_dimension')
    }
    if (options.keys.size > 0) {
      alert('Misspelled option given');
    }
    corner_size = corner_size != null ? corner_size : this.DefaultCornerSize;
    stretch_side = stretch_side != null ? stretch_side : 'bottom';
    image_start_corner = image_start_corner != null ? image_start_corner : 'north_west';
    reference_size = reference_size != null ? reference_size : 40;
    scaled_dimension = scaled_dimension != null ? scaled_dimension : 'height';
  
    return {corner_size : corner_size, 
        stretch_side : stretch_side, 
        image : image, 
        image_start_corner : image_start_corner,
        scaled_image : scaled_image,
        reference_size : reference_size,
        scaled_dimension : scaled_dimension};
  },
  
  get_element_vars: function(element, options) {
    var full_width, full_height, width, height, 
        background_color, border_width, border_color, full_padding_values
    // // Get the current sizes and colors
    full_width = element.getWidth(); 
    full_height = element.getHeight();
  
    full_width = this.make_even(full_width);
    full_height = this.make_even(full_height);
  
    background_color = this.get_color_style(element, 'backgroundColor');
    border_width = this.get_int_style(element, 'borderTopWidth');
  
    border_color;
    if (!isNaN(border_width) && (border_width != 0)) {
      border_color = this.get_color_style(element, 'borderTopColor');
    } else {
      border_width = 0;
      border_color = 0;
    }
  
    // full_padding is padding plus border-size (on FF)
    full_padding_values = [];
    this.PaddingSides.each(function(setting) {
      full_padding_values.push(this.get_int_style(element, setting) + border_width);
    }.bind(this));
  
    width = full_width - full_padding_values[1] - full_padding_values[3];
    height = full_height - full_padding_values[0] - full_padding_values[2];
  
    width = this.make_even(width);
    height = this.make_even(height);
  
    return {full_width : full_width, full_height : full_height,
        width : width, height : height,
        background_color : background_color,
        border_width : border_width,
        border_color : border_color,
        full_padding_values : full_padding_values};
  },
  
  not_done: function(element, done_tag) {
    if (element.className.match(done_tag)) {
      return false;
    } else {
      return true;
    }
  },
  
  get_done_tag: function(done_tag_post) {
    if (Object.isUndefined(done_tag_post)) {
      return 'mcdone';
    } else {
      return 'mcdone' + done_tag_post;
    }
  },
  
  get_int_style: function(outer_element, style) {
    var str = outer_element.getStyle(style)
    if (str.match(/em/)) {
      return parseInt(parseFloat(str.gsub('em','')) * this.Emsize);
    } else {
      return parseInt(parseFloat(str.gsub('px','')));
    }
  },
  
  get_color_style: function(outer_element, style) {
    var str = outer_element.getStyle(style).gsub('#','');
    if (str.match(/rgb/i)) {
      str = str.gsub('rgb\\(','').gsub(/\)/,'');
      var arr = str.split(', ');
      return this.to_hex(arr[0]) + this.to_hex(arr[1]) + this.to_hex(arr[2]);
    } else {
      return str
    }
  },
  
  to_hex: function(N) {
    if (N == null) {
      return "00";
    }
    N = parseInt(N);
    if (N == 0 || isNaN(N)) {
      return "00";
    }
    N = Math.max(0,N);
    N = Math.min(N,255);
    N = Math.round(N);
    return "0123456789abcdef".charAt((N-N%16)/16) + "0123456789abcdef".charAt(N%16);
  },
  
  make_even: function(nr) {
  //  return Math.floor(nr / 2) * 2;
    return nr;
  }
});
