// Copyright (c) 2008 Fatguy AS
//
// *some copyright notice*
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software

// Modifications by:
// Copyright (C) 2009 Node7, Kristoffer Tjaernaas. All rights reserved.



var Fatlets = function(options) {
  if (!(this instanceof arguments.callee)) {
    return new arguments.callee(options);
  }
  
  var $ = window.jQuery;
  var self = this;
  var save = true;
  var curr_id = 1;
  var box_pos;
  var start_opts; 
  var local_options = ['wrapper', 'prefix', 'columns', 'columns_updated', 'save'];
  var callbacks = {
    start: function(e, ui) {
      var box = ui.item.data('fatlet');
      box_pos = box.pos(ui.placeholder);
    },
    
    stop: function(e, ui) {
     
      var box = ui.item.data('fatlet');
      var pos = box.pos(ui.placeholder);
      if (!(box_pos[0] == pos[0] && box_pos[1] == pos[1])) {
        self.save();
      }
    }
  };
  
  var joptions = options || {};
  joptions = $.extend({prefix: 'box', columns: 'columns', items: 'box'}, joptions);
  options = extract_options(joptions);
  var columns_class = options.wrapper + ' .' + options.columns;
    
  this.boxes = {};
  this.widgets = {};
  
  
  
  
  function extract_options(joptions) {
    var n = {};
    $.each(joptions, function(key, val) {
      if (key == "items") {
        
        
        n[key] = val;
        joptions[key] = '.' + val;
      } else if ($.inArray(key, local_options) != -1) {
        
        n[key] = val;
        delete joptions[key];
      } else if (typeof callbacks[key] == "function") {
        if (typeof val == "function") {
          var old = val;
        }
        
        joptions[key] = function() {
          var args = $.makeArray(arguments);
          callbacks[key].apply(this, args);
          run(old, args, this);
        };   
      }
    });
    
    $.each(callbacks, function(key, val) {
      if (!(typeof joptions[key] == "function")) {
        joptions[key] = val;
      }
    });
    
    return n;
  }
  
  function box_id(id) {
    return options.prefix + '-' + id;
  }
  
  
  function pos(at, siblings) {
    if (at == -1) {
      at = siblings.length - 1;
      var neg = true;
    }
    
    return [siblings.eq(at), neg || (at < 0)];
  }
  
  function find_pos(at, siblings) {
    return pos(at, siblings)[0];
  }
  
  
  function add_among(at, parent, obj) {
    var siblings = parent.children();
    
    if (siblings.length == 0) {
      return parent.html(obj);
    }
    
    var p = pos(at, siblings);
    var r = p[0];
    var neg = p[1];
    
    if (neg) {
      return r.after(obj);
    } else {
      return r.before(obj);
    }
  }
  
  function add_inside(at, siblings, obj) {
    var p = pos(at, siblings);
    
    
    return $(p[0]).append(obj);
  }
  
  function run(obj, args, context) {
    if (typeof obj == "function") {
      return obj.apply((context || this), (args || []));
    }
  }
  
  this.init = function() {
    var columns = $(columns_class);

    columns.each(function() {
      $.extend(joptions, {
        connectWith: columns
      });
      
      $(this).sortable("destroy").sortable(joptions);
    });
    
    run(options.columns_updated, [columns]);
  };
  
  this.add_column = function(at) {
    at = (typeof at == "number") ? at : -1;
    var column = $('<div/>').
               attr('class', options.columns);
    
    var wrapper = $(options.wrapper);
    
    add_among(at, wrapper, column);
    this.init();
  };
  
  this.add_columns = function(num) {
    for (var i=0; i < num; i++) {
      this.add_column(-1);
    }
  };
  
  this.remove_column = function(at) {
    $(find_pos(at, $(columns_class))).remove();
    this.init();
  };
  
  
  this.save = function() {
    
    
    if (options.save && save  && false) {
      var columns = $(columns_class);
      var res = [];
      columns.each(function(key, value) {
        res.push($.map($(value).sortable("toArray"), function(id) {
          var fat = $('#' + id).data('fatlet');
          
          return {
            widget: fat.name,
            options: fat.options,
            interval: fat.interval
          };
        }));
      });
      run(options.save, [res]);
      
      
      return res;
    }
  };
  
  this.load = function(arr) {
    
    $(options.wrapper).empty();
    self.boxes = [];
    curr_id = 1;
    save = false;
    
    $.each(arr, function() {
      self.add_column();
      $.each(this.reverse(), function() {
        var box = self.add(this.widget, this.options, -1);
        box.start_timer(this.interval);
      });
    });
    save = true;
  };
  
  this.add = function(widget, opts, at) {
    var id = curr_id++;
    
    
    
    
    var box = new this.box(id, widget, opts || {});
    
    add_inside(at, $(columns_class), box.element);
    
    box.loading();
    box.load();
    
    self.save();
    
    return box;
  };
  
  this.register = function(name, widget) {
    this.widgets[name] = widget;
  };
  
  this.widget = function(name) {
    return this.widgets[name];
  };
    
  this.box = function(id, widget, opts, element) {
    if (!(this instanceof arguments.callee)) {
      var box = self.boxes[id];
      if (box) {
        return box;
      } else {
        return false;
      }
    }
    
    this.init = function(widget, opts, element) {
      this.parent = self;
      this.name = widget;
      this.widget = self.widget(widget);
      this.interval = this.widget.interval;
      this.options = opts;
      this.element = element || $(document.createElement('div'));
      this.element.empty().
                   removeClass().
                   addClass(options.items).
                   addClass(this.widget.klass).
                   attr('id', box_id(id)).
                   data('fatlet', this);
      run(this.widget.create, [this], this.widget);
    };
    
    this.loading = function() {
      run(this.widget.loading, [this], this.widget);
    };
    
    this.load = function() {
        var content = this.widget.load(this);
        
      if (content) {
        this.element.html(content);
        this.loaded();
      }
    };
    
    this.loaded = function() {
        run(this.widget.loaded, [this], this.widget);
        
      this.run_timer();
    };

    this.update = function(opts) {
        this.loading();
        
        if(opts){
            this.updateOptions(opts);
        }
        this.load();
    };
    
    this.updateOptions = function(opts) {
        if(opts){
            box = self.boxes[this.id];
            
            var blockprefsURL = "/ezcore/n7/prefs/"+portal_id+"/"+box.options.zone+"/"+box.options.block_id+"/";
            
            $.ajax({
                type: "POST",
                url: blockprefsURL,
                data: ({ "preferences": JSON.stringify(opts.preferences)}),
                async: false,
                
                success: function(data){
            
                }
            });
            
            
        } else {
                
        }
        if (opts) {
            $.extend(this.options, opts); 
            self.save();
        }
    };
    
    this.remove = function() {
      this.stop_timer();
      this.element.remove();
      delete self.boxes[this.id];
      self.save();
    };
    
    this.run_timer = function() {
      if (this.interval) {
        var t = this;
        this.timer = setTimeout(function(){
          t.allow_timer = false;
          t.update.call(t);
        }, this.interval * 1000);
      } else {
        this.allow_timer = true;
      }
    };
    
    this.stop_timer = function() {
      clearTimeout(this.timer);
      this.interval = null;
      this.allow_timer = true;
    };
    
    this.start_timer = function(interval) {
      interval = interval || this.widget.interval;
      this.interval = interval;
      if (this.allow_timer) {
        this.run_timer();
      }
    };
    
    this.pos = function(except) {
      var e = this.element;
      var c = e.parent();
      return [c.prevAll().length, e.prevAll().not(except).length];
    };
    
    this.id = id;
    this.init(widget, opts, element);
    self.boxes[this.id] = this;  
  };

  $(options.wrapper).empty();
  this.init();
};