var ImageEditor = new Class(
{
   Implements: [Options],

   options:
   {
      editorID: 'editor',
      canvasWidth: 600,
      canvasHeight: 450,
      cropWidth: 100,
      cropHeight: 50,
      cropMinWidth: 10,
      cropMinHeight: 10,
      cropMaxWidth: 600,
      cropMaxHeight: 450, 
      cropResizable: false,
      scale: 1,
      angle: 0,
      opacity: 0.5
   },
   
   initialize: function(options)
   {
      this.setOptions(options);
      var fn = function(event)
      {         
         this.sx = event.client.x;
         this.sy = event.client.y;
         this.isMove = true;
         event.stop();    
      }.bind(this);
      var area = $(this.options.editorID).addClass('imgeditor_area').set('html', '').addEvent('selectstart', $lambda(false));      
      var shadow = new Element('div', {'id': this.options.editorID + '_shadow', 'class': 'imgeditor_shadow', 'styles': {'display': 'none', 'position': 'absolute'}});
      var crop = new Element('div', {'id': this.options.editorID + '_crop', 'class': 'imgeditor_crop', 'styles': {'position': 'absolute', 'width': this.options.cropWidth, 'height': this.options.cropHeight}}).inject(shadow);                   
      new Element('div', {'id': this.options.editorID + '_snap_left_top', 'class': 'imgeditor_crop_snap', 'styles': {'cursor': 'nw-resize', 'position': 'absolute', 'display': 'none'}}).inject(shadow);
      new Element('div', {'id': this.options.editorID + '_snap_top', 'class': 'imgeditor_crop_snap', 'styles': {'cursor': 's-resize', 'position': 'absolute', 'display': 'none'}}).inject(shadow);
      new Element('div', {'id': this.options.editorID + '_snap_right_top', 'class': 'imgeditor_crop_snap', 'styles': {'cursor': 'ne-resize', 'position': 'absolute', 'display': 'none'}}).inject(shadow);
      new Element('div', {'id': this.options.editorID + '_snap_left', 'class': 'imgeditor_crop_snap', 'styles': {'cursor': 'e-resize', 'position': 'absolute', 'display': 'none'}}).inject(shadow);
      new Element('div', {'id': this.options.editorID + '_snap_right', 'class': 'imgeditor_crop_snap', 'styles': {'cursor': 'e-resize', 'position': 'absolute', 'display': 'none'}}).inject(shadow);
      new Element('div', {'id': this.options.editorID + '_snap_left_bottom', 'class': 'imgeditor_crop_snap', 'styles': {'cursor': 'ne-resize', 'position': 'absolute', 'display': 'none'}}).inject(shadow);
      new Element('div', {'id': this.options.editorID + '_snap_bottom', 'class': 'imgeditor_crop_snap', 'styles': {'cursor': 's-resize', 'position': 'absolute', 'display': 'none'}}).inject(shadow);
      new Element('div', {'id': this.options.editorID + '_snap_right_bottom', 'class': 'imgeditor_crop_snap', 'styles': {'cursor': 'nw-resize', 'position': 'absolute', 'display': 'none'}}).inject(shadow);                               
      new Element('div', {'id': this.options.editorID + '_shadow_top', 'class': 'imgeditor_shadow_top', 'styles': {'position': 'absolute'}, 'events': {'mousedown': fn}}).inject(shadow);
      new Element('div', {'id': this.options.editorID + '_shadow_left', 'class': 'imgeditor_shadow_left', 'styles': {'position': 'absolute'}, 'events': {'mousedown': fn}}).inject(shadow);
      new Element('div', {'id': this.options.editorID + '_shadow_right', 'class': 'imgeditor_shadow_right', 'styles': {'position': 'absolute'}, 'events': {'mousedown': fn}}).inject(shadow);
      new Element('div', {'id': this.options.editorID + '_shadow_bottom', 'class': 'imgeditor_shadow_bottom', 'styles': {'position': 'absolute'}, 'events': {'mousedown': fn}}).inject(shadow);      
      shadow.inject(area);    
      new Element('div', {'id': this.options.editorID + '_container', 'class': 'imgeditor_container'}).inject(area);                  
      this.paper = Raphael(this.options.editorID + '_container', this.options.canvasWidth, this.options.canvasHeight);
      crop.makeDraggable({'container': this.options.editorID, 'onDrag': function(){this.redrawShadow();}.bind(this)});
      $(this.options.editorID + '_container').addEvent('mousedown', fn);    
      $(document.body).addEvent('mousemove', function(event)
      {             
         if (this.isMove)
         { 
            this.transform(this.options.angle, this.options.scale, event.client.x - this.sx, event.client.y - this.sy);
            this.sx = event.client.x;
            this.sy = event.client.y;
            return;
         }
      }.bind(this)).addEvent('mouseup', function(event){this.isMove = false;}.bind(this));
      var resize = function(event)
      {
         this.cropTop = crop.getStyle('top').toInt();
         this.cropLeft = crop.getStyle('left').toInt();
         this.cropBottom = this.cropTop + crop.getSize().y;   
         this.cropRight = this.cropLeft + crop.getSize().x;     
      }.bind(this);
      var drag = function(snap)
      {
         var y = snap.getStyle('top').toInt(), x = snap.getStyle('left').toInt();
         switch (snap.id.substr(this.options.editorID.length + 6))
         {
            case 'top':    
              if (this.cropBottom - y > this.options.cropMaxHeight) y = this.cropBottom - this.options.cropMaxHeight; 
              if (this.cropBottom - y < this.options.cropMinHeight) y = this.cropBottom - this.options.cropMinHeight;                   
              crop.setStyles({'top': y - 2, 'height': this.cropBottom - y});
              break;
            case 'bottom':
              if (y - this.cropTop > this.options.cropMaxHeight) y = this.cropTop + this.options.cropMaxHeight; 
              if (y - this.cropTop < this.options.cropMinHeight) y = this.cropTop + this.options.cropMinHeight;
              crop.setStyles({'height': y - this.cropTop});
              break;
            case 'left':
              if (this.cropRight - x > this.options.cropMaxWidth) x = this.cropRight - this.options.cropMaxWidth; 
              if (this.cropRight - x < this.options.cropMinWidth) x = this.cropRight - this.options.cropMinWidth;                   
              crop.setStyles({'left': x - 2, 'width': this.cropRight - x});
              break;
            case 'right':
              if (x - this.cropLeft > this.options.cropMaxWidth) x = this.cropLeft + this.options.cropMaxWidth; 
              if (x - this.cropLeft < this.options.cropMinWidth) x = this.cropLeft + this.options.cropMinWidth;
              crop.setStyles({'width': x - this.cropLeft});
              break;
            case 'left_top':
              if (this.cropBottom - y > this.options.cropMaxHeight) y = this.cropBottom - this.options.cropMaxHeight; 
              if (this.cropBottom - y < this.options.cropMinHeight) y = this.cropBottom - this.options.cropMinHeight;
              if (this.cropRight - x > this.options.cropMaxWidth) x = this.cropRight - this.options.cropMaxWidth; 
              if (this.cropRight - x < this.options.cropMinWidth) x = this.cropRight - this.options.cropMinWidth;
              crop.setStyles({'top': y - 2, 'height': this.cropBottom - y, 'left': x - 2, 'width': this.cropRight - x});
              break;
            case 'right_top':
              if (this.cropBottom - y > this.options.cropMaxHeight) y = this.cropBottom - this.options.cropMaxHeight; 
              if (this.cropBottom - y < this.options.cropMinHeight) y = this.cropBottom - this.options.cropMinHeight;
              if (x - this.cropLeft > this.options.cropMaxWidth) x = this.cropLeft + this.options.cropMaxWidth; 
              if (x - this.cropLeft < this.options.cropMinWidth) x = this.cropLeft + this.options.cropMinWidth;
              crop.setStyles({'top': y - 2, 'height': this.cropBottom - y, 'width': x - this.cropLeft});
              break;
            case 'left_bottom':
              if (y - this.cropTop > this.options.cropMaxHeight) y = this.cropTop + this.options.cropMaxHeight; 
              if (y - this.cropTop < this.options.cropMinHeight) y = this.cropTop + this.options.cropMinHeight;
              if (this.cropRight - x > this.options.cropMaxWidth) x = this.cropRight - this.options.cropMaxWidth; 
              if (this.cropRight - x < this.options.cropMinWidth) x = this.cropRight - this.options.cropMinWidth;
              crop.setStyles({'height': y - this.cropTop, 'left': x - 2, 'width': this.cropRight - x});
              break;
            case 'right_bottom':
              if (y - this.cropTop > this.options.cropMaxHeight) y = this.cropTop + this.options.cropMaxHeight; 
              if (y - this.cropTop < this.options.cropMinHeight) y = this.cropTop + this.options.cropMinHeight;
              if (x - this.cropLeft > this.options.cropMaxWidth) x = this.cropLeft + this.options.cropMaxWidth; 
              if (x - this.cropLeft < this.options.cropMinWidth) x = this.cropLeft + this.options.cropMinWidth;
              crop.setStyles({'height': y - this.cropTop, 'width': x - this.cropLeft});
              break;
         }
         this.redrawShadow();
      }.bind(this);
      var limit = {'x': [1, area.getSize().x - 3], 'y': [1, area.getSize().y - 3]};
      $(this.options.editorID + '_snap_top').makeDraggable({'modifiers': {'y': 'top', 'x': false}, 'limit': limit, 'onStart': resize, 'onDrag': drag});
      $(this.options.editorID + '_snap_bottom').makeDraggable({'modifiers': {'y': 'top', 'x': false}, 'limit': limit, 'onStart': resize, 'onDrag': drag});
      $(this.options.editorID + '_snap_left').makeDraggable({'modifiers': {'y': false, 'x': 'left'}, 'limit': limit, 'onStart': resize, 'onDrag': drag});
      $(this.options.editorID + '_snap_right').makeDraggable({'modifiers': {'y': false, 'x': 'left'}, 'limit': limit, 'onStart': resize, 'onDrag': drag});
      $(this.options.editorID + '_snap_left_top').makeDraggable({'modifiers': {'y': 'top', 'x': 'left'}, 'limit': limit, 'onStart': resize, 'onDrag': drag});
      $(this.options.editorID + '_snap_right_top').makeDraggable({'modifiers': {'y': 'top', 'x': 'left'}, 'limit': limit, 'onStart': resize, 'onDrag': drag});
      $(this.options.editorID + '_snap_left_bottom').makeDraggable({'modifiers': {'y': 'top', 'x': 'left'}, 'limit': limit, 'onStart': resize, 'onDrag': drag});
      $(this.options.editorID + '_snap_right_bottom').makeDraggable({'modifiers': {'y': 'top', 'x': 'left'}, 'limit': limit, 'onStart': resize, 'onDrag': drag});  
   },   
   
   read: function(src, width, height)
   {
      if (this.image) this.image.remove();
      var dx = dy = Math.sqrt(width * width + height * height) * this.options.scale;
      var size = $(this.options.editorID).getSize();
      if (dx < size.x) dx = size.x;
      if (dy < size.y) dy = size.y;
      this.setCanvasSize(dx, dy);
      this.image = this.paper.image(src, 0, 0, width, height);        
      this.centre();
   },      
   
   redrawShadow: function()
   {
      var area = $(this.options.editorID);
      var size = area.getSize();
      var crop = $(this.options.editorID + '_crop');      
      var cropSize = crop.getSize();
      var top = $(this.options.editorID + '_shadow_top');
      var left = $(this.options.editorID + '_shadow_left');
      var right = $(this.options.editorID + '_shadow_right');
      var bottom = $(this.options.editorID + '_shadow_bottom');
      var bx = area.getStyle('border-left-width').toInt() + area.getStyle('border-right-width').toInt();
      var by = area.getStyle('border-top-width').toInt() + area.getStyle('border-bottom-width').toInt();    
      var cx = crop.getStyle('left').toInt(), cy = crop.getStyle('top').toInt();      
      top.setStyles({'top': 0, 'left': 0, 'width': size.x - bx, 'height': ((cy < 0) ? 0 : cy)});            
      top.setOpacity(this.options.opacity);      
      left.setStyles({'top': cy, 'left': 0, 'width': ((cx < 0) ? 0 : cx), 'height': cropSize.y});
      left.setOpacity(this.options.opacity);      
      var w = size.x - cx - cropSize.x - bx;
      right.setStyles({'top': cy, 'left': cx + cropSize.x, 'width': ((w < 0) ? 0 : w), 'height': cropSize.y});
      right.setOpacity(this.options.opacity);      
      var h = size.y - cy - cropSize.y - by; 
      bottom.setStyles({'top': cy + cropSize.y, 'left': 0, 'width': size.x - bx, 'height': ((h < 0) ? 0 : h)});
      bottom.setOpacity(this.options.opacity);      
      if (this.options.cropResizable)
      {
         $(this.options.editorID + '_snap_left_top').setStyles({'top': (cy - 2), 'left': (cx - 2), 'display': ''});
         $(this.options.editorID + '_snap_top').setStyles({'top': (cy - 2), 'left': (cx + cropSize.x / 2 - 3), 'display': ''});
         $(this.options.editorID + '_snap_right_top').setStyles({'top': (cy - 2), 'left': (cx + cropSize.x - 4), 'display': ''});
         $(this.options.editorID + '_snap_left').setStyles({'top': (cy + cropSize.y / 2 - 3), 'left': (cx - 2), 'display': ''});
         $(this.options.editorID + '_snap_right').setStyles({'top': (cy + cropSize.y / 2 - 3), 'left': (cx + cropSize.x - 4), 'display': ''});
         $(this.options.editorID + '_snap_left_bottom').setStyles({'top': (cy + cropSize.y - 4), 'left': (cx - 2), 'display': ''});
         $(this.options.editorID + '_snap_bottom').setStyles({'top': (cy + cropSize.y - 4), 'left': (cx + cropSize.x / 2 - 3), 'display': ''});
         $(this.options.editorID + '_snap_right_bottom').setStyles({'top': (cy + cropSize.y - 4), 'left': (cx + cropSize.x - 4), 'display': ''});
      }  
   },
   
   transform: function(angle, scale, dx, dy)
   {     
      if (!this.image) return; 
      var cx = this.image.attrs.x + this.image.attrs.width / 2, cy = this.image.attrs.y + this.image.attrs.height / 2; 
      this.image = this.image.rotate(angle, false).scale(scale, scale);
      this.image.translate(dx + cx - this.image.attrs.x - this.image.attrs.width / 2, dy + cy - this.image.attrs.y - this.image.attrs.height / 2);
      this.options.angle = angle;
      this.options.scale = scale;                          
   },
   
   centre: function()
   {
      var size = $(this.options.editorID).getSize();      
      this.setCoordinates(0, 0);
      this.transform(this.options.angle, this.options.scale, (size.x - this.image.attrs.width) / 2, (size.y - this.image.attrs.height) / 2);          
   },
   
   cropShow: function(isCentre)
   {      
      $(this.options.editorID + '_shadow').setStyle('display', '');
      if (isCentre) this.cropCentre();
      else this.cropMove(0, 0);            
   },
   
   cropHide: function()
   {
      $(this.options.editorID + '_shadow').setStyle('display', 'none');
   },
   
   cropMoveTo: function(x, y)
   {
      $(this.options.editorID + '_crop').setStyles({'left': x, 'top': y});
      this.redrawShadow();
   },
   
   cropResize: function(width, height)
   {
      $(this.options.editorID + '_crop').setStyles({'width': width, 'height': height});
      this.redrawShadow();
   },
   
   cropCentre: function()
   {
      var size = $(this.options.editorID).getSize();
      var cropsize = $(this.options.editorID + '_crop').getSize();
      this.cropMoveTo((size.x - cropsize.x) / 2, (size.y - cropsize.y) / 2);
   },
   
   getCropParameters: function()
   {
      var params = new Array();
      var crop = $(this.options.editorID + '_crop');
      params['width'] = crop.getSize().x;
      params['height'] = crop.getSize().y;
      params['x'] = crop.getStyle('left').toInt() - this.getBox().x;
      params['y'] = crop.getStyle('top').toInt() - this.getBox().y;
      return params;
   },        
   
   getBox: function()
   {      
      var cx = this.image.attrs.width / 2, cy = this.image.attrs.height / 2;
      var box = {}, x1, x2, y1, y2, angle = this.options.angle;
      if (angle == 180 || angle == 360) angle = 0;
      if (angle <= 90)
      {
         x1 = -cx, y1 = cy;
         x2 = -cx, y2 = -cy;
      }
      else if (angle < 180)
      {
         x1 = cx, y1 = cy;
         x2 = -cx, y2 = cy;
      }
      else if (angle <= 270)
      {
         x1 = cx, y1 = -cy;
         x2 = cx, y2 = cy;
      }
      else if (angle < 360)
      {
         x1 = -cx, y1 = -cy;
         x2 = cx, y2 = -cy;
      }
      var rad = angle / 180 * Math.PI;
      x1 = x1 * Math.cos(rad) - y1 * Math.sin(rad);
      y1 = x2 * Math.sin(rad) + y2 * Math.cos(rad);            
      box.x = x1 + cx + this.image.attrs.x;
      box.y = y1 + cy + this.image.attrs.y;
      box.width = 2 * (cx + this.image.attrs.x) - x1;
      box.height = 2 * (cy + this.image.attrs.y) - y1;
      return box;
   },
   
   setCoordinates: function(x, y)
   {
      this.image.attr('x', x);
      this.image.attr('y', y);
   },
   
   getCoordinates: function()
   {
      return {'x': this.image.attrs.x, 'y': this.image.attrs.y};
   },
   
   setCanvasSize: function(width, height)
   {
      this.paper.setSize(width, height);
   }, 
   
   getCanvasSize: function()
   {
      return {'width': this.paper.canvas.width.baseVal.value, 'height': this.paper.canvas.height.baseVal.value};
   }
});

var ed, zoomSlider, rotateSlider; 
window.addEvent('domready', function()
{
   ed = new ImageEditor({'editorID': 'div_img_zone', 'canvasWidth': 504, 'canvasHeight': 276});
   zoomSlider = new Slider('slider_zoom', $('slider_zoom').getElement('.knob'), {'steps': 199,	'range': [1, 200], 'wheel': true, 'onChange': function(value)
   {
      ed.transform(ed.options.angle, value / 100, 0, 0);
      $('imgedt_zoom').value = value;
   }}).set(100);
   $('imgedt_zoom').addEvent('keydown', function(event)
   {
      if (event.key == 'enter') zoomSlider.set(this.value);	
   });
   rotateSlider = new Slider('slider_rotate', $('slider_rotate').getElement('.knob'), {'steps': 360, 'range': [0, 360], 'wheel': true, 'onChange': function(value)
   {		
      ed.transform(value, ed.options.scale, 0, 0);
      $('imgedt_angle').value = value;
   }}).set(0);	
   $('imgedt_angle').addEvent('keydown', function(event)
   {
      if (event.key == 'enter') rotateSlider.set(this.value);		
   });     
});

function imgedtInit(src, imgWidth, imgHeight, cropWidth, cropHeight, cropResizable)
{
   zoomSlider.set(100);
   rotateSlider.set(0);   
   $('imgedt_zoom').value = 100;
   $('imgedt_angle').value = 0;
   ed.options.cropResizable = cropResizable;
   ed.options.angle = 0;
   ed.options.scale = 1;   
   ed.cropResize(cropWidth, cropHeight);
   ed.cropShow(true);      			
   ed.read(src, imgWidth, imgHeight);      
}