/**
 * ウィンドウ・システム風のWebサイトを実現するためのモジュール
 */
if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function(target) {
    for (var i = 0; i < this.length; i++) {
      if (this[i] === target) {
        return i;
      }
    }
    return -1;
  };
}

$(function() {
  /**
   * EventEmitter Pattern from move.js written by visionmedia
   * https://github.com/visionmedia/move.js/blob/master/move.js
   */
  var EventEmitter = function() {
    this.callbacks = {};
  };

  EventEmitter.prototype.on = function(event, fn) {
    (this.callbacks[event] = this.callbacks[event] || []).push(fn);
    return this;
  };

  EventEmitter.prototype.emit = function(event) {
    var args = Array.prototype.slice.call(arguments, 1)
      , callbacks = this.callbacks[event]
      , len;
    if (callbacks) {
      len = callbacks.length;
      for (var i = 0; i < len; ++i) {
        callbacks[i].apply(this, args);
      }
    }
    return this;
  };

  var Manager = function(elem) {
    EventEmitter.call(this);
    var that = this
      , boxes = {};

    $(elem).find('div[data-utakata-template]').each(function() {
      var tmplName = this.getAttribute('data-utakata-template');
      var $boxes = $(this).find('.box');
      
      $boxes.each(function(i) {
        var box = new Box(that, this);
        if (boxes[this.id]) {
          throw new Error('Box [' + this.id + '] already exists.');
        } else {
          boxes[this.id] = box;
        }
        var tmpl = $('#' + tmplName).tmpl({
          title: this.getAttribute('title') || '',
          subtitle: this.getAttribute('data-utakata-subtitle') || '',
          useLoader: this.getAttribute('data-utakata-loader') === 'true',
          closable: this.getAttribute('data-utakata-closable') === 'true',
          content: this.innerHTML
        });
        tmpl.appendTo(box.element.empty());

        box.element.find('.close').live('mousedown', function(e) {
          $(this).one('mouseup', function(e) {           
            var id = $(this).parents('.box').prop('id')
            , box = that.getBox(id);
            box.close();
          });
        });

        setTimeout(function() { box.emit('initialize'); }, 0);
      });
      //冗長だけれど2回みる
      $boxes.each(function() {
        var that = this
          , ids = this.getAttribute('data-utakata-parent')
          , tags = this.getAttribute('data-utakata-tags');
        if (ids) {
          $.each(ids.split(' '), function(i, id) {
            boxes[id].addChild(boxes[that.id]);
          });
        }
        if (tags) boxes[that.id].tags = tags.split(' ');
      });
    });
    this._boxes = boxes;
  };

  Manager.prototype = new EventEmitter();

  Manager.prototype.of = function(query, callback) {
    var boxes = this.getBoxes(function(box) {
      if (typeof query === 'string') {
        return box.id === query;
      } else if (query instanceof RegExp) {
        return query.test(box.id);
      } else {
        throw new Error('Invalid query [' + query + '].');
      }
    });
    $.each(boxes, function(i, box) {
      callback(box);
    });
  };

  Manager.prototype.getBox = function(id) {
    var box = this._boxes[id];
    if (box) {
      return box;
    } else {
      throw new Error('Cannot find box [' + id + '].');
    }
  };

  Manager.prototype.getBoxes = function(callback) {
    var id
      , box
      , filtered = [];
    for (id in this._boxes) {
      box = this._boxes[id];
      if (typeof callback === 'function') {
        if (callback(box)) filtered.push(box);
      } else {
        filtered.push(box);
      }
    }
    return filtered;
  };
  
  Manager.prototype.moveTo = function(pageName) {
    var page = this._pages[pageName];
  };

  Manager.prototype.switchTo = function(tagName) {
    var id
      , box;
    this.emit('tagchange', tagName);
    for (id in this._boxes) {
      box = this._boxes[id];
      if (box.tags.indexOf(tagName) > -1) {
        if (box.openDefault) box.open();
      } else {
        box.close();
      }
    }
  };

  Manager.prototype.getBoxesByTagName = function(tagName) {
    var id
      , box
      , filtered = [];
    for (id in this._boxes) {
      box = this._boxes[id];
      if (box.tags.indexOf(tagName) > -1) filtered.push(box);
    }
    return filtered;
  };

  Manager.prototype.getMaxDepth = function(target) {
    var tmp = [];
    $.each(this._boxes, function(i, box) {
      var z = box.element.css('zIndex');
      if (z !== 'auto' && box.id !== target.id) tmp.push(z);
    });
    return (tmp.length > 0 ? Math.max.apply(null, tmp) : 0) + 1;
  };

  var Box = function(manager, elem) {
    EventEmitter.call(this);
    this.status = 'closed';
    this.manager = manager;
    this.id = elem.id;
    this.element = $(elem);
    this.children = [];
    this.parents = [];
    this.tags = [];
    this.openDefault = !$(elem).hasClass('hide');
  };

  Box.prototype = new EventEmitter();

  Box.prototype.close = function() {
    this._eachChild(function(box) {
      if (box.status === 'open') {
        box.status = 'closed';
        box.emit('close');
      }
    });
  };

  Box.prototype.open = function() {
    //運用の都合上、開く処理は子boxに伝播しない
    if (this.status === 'closed') {
      this.status = 'open';
      this.emit('open');
    }
    /*
    this._eachChild(function(box) {
      if (box.status === 'closed') {
        box.status = 'open';
        box.emit('open');
      }
    });
     */
  };

  Box.prototype.error = function(msg) {
    this.emit('error');
  };

  Box.prototype.success = function() {
    this.emit('success');
  };

  Box.prototype.addChild = function(box) {
    box.parents.push(this);
    this.children.push(box);
  };

  Box.prototype._eachChild = function(callback) {
    var checked = {};
    var iter = function(box) {
      if (!checked[box.id]) {
        setTimeout(function() { callback(box); }, 0);
        checked[box.id] = true;
        $.each(box.children, function(i, childBox) {
          iter(childBox);
        });
      } else {
        throw new Error('Relation between boxes is looped! Please check attribute data-utakata-parent.');
      }
    };
    iter(this);
  };

  window.Utakata = Manager;
});


