//---------------------------------------------------------------------
//
//   Resopal: Mein Resopal
//
//   @file js/mein_resopal.js
//   @author Dirk Eschler <eschler@zeitform.de>
//   @date 2006-12-13
//   @version 1.36
//
//   production: zeitform Internet Dienste OHG
//               Fraunhoferstr. 5
//               D-64283 Darmstadt
//   tel.      : +49-6151/155-637
//   fax       : +49-6151/155-634
//   e-mail    : zeitform@zeitform.de
//   web       : http://www.zeitform.de
//
//---------------------------------------------------------------------

// Preview images are scaled to this size (1px images)
var IMG_PRE_WIDTH  = 100;
var IMG_PRE_HEIGHT = 53;

// Path to medium images (relative to doc root)
var IMG_MED_PATH = '/images/products/dekore/'; // trailing slash!

// Colorpicker search url (rgb value will be appended)
var CP_SEARCH_URL = '/kollektionen/suche?rgb=';

// --------------------------------------------------------------------
// --------------------------------------------------------------------

Event.observe(window, 'load', function() {
  app = new Application('indicatorArea', IMG_MED_PATH, CP_SEARCH_URL);
  if(app.init()) {    
    app.dragbox = new DragBox('dragArea', IMG_PRE_WIDTH, IMG_PRE_HEIGHT);
    app.dragbox.registerDraggables();
    app.dropbox = new DropBox('dropArea');
    app.dropbox.registerDroppables();
    app.ctrl = new Controller();
    app.regEventHandler();
    app.dropbox.init();
  }
});

// --------------------------------------------------------------------
//  Application
// --------------------------------------------------------------------

/**
 * Mein Resopal Application
 * @param responder   string  Container element that acts as a Ajax responder
 * @param medPath     string  Path to medium images (relative to site,
 *                            leading and trailing slashes)
 * @param cpSearchUrl string  Colorpicker search URL (can be absolute or relative)
 * TODO use object literal
 */
function Application(responder, medPath, cpSearchUrl) {
  this.resetShown = false;
  this.touched = false;
  this.colorscheme = 'standard'; // current color scheme
  this.medPath = medPath || "";
  this.cpSearchUrl = cpSearchUrl || "";
  
  /**
   * List of color schemes
   */
  this.Colorschemes = {
    'standard' : {
      playgroundBackground    : '', // none
      playgroundBorder        : '1px solid white',
      panelForeground         : '#313031',
      panelDisabledForeground : '#777',
      previewBorder           : 'none',
      dropBorder              : 'none',
      dropBorderStartDrag     : '1px solid red',
      dropBackgroundImg       : '/images/products/dropzone.gif',
      dropBackground          : 'url(/images/products/dropzone.gif) no-repeat'
    },
    'white' : {
      playgroundBackground    : 'white',
      playgroundBorder        : '1px solid darkgray',
      panelForeground         : '#313031',
      panelDisabledForeground : '#777',
      previewBorder           : 'none',
      dropBorder              : 'none',
      dropBorderStartDrag     : '1px solid red',
      dropBackgroundImg       : '/images/products/dropzone.gif',
      dropBackground          : 'url(/images/products/dropzone.gif) no-repeat'
    },
    'black' : {
      playgroundBackground    : 'black',
      playgroundBorder        : '1px solid black',
      panelForeground         : 'white',
      panelDisabledForeground : '#aaa',
      previewBorder           : 'none',
      dropBorder              : 'none',
      dropBorderStartDrag     : '1px solid red',
      dropBackgroundImg       : '/images/products/dropzone.gif',
      dropBackground          : 'url(/images/products/dropzone.gif) no-repeat'
    }
  }
  
  /**
   * Returns current color
   * @param style string  The style (ex. playgroundBackground)
   * @return string       The current CSS setting by style
   */
  this.currentColor = function(style) {
    return this.Colorschemes[this.colorscheme][style];
  }
  
  // register ajax responder
  this.registerResponder(responder);
}  


/**
 * Creates a Ajax Responder that gives visual feedback about any Ajax activity
 * @param id string  Indicator element, that is shown on Ajax activity and
 *                   hidden when the Ajax request is complete. The element
 *                   usually contains a animated gif or makes use of effects.
 */
Application.prototype.registerResponder = function(id) {
  Ajax.Responders.register({
    onCreate: function() {
      if(Ajax.activeRequestCount > 0)
        Element.show(id);
    },
    onComplete: function() {
      if(Ajax.activeRequestCount == 0)
        Element.hide(id);
    }
  });
}


/**
 * @return int Number of remembered decors (image tags inside drag area)
 */
Application.prototype.getNumPreviews = function() {
  var numPreviews = 0;
  var imgs = $('dragArea').getElementsByTagName("img");
  for(var i=0; i<imgs.length; i++) {
    numPreviews++;
  }
  return numPreviews;
}


/**
 * Initializes the application and toggles the main container
 * depending on the number of remembered decors
 * @return false if no decors were remembered, else true
 */
Application.prototype.init = function() {
  if(app.getNumPreviews() == 0) {
    $('main').style.display = 'none';
    $('noDecors').style.display = 'block';
    return false;
  }
  else {
    $('main').style.display = 'block';
    $('noDecors').style.display = 'none';
  }
  return true;
}


/**
 * Registers application wide event handler
 */
Application.prototype.regEventHandler = function() {
  this.regPanelHandler();
  this.regInfoPopupHandler();
  this.regTrashcanHandler();
  this.regMediumClickHandler();
  this.regCpOkHandler();
}

Application.prototype.regMediumClickHandler = function() {
  var meds = document.getElementsByClassName("medium");
  for(var i=0; i<meds.length; i++) {
    new Control.ColorPicker("colorfield", {
      "swatch": meds[i],
      "onOpen": function() {
        // picker gets his start color from #colorfield value
        this.swatch.style.background = '#' + $('colorfield').value;
        app.ctrl.showReset();
        app.ctrl.setColorInfo(this.swatch, '#' + $('colorfield').value);
      },
      "onUpdate": function() {
        app.ctrl.setColorInfo(this.swatch, '#' + $('colorfield').value);
        /*// paint border to avoid "color=background effect"
        this.swatch.style.border = app.currentColor('dropBorderStartDrag');*/
        app.dropbox.meds[this.swatch.id].color = '#' + $('colorfield').value;
        app.dropbox.meds[this.swatch.id].image = "";
        //app.dropbox.save(); // might flood server with AJAX-requests
      }
    });
    // click into medium save state
    Event.observe(meds[i], 'click', function() {
      app.dropbox.save();
    });
  }
  
}


Application.prototype.regPanelHandler = function() {
  // --- reset all ---
  // Event.observe('resetAll', 'click', this.ctrl.resetView, false);
  Event.observe('resetAll', 'click', function() {
    // reset images
    var meds = document.getElementsByClassName('medium');
    for(var i=0; i<meds.length; i++) {
      if(meds[i].style.background != app.currentColor('dropBackground')) {
        meds[i].style.background = app.currentColor('dropBackground');
        med = app.dropbox.meds[meds[i].id];
        med.color = "";
        med.image = "";
        med.decor_id = "";
        med.subcollection_id = "";
      }
    }
    
    // hide all info divs
    var infos = document.getElementsByClassName('info');
    for(var i=0; i<infos.length; i++) {
      infos[i].style.visibility = 'hidden';
    }
    
    app.ctrl.resetTouch();
    app.ctrl.hideReset();
    app.dropbox.save();
  });
  
  // --- colorschemes ---
  Event.observe('standardScheme', 'click', function() {
    app.colorscheme = 'standard';
    app.ctrl.updatePlayground();
  });
  Event.observe('whiteScheme', 'click', function() {
    app.colorscheme = 'white';
    app.ctrl.updatePlayground();
  });
  Event.observe('blackScheme', 'click', function() {
    app.colorscheme = 'black';
    app.ctrl.updatePlayground();
  });
    
  // --- touch ---
  Event.observe('touch_icon', 'click', function() {
    if(!app.touched)
      app.ctrl.setTouch();
    else
      app.ctrl.resetTouch();
  });
}
/**
 * Applies onclick handler to all elements with id and class "toggle"
 * and binds a toggle function to it through the event observer.
 * The toggle function toggles the corresponding elements with id
 * and class "desc".
 *
 * @example
 * element with id "toggle_4" (and class "toggle"),
 * will toggle element with id "desc_4" (and class "desc")
 */
Application.prototype.regInfoPopupHandler = function() {
  var toggles = document.getElementsByClassName("toggle");
  for(var i=0; i<toggles.length; i++) {
    toggles[i].onclick=function() {
      var str = this.id || window.event;
      var id = str.replace('toggle', 'desc');
      Element.toggle(id);
    }
    /*    
    var infoClickHandler=function(event) {
      var str = (Event.element(event)).id;
      var id = str.replace('toggle', 'desc');
      Element.toggle(id);
    }.bindAsEventListener(this);
    Event.observe(toggles[i], "click", infoClickHandler);    
    */
  }
}
Application.prototype.regTrashcanHandler = function() {
  Droppables.add('trashcan', {
    accept: 'dragItem',
    onDrop: function(dragElem, dropElem) {    
      app.ctrl.removeDecorFromBasket(dragElem);
      //app.ctrl.removePreview(dragElem);
      Element.remove(dragElem);
      //app.ctrl.clearView();
      if(app.getNumPreviews() == 0)
        app.init();
    }
  });
}
/**
 * Add a click event to the Colorpicker OK button
 */
Application.prototype.regCpOkHandler = function() {
  Event.observe('colorpicker-okbutton', 'click', function() {
    app.dropbox.save();
  });
}

/**
 * Converts id of a draggable element to its corresponding database id
 * @param elem string  draggable element
 * @return database id
 */
Application.prototype.dragElemToDecorId = function(elem) {
  return elem.id.replace('drag_', '');
}


// --------------------------------------------------------------------
//  DragBox
// --------------------------------------------------------------------


/**
 * Container class holding Draggables
 */
function DragBox(dragArea, preWidth, preHeight) {
  this.id = dragArea || "";
  this.preWidth = preWidth || "";
  this.preHeight = preHeight || "";
  this.counter = 0; // number of registered draggables
}


/**
 * Makes remembered decor preview images draggable
 * and applies onmouseover and onmouseout events in the same loop
 * PRE: some preview images are bigger than others and some are 1px images
 */
DragBox.prototype.registerDraggables = function() {
  var imgs = $(this.id).getElementsByTagName('img');
  
  // -- apply onmouseover and onmouseout handler ---
  var overHandler = function(event) {
    if(Event.element) { // workaround
      Event.element(event).style.border = app.currentColor('dropBorderStartDrag');
      //Event.element(event).style.padding = '0px'; // not for ie but better 
      Event.element(event).style.margin = '0px 6px 1px 0px';
    }
  }.bindAsEventListener(this);
  var outHandler = function(event) {
    if(Event.element) { // workaround
      Event.element(event).style.border = app.currentColor('previewBorder');
      //Event.element(event).style.padding = '1px'; // not for ie but better 
      Event.element(event).style.margin = '1px 7px 2px 1px';
    }
  }.bindAsEventListener(this);
 
  for(var i=0; i<imgs.length; i++) {
    // apply default size to decors
    if(imgs[i].width != this.preWidth)
      imgs[i].width = this.preWidth;
    if(imgs[i].height != this.preHeight)
      imgs[i].height = this.preHeight;
    
    Event.observe(imgs[i], "mouseover", overHandler);
    Event.observe(imgs[i], "mouseout", outHandler);
      
    // register draggable
    new Draggable(imgs[i].id, {
      revert: true,
      starteffect: function(el) {
        // stop observing mouse events on current draggable while dragging
        // (ie bug)
        Event.stopObserving(el, "mouseover", overHandler);
        Event.stopObserving(el, "mouseout", outHandler);
        
        // stop observing mouse events on all draggables
        // (safari fires onmouseover on draggables while hovering)
        var imgs = $('dragArea').getElementsByTagName('img');
        for(var k=0; k<imgs.length; k++) {
          Event.stopObserving(imgs[k], "mouseover", overHandler);
        }
        
        var meds = app.dropbox.getMediumIds();
        for(var j=0; j<meds.length; j++) {
          $(meds[j]).style.border = app.currentColor('dropBorderStartDrag');
	        //$(meds[j]).style.padding = '0px';
          $(meds[j]).style.margin = '0px 6px 1px 0px';
        }
        $('trashcan').style.border = app.currentColor('dropBorderStartDrag');
	      //$(el).style.padding = '1px';
	      $('trashcan').style.padding = '0px';
	      $(el).style.margin = '1px 7px 2px 1px';
	      //$('trashcan').style.margin = '0px 6px 1px 0px';
        
        // paint border of current draggable
        $(el).style.border = app.currentColor('previewBorder');
      },
      endeffect:function(el) {
        var meds = app.dropbox.getMediumIds();
        for(var j=0; j<meds.length; j++) {
          $(meds[j]).style.border = app.currentColor('dropBorder');
	        //$(meds[j]).style.padding = '1px';
	        $(meds[j]).style.margin = '1px 7px 2px 1px';
        }
        $('trashcan').style.border = app.currentColor('previewBorder');
	      $('trashcan').style.padding = '1px';
	      //$('trashcan').style.margin = '1px 7px 2px 1px';
      
        // paint border of current draggable (safari)
        $(el).style.border = app.currentColor('previewBorder');
        
        // start observing mouse events on current draggable again (ie bug)
        Event.observe(el, "mouseover", overHandler);
        Event.observe(el, "mouseout", outHandler);
      
        // start observing mouse events on all draggables again
        // (safari fires onmouseover on draggables while hovering)
        var imgs = $('dragArea').getElementsByTagName('img');
        for(var k=0; k<imgs.length; k++) {
          Event.observe(imgs[k], "mouseover", overHandler);
        }
      }
    });   
    this.counter++;
  }
}


// --------------------------------------------------------------------
//  DropBox
// --------------------------------------------------------------------


/**
 * Class Medium: constructor
 */
function Medium() {
  this.decor_id = "";
  this.subcollection_id = "";
  this.image = "";
  this.color = "";
}


/**
 * Container class holding Droppables
 */  
function DropBox(dropArea, medWidth, medHeight) {
  this.id = dropArea || "";
  this.counter = 0; // number of registered droppables
  this.meds = new Object();
  var medIds = this.getMediumIds();
  for(var i=0; i<medIds.length; i++) {
    this.meds[medIds[i]] = new Medium();
  }
}


/**
 * Iterates over all images inside the DropBox and registers them
 * as droppable elements
 */
DropBox.prototype.registerDroppables = function() {
  var meds = document.getElementsByClassName('medium');
  
  for(var i=0; i<meds.length; i++) {
    // --- register droppable ---
    Droppables.add(meds[i], {
      accept: 'dragItem',
      onDrop: function(dragElem, dropElem) {
        var decorId = app.dragElemToDecorId(dragElem);
        new Ajax.Request('/cgi-bin/yget_decor_info.pl', {
          parameters: 'did=' + decorId,
          onSuccess: function(t) {
            var json = JSON.parse(t.responseText); // sanitize
            // show reset area if at least one image was dropped
            if(!app.resetShown)
              app.ctrl.showReset();
            // update medium
            app.ctrl.setMediumImage(dropElem, json);
            app.ctrl.setDecorInfo(dropElem, json);
            app.dropbox.save();    
          },
          onFailure: function(t) {
            alert('Error ' + t.status + ' -- ' + t.statusText);
          }
        });
      }
    });    
    // we need to set this in js, otherwise the background style is empty
    meds[i].style.background = app.currentColor('dropBackground');    
    
    this.counter++;
  }
}


/**
 * @return Array Ids of all medium images
 */
DropBox.prototype.getMediumIds = function() {
  var meds = document.getElementsByClassName('medium');
  var ids = new Array();
  for(var i=0; i<meds.length; i++) {
    ids[i] = meds[i].id;
  }
  return ids;
}


/**
 * Checks if all medium images are set
 * (resp. one image is not equal to 'dropBackground')
 * @return true if all medium images are set, else false
 */
DropBox.prototype.isFull = function() {
  var divs = document.getElementsByClassName('medium');
  for(var i=0; i<divs.length; i++) {
    // strstr() equivalent, returns -1 if not found
    // index_of_string2_in_string1 = string1.indexOf(string2);
    // alert(divs[i].id + ": " + divs[i].style.backgroundImage);
    if((divs[i].style.background).indexOf(app.currentColor('dropBackground')) != -1) {
      return false;
    }
  }
  return true;
}


/**
 * Checks if a medium image is set
 * (resp. one image is equal to 'dropBackground')
 * @return true if no image is set, false if at least one image is set
 */
DropBox.prototype.isEmpty = function() {
  var isEmpty = true;
  var divs = document.getElementsByClassName('medium');
  for(var i=0; i<divs.length; i++) {
    if((divs[i].style.background).indexOf(app.currentColor('dropBackgroundImg')) == -1) {
      isEmpty = false;
    }
  }
  return isEmpty;
}


/**
 * Initialises the drop box
 * Loads state of medium images and sets them to the appropriate value
 */
DropBox.prototype.init = function() {
  new Ajax.Request('/cgi-bin/init_decor_info.pl', {
    method: 'get',
    onSuccess: function(t) {
      var json = JSON.parse(t.responseText); // sanitize
      //alert(json);
      meds = app.dropbox.meds;
      for(medId in meds) {
//         alert(json[medId]);
        if((typeof(json[medId]) != 'object') && json[medId].charAt(0) == '#') { // color
          $(medId).style.background = json[medId];
          app.dropbox.meds[medId].color = json[medId];
          app.ctrl.setColorInfo($(medId), json[medId]);
        }
        else if(typeof(json[medId]) == 'object') {  // image
          app.ctrl.setMediumImage($(medId), json[medId]);
          if(json[medId].dname)
            app.ctrl.setDecorInfo($(medId), json[medId]);
        }
      }
    },
    onFailure: function(t) {
      alert('Error ' + t.status + ' -- ' + t.statusText);
    }/*,
    onSuccess: function(t) {
      alert('Success ' + t.status + ' -- ' + t.statusText);
    }*/
  });
}


/**
 * Saves the drop box state
 * Fails silently in case of an error
 */
DropBox.prototype.save = function() {
  meds = app.dropbox.meds;
  params = new Array();
  for(medId in meds) {
    //alert(medId + '\ncolor:'+meds[medId].color+'\nimage: '+meds[medId].image+'\ndecor_id: '+meds[medId].decor_id+'\nsubcollection_id: '+meds[medId].subcollection_id);
    if(meds[medId].image != "") { // image
      if(meds[medId].decor_id != "" && meds[medId].subcollection_id != "")
        params[medId] = meds[medId].decor_id + "," + meds[medId].subcollection_id;
    }
    else if(meds[medId].image == "" && meds[medId].color.charAt(0) == '#') { // color
      params[medId] = meds[medId].color;
    }
    else { // not set
      params[medId] = "";
    }
  }  
  
  new Ajax.Request('/cgi-bin/save.pl', {
    method: 'post',
    parameters: 'medium1='  + params['medium1'] +
                '&medium2=' + params['medium2'] +
                '&medium3=' + params['medium3']/*,
    onSuccess: function(t) {
      alert('Success ' + t.status + ' -- ' + t.statusText);
    },
    onFailure: function(t) {
      alert('Error ' + t.status + ' -- ' + t.statusText);
    }*/
  });
}


// --------------------------------------------------------------------
//  Controller
// --------------------------------------------------------------------


function Controller() {} // TODO use object literal

/**
 * Applies background to a medium
 * @param medElem  Reference to medium element
 * @param json     Reference to full json object
 */
Controller.prototype.setMediumImage = function(medElem, json) {
  medElem.style.background = 'url(' + app.medPath + json.image + ')';
  med = app.dropbox.meds[medElem.id];
  med.image = json.image;
  med.decor_id = json.decor_id;
  med.subcollection_id = json.subcollection_id;
}


/**
 * Resets a medium image (by applying the dropBackground)
 * Hides reset panel if the dropbox becomes empty
 * @param medElem  Reference to medium element that will be reseted
 */
Controller.prototype.resetMedium = function(medElem) {
  var mediumImgId = new String(medElem.id);
  var infoDivId = mediumImgId.replace('mediumImg', 'info');
  $(infoDivId).style.visibility = 'hidden';
   
  medElem.style.background = app.currentColor('dropBackground');
  medElem.style.border = app.currentColor('dropBorder');
  
  // hide reset panel
  if(app.dropbox.isEmpty())
    app.ctrl.hideReset();
}


/**
 * Updates the info area
 * @param medElem  Reference to a medium element
 * @param json     Reference to evaluated json object
 * PRE: not all decors have the "app" attribute
 *      some decors don't have codes / are undefinied
 */
Controller.prototype.setDecorInfo = function(medElem, json) {
  /**
   * @return  Full qualified anchor tag by link
   */
  var toUrl = function(link) {
    return "<a class='value' href='kollektionen/dekor/" + json.decor_id +
           "/" + json.subcollection_id + "'>" + link + "</a>";
  }
  
  var infoDivId = new String(medElem.id).replace('medium', 'info');
  var divs = $(infoDivId).getElementsByTagName('div');
  
  // --- "info" container ---
  $(infoDivId).style.visibility = 'visible';
  // --- "no" inner div ---
  var noapp = json.no;
  if(json.app)
    noapp += '-' + json.app;
  divs[0].innerHTML = toUrl(noapp);
  // --- "name" inner div ---
  divs[1].innerHTML = toUrl(json.dname);
  // --- "code" inner div ---
  if(json.code)
    divs[2].innerHTML = json.code;
  else
    divs[2].innerHTML = '&nbsp;'; // always overwrite to avoid leftovers
  
  // fix for links being not updated on certain color schemes
  app.ctrl.updatePlayground();
}


/**
 * Inserts a search link into the info container
 * @param medElem     Reference to a medium element
 * @param string rgb  RGB hex value (without hashmark)
 */
Controller.prototype.setColorInfo = function(medElem, rgb) {
  var infoDivId = new String(medElem.id).replace('medium', 'info');
  var divs = $(infoDivId).getElementsByTagName('div');
  divs[0].innerHTML = '&nbsp;'; //"<a class='value' href='" + app.cpSearchUrl + rgb.substr(1,6) + "'>" + "&Auml;hnliche Dekore suchen</a>"; // FIXME
  divs[1].innerHTML = '&nbsp;';      
  divs[2].innerHTML = '&nbsp;';  
  $(infoDivId).style.visibility = 'visible'; // info container
  // fix for links being not updated on certain color schemes
  app.ctrl.updatePlayground();
}


/**
 * Hide or show the resetArea
 */
Controller.prototype.showReset = function() {
  $('resetAll').style.cursor = 'pointer';
  $('resetAll').style.color = app.currentColor('panelForeground');
}
Controller.prototype.hideReset = function() {
  $('resetAll').style.cursor = '';
  $('resetAll').style.color = app.currentColor('panelDisabledForeground');
}

/**
 * Set or reset the touch effect
 */
Controller.prototype.setTouch = function() {
  $('medium1').className = 'medium touch';
  $('medium2').className = 'medium touch';
  $('medium3').className = 'medium touch';
  $('info1').className = 'info touch';
  $('info2').className = 'info touch';
  $('info3').className = 'info touch';
  $('touch_icon').src = '/images/global/touch-back.png'
  app.touched = true;
  app.ctrl.showReset();
}
Controller.prototype.resetTouch = function() {
  $('medium1').className = 'medium';
  $('medium2').className = 'medium';
  $('medium3').className = 'medium';
  $('info1').className = 'info';
  $('info2').className = 'info';
  $('info3').className = 'info';  
  $('touch_icon').src = '/images/global/touch.png'
  app.touched = false;
  if(app.dropbox.isEmpty())
    app.ctrl.hideReset();
}

/**
 * Removes id of dropped preview image element from session cookie
 * by sending an Ajax request
 * @param dragElem  Element to remove from basket
 */
Controller.prototype.removeDecorFromBasket = function(dragElem) {
  new Ajax.Request('/cgi-bin/decor_basket.pl', {
    parameters: 'did=' + app.dragElemToDecorId(dragElem), // id of dropped image
    onFailure: function(t) {
      alert('Error ' + t.status + ' -- ' + t.statusText);
    }
  });
}


/**
 * Colors playground area depending on the current color scheme
 */
Controller.prototype.updatePlayground = function() {
  $('playgroundSection').style.backgroundColor = app.currentColor('playgroundBackground');
  $('playgroundSection').style.border = app.currentColor('playgroundBorder');
  $('panelArea').style.color = app.currentColor('panelForeground');
  
  // set foreground color of class value elements
  var values = document.getElementsByClassName('value');
  for(var i=0; i<values.length; i++) {
    values[i].style.color = app.currentColor('panelForeground');
  }
  
  if(app.dropbox.isEmpty())
    app.ctrl.hideReset();
  else    
    app.ctrl.showReset();
}
