if (!window.pb) {
  window.pb = {};
}

pb.windowHandler = {
  windows: [],

 imSettingFocus: false,

 create: function(id, title, content, args) {
    var win = this.findWindow(id);
    if (win != null) {
      win[1].reload();
      win[1].toggleState('m', 'normal');
      win[1].giveFocus();
      return;
    }
    var win = new pb.window(id, title, content, args);
    win.onFocus = (function(window) { if (!this.imSettingFocus) this.goThroughOrder(window); }).bind(this);
    this.add(win);

    win.onClose = win.onClose.wrap((function(orig, window) {
      orig(window);
      this.removeWindow(window);
    }).bind(this));

    this.addToBar(win);
    win.giveFocus();
    return (win);
  },

  addToBar: function(window) {
    if (!this.bar) {
      this.bar = document.createElement('div');
      this.bar.id = 'windowBar';
      document.body.appendChild(this.bar);
    }

    var id = 'windowBar_' + window.getId();
    if (!$(id)) {
      var div = document.createElement('div');
      div.id = id;
      div.innerHTML = window.title;
      div.title = window.title;
      window.onTitleChange = window.onTitleChange.wrap((function(orig, window, title) { div.innerHTML = title; div.title = title; }));
      Event.observe(div, 'click', (function() {
        if (window.getState().indexOf('m') == -1 && window.hasFocus()) {
          window.toggleState('m', 'minimize');
          this.goThroughOrder();
        } else {
          window.toggleState('m', 'normal');
          this.goThroughOrder(window);
        }
      }).bind(this));
      this.bar.appendChild(div);
    }
  },

  removeFromBar: function(window) {
    var div = $('windowBar_' + window.getId());
    if (div) div.parentNode.removeChild(div);
  },

  createBar: function() {
    this.bar = document.createElement('div');
    this.bar.id = 'windowBar';
    document.body.appendChild(this.bar);
  },

  removeWindow: function(window) {
    var result = [-1, null];
    this.windows.each(function(win, index) {
      if (win.getId() == window.getId()) {
        result = [ index, win ];
        throw $break;
      }
    });
    if (result[0] > -1) {
      this.removeFromBar(window);
      delete this.windows[result[0]];
      this.windows = this.windows.compact();
    }
    this.goThroughOrder();
  },

  findWindow: function(id) {
    var result = null;
    this.windows.each(function(win, index) {
      if (!!win && win.getId() == id) {
        result = [ index, win ];
        throw $break;
      }
    });
    return result;
  },

  add: function(window) {
    var id = this.windows.length;
    this.windows[id] = window;

    var zi = window.getZIndex();
    if (zi == undefined) {
      zi = 700 + id;
    }
    window.setZIndex(zi);
  },

  goThroughOrder: function(window) {
    var windowId = null;
    if (window != undefined) var windowId = window.getId();
    this.windows = this.windows.sortBy(function(win) {
      var zi = win.getZIndex();
      if (zi == undefined) zi = 999;
      if (win.getState().indexOf('m') > -1) zi = 0;
      else if (win.getId() == windowId) zi = 1000;
      return zi;
    });

    var zi = 500;
    var dbg = '';
    this.windows.each(function(window, id) {
      window.removeFocus();
      window.setZIndex(zi, false);
      zi++;
    });

    this.imSettingFocus = true;
    (this.windows[this.windows.length -1]).giveFocus();
    this.imSettingFocus = false;


    saveSettings();
  }
}

var observingMouse = false;
var mousePosition = [];
var mouseEvent = function(e) {
  mousePosition = [Event.pointerX(e), Event.pointerY(e)]
};
function observeMouse() {
  if (observingMouse) return;
  Event.observe(document, 'mousemove', mouseEvent);
  observingMouse = true;
}


pb.window = Class.create({
  initialize: function(id, title, content, args) {
    observeMouse();
    if (args == undefined) args = {};
    this.sizerSize = 5;

    this.id = id;
    this.title = title;
    this.content = content;
    this.args = args;
    this.args.scrollOn = (!!this.args.scroll && (this.args.scroll.indexOf('h') > -1 || this.args.scroll.indexOf('v') > -1));
    if (this.args.closable == undefined) this.args.closable = true;

    this.nodes = {};

    this.initEvents();
    this.createNodes();
    this.setTitle(this.title);
    this.setContent(this.content, undefined, undefined, true);

    this.setDimensions();
    this.setStartPosition();
    this.initDragNDrop();
    this.initResize();

    this.initScroll();
    this.redrawWindow(null, null, false);

    this.setState();
  },

  getId: function() { return this.id; },

  setZIndex: function(zi, save) {
    Element.setStyle(this.nodes.container, {zIndex: zi});
    this.setSettingsProperty('zi', zi);

    if (save != false) { saveSettings(); }
  },

  getZIndex: function() {
    return this.getSettingsProperty('zi');
  },

  removeFocus: function() {
    Element.removeClassName(this.nodes.container, 'active');
    Element.addClassName(this.nodes.container, 'inactive');
    this.onBlur(this);
  },

  giveFocus: function() {
    Element.removeClassName(this.nodes.container, 'inactive');
    Element.addClassName(this.nodes.container, 'active');
    this.onFocus(this);
  },

  hasFocus: function() {
    return Element.hasClassName(this.nodes.container, 'active');
  },

  getSettingsProperty: function(property, defaultValue) {
    if (settings.window && settings.window[this.id] && settings.window[this.id][property]) {
      return settings.window[this.id][property];
    } else {
      return defaultValue;
    }
  },

  setSettingsProperty: function(property, value) {
    if (settings.window == undefined) settings.window = {};
    if (settings.window[this.id] == undefined) settings.window[this.id] = {};
    settings.window[this.id][property] = value;
  },

  createNodes: function() {
    this.nodes.container = document.createElement('div');
    this.nodes.title     = document.createElement('h1');
    this.nodes.body      = document.createElement('div');
    this.nodes.tools     = document.createElement('div');

    this.nodes.container.id = this.id;

    Element.addClassName(this.nodes.container, 'window');
    Element.addClassName(this.nodes.container, 'inactive');
    Element.addClassName(this.nodes.body, 'windowContent');
    Element.addClassName(this.nodes.tools, 'windowTools invisible');

    this.nodes.container.appendChild(this.nodes.tools);
    this.nodes.container.appendChild(this.nodes.title);
    this.nodes.container.appendChild(this.nodes.body);

    Event.observe(this.nodes.container, 'mousedown', (function() { this.giveFocus(); }).bind(this));
    Event.observe(this.nodes.title, 'dblclick', (function() { this.toggleState('r') }).bind(this));

    Event.observe(this.nodes.tools, 'mouseover', (function() { Element.removeClassName(this.nodes.tools, 'invisible'); }).bind(this));
    Event.observe(this.nodes.tools, 'mouseout',  (function() { Element.addClassName(this.nodes.tools, 'invisible'); }).bind(this));

    if (this.args.closable) {
      this.nodes.closeBtn = document.createElement('div');
      this.nodes.closeBtn.innerHTML = '&nbsp;';
      this.nodes.closeBtn.setAttribute('title', 'Close');
      Element.addClassName(this.nodes.closeBtn, 'button btnClose');
      this.nodes.tools.appendChild(this.nodes.closeBtn);
      Event.observe(this.nodes.closeBtn, 'click',  (function() { this.close(); }).bind(this));
    }
    if (this.args.minimizable) {
      this.nodes.minimizeBtn = document.createElement('div');
      this.nodes.minimizeBtn.innerHTML = '&nbsp;';
      this.nodes.minimizeBtn.setAttribute('title', 'Minimize');
      Element.addClassName(this.nodes.minimizeBtn, 'button btnMinimize');
      this.nodes.tools.appendChild(this.nodes.minimizeBtn);
      Event.observe(this.nodes.minimizeBtn, 'click',  (function() { this.minimize(); }).bind(this));
    }
    if (this.args.reloadable) {
      this.nodes.reloadBtn = document.createElement('div');
      this.nodes.reloadBtn.innerHTML = '&nbsp;';
      this.nodes.reloadBtn.setAttribute('title', 'Reload');
      Element.addClassName(this.nodes.reloadBtn, 'button btnReload');
      this.nodes.tools.appendChild(this.nodes.reloadBtn);
      Event.observe(this.nodes.reloadBtn, 'click',  (function() { this.reload(); }).bind(this));
    }

    if (this.args.customButtons) {
      var length = this.args.customButtons.length;
      for (var i = 0; i < length; i++) {
        Element.addClassName(this.args.customButtons[i].button, 'button ' + this.args.customButtons[i].cssClass);
        this.nodes.tools.appendChild(this.args.customButtons[i].button);
      }
    }

    document.body.appendChild(this.nodes.container);
  },

  setState: function() {
    this.state = this.getSettingsProperty('state', '');
    if (this.state.indexOf('r') > -1) Element.addClassName(this.nodes.container, 'rolledUp');
    if (this.state.indexOf('m') > -1) Element.addClassName(this.nodes.container, 'minimized');

  },

  /*
    States:
      r - rolled up
      m - minimized
  */
  toggleState: function(state, force) {
    // can only toggle rolled up and minimized
    var stateChanged = false;
    var needRedraw = false;
    switch(state) {
      case 'r':
        if (force == 'down' ||(Element.hasClassName(this.nodes.container, 'rolledUp') && force != 'up')) {
          Element.removeClassName(this.nodes.container, 'rolledUp');
          this.state = this.state.replace(/r/, '');
          needRedraw = true;
        } else {
          Element.addClassName(this.nodes.container, 'rolledUp');
          this.state += 'r';
        }
        stateChanged = true;
        break;
      case 'm':
        if (force == 'normal' || (Element.hasClassName(this.nodes.container, 'minimized') && force != 'minimize')) {
          Element.removeClassName(this.nodes.container, 'minimized');
          this.state = this.state.replace(/m/, '');
          needRedraw = true;
        } else {
          Element.addClassName(this.nodes.container, 'minimized');
          this.state += 'm';
        }
        stateChanged = true;
        break;
    }
    if (stateChanged) {
      this.setSettingsProperty('state', this.state);
      saveSettings();
      if (needRedraw) {
        this.setDimensions();
        var hasWidth = this.hasWidth();
        var hasHeight = this.hasHeight()
        this.fitToContent(!hasWidth, !hasHeight);
        if (hasWidth || hasHeight) this.redrawWindow(null, null, false);
      }
      this.onStateChange(this, this.state);
    }
  },

  getState: function() {
    return this.state;
  },

  initEvents: function() {
    this.onResize        = function(window, direction) {};
    this.onContentChange = function(window) {};
    this.onStateChange   = function(window, state) {};
    this.onClose         = function(window) {};
    this.onTitleChange   = function(window, title) {};
    this.onMinimize      = function(window) {};
    this.onFocus         = function(window) {};
    this.onBlur          = function(window) {};
  },

  setTitle: function(title) {
    this.title = title;
    this.nodes.title.innerHTML = title;
    this.onTitleChange(this, title);
  },

  setContent: function(content, contentType, save, first) {
    //var contentType = this.args.contentType;
    if (!contentType) contentType = this.args.contentType;
    if (!contentType) contentType = 'html';
    if (save == undefined || !!save) this.content = content;

    switch (contentType.toLowerCase()) {
      case 'ajax':
        this.setAJAX(content, first);
        break;
      case 'html':
      default:
        this.setHTML(content);
        if (first) this.fitToContent(!this.hasWidth(), !this.hasHeight());
        break;
    }
  },

  hasWidth: function() {
    if (this.args.width != undefined) return true;
    if (this.getSettingsProperty('width', null) != null) return true;
    return false;
  },

  hasHeight: function() {
    if (this.args.height != undefined) return true;
    if (this.getSettingsProperty('height', null) != null) return true;
    return false;
  },

  setHTML: function(data) {
    this.nodes.body.innerHTML = data;
    if (this.scroller) this.scroller.positionHandles();

    this.onContentChange(this);
  },

  appendHTML: function(data) {
    this.setHTML(this.nodes.body.innerHTML + data);
  },

  setAJAX: function(content, first) {
    if (!content) content = this.content;
    new Ajax.Request(content, {
      method: 'get',
      onSuccess: (function(transport) {
        if (this.args.ajaxSuccess) this.args.ajaxSuccess(this, transport);
        else this.setHTML(transport.responseText);
        if (first) this.fitToContent(!this.hasWidth(), !this.hasHeight());
      }).bind(this)
    });
  },

  setStartPosition: function() {
    var x = this.args.left == undefined ? 0 : this.args.left;
    var y = this.args.top  == undefined ? 0 : this.args.top;

    if (!!this.args.corners && this.args.corners.length == 2) {
      this.args.corner = this.args.corners[0];
      this.args.parentCorner = this.args.corners[1];
    }
    if (this.args.corner == undefined) this.args.corner = 'TL';
    if (this.args.parentCorner == undefined) this.args.parentCorner = 'TL';
    if (this.args.parent == undefined) this.args.parent = 'viewport';

    var pos = this.getCornerPosition(this.args.corner, this.args.parentCorner, this.args.parent, this.args.keepInside, x, y);
    x = pos[0];
    y = pos[1];

    if (!this.args.forcePosDim && !!this.args.movable) {
      x = this.getSettingsProperty('x', x);
      y = this.getSettingsProperty('y', y);
    }
    Element.setStyle(this.nodes.container, { left: x + 'px', top: y + 'px' });
  },

  getCornerPosition: function(myCorner, parentCorner, parent, keepInside, offsetX, offsetY) {
    if (keepInside == undefined) keepInside = true;
    if (parent == undefined) parent = 'viewport';

    var myDim = Element.getDimensions(this.nodes.container);
    var parentDim;
    var parentPosition;
    if (parent == 'viewport') {
      parentDim = document.viewport.getDimensions();
      parentPosition = document.viewport.getScrollOffsets();
    }
    else if (parent == 'mouse') {
      parentDim = { width: 0, height: 0 };
      parentPosition = { left: mousePosition[0], top: mousePosition[1] };
    }
    else {
      parentDim = Element.getDimensions(parent);
      parentPosition = Element.cumulativeOffset(parent);
    }

    var pc = this.getCornerOffset(parentCorner, parentPosition, parentDim);
    var cc = this.getCornerOffset(myCorner, {left: 0,top: 0}, myDim);

    var x = pc[0] - cc[0] + offsetX;
    var y = pc[1] - cc[1] + offsetY;

    if (keepInside) {
      if (parent != 'viewport') {
        parentDim = document.viewport.getDimensions();
        parentPosition = document.viewport.getScrollOffsets();
      }

      if (x + myDim.width > parentPosition.left + parentDim.width) x = parentPosition.left + parentDim.width - myDim.width;
      if (y + myDim.height > parentPosition.top + parentDim.height) y = parentPosition.top + parentDim.height - myDim.height;
      if (x < parentPosition.left) x = parentPosition.left;
      if (y < parentPosition.top) y = parentPosition.top;
    }

    x = Math.floor(x);
    y = Math.floor(y);
    return [x, y];

  },

  getCornerOffset: function(corner, position, dimensions) {
    var x = 0;
    var y = 0;

    if (corner.indexOf('L') > -1) {
      x = position.left;
    } else if (corner.indexOf('C') > -1) {
      x = position.left + (dimensions.width / 2);
    } else if (corner.indexOf('R') > -1) {
      x = position.left + dimensions.width;
    }

    if (corner.indexOf('T') > -1) {
      y = position.top;
    } else if (corner.indexOf('M') > -1) {
      y = position.top + (dimensions.height / 2);
    } else if (corner.indexOf('B') > -1) {
      y = position.top + dimensions.height;
    }

    return [x, y];
  },

  setDimensions: function() {
    var width  = this.args.width  == undefined ? 320 : this.args.width;
    var height = this.args.height == undefined ? 240 : this.args.height;

    if (!this.args.forcePosDim) {
      width  = !!this.args.sizable && this.args.sizable.indexOf('h') > -1 ? this.getSettingsProperty('width', width) : width;
      height = !!this.args.sizable && this.args.sizable.indexOf('v') > -1 ? this.getSettingsProperty('height', height) : height;
    }

    Element.setStyle(this.nodes.body, { width:  width  + 'px' });
    Element.setStyle(this.nodes.body, { height: height + 'px' });
  },

  initDragNDrop: function() {
    if (this.args.movable === true) {
      var minX = this.args.minX == undefined ? 0 : this.args.minX;
      var minY = this.args.minY == undefined ? 0 : this.args.minY;
      var maxX = this.args.maxX == undefined ? null : this.args.maxX;
      var maxY = this.args.maxY == undefined ? null : this.args.maxY;
      Drag.init(this.nodes.title, this.nodes.container, minX, maxX, minY, maxY);
      Drag.init(this.nodes.tools, this.nodes.container, minX, maxX, minY, maxY);

      this.nodes.container.onDrag = (function(x, y) {
        this.placeSizers();
      }).bind(this);

      this.nodes.container.onDragEnd = (function(x, y) {
        this.setSettingsProperty('x', x);
        this.setSettingsProperty('y', y);
        saveSettings();
      }).bind(this);
    }
  },

  initResize: function() {
    this.sizer = {};

    var sizerSize = this.args.sizerSize ? this.args.sizerSize : 5;

    if (!this.args.sizable) this.args.sizable = '';

    if (this.args.sizable.indexOf('v') > -1) {
      this.nodes.vSizer = document.createElement('div');
      this.nodes.vSizer.innerHTML = '&nbsp;';

      Element.setStyle(this.nodes.vSizer, { height: sizerSize + 'px', lineHeight: '1px', position: 'absolute', cursor: 's-resize' });
      Element.addClassName(this.nodes.vSizer, 'vSizer');

      this.nodes.container.appendChild(this.nodes.vSizer);

      Drag.init(this.nodes.vSizer, null, 0, 0, null, null);

      this.nodes.vSizer.onDragStart = (function(x, y) {
        this.sizer.startY = y;
        var div = this.args.scrollOn ? this.nodes.scrollContainer : this.nodes.body;
        this.sizer.startDimensions = Element.getDimensions(div);
      }).bind(this);

      this.nodes.vSizer.onDrag = (function(x, y) {
        this.redrawWindow(null, y, false);
      }).bind(this);

      this.nodes.vSizer.onDragEnd = (function(x, y) {
        this.redrawWindow(null, y, true);
        this.placeSizers();
      }).bind(this);
    }

    if (this.args.sizable.indexOf('h') > -1) {
      this.nodes.hSizer = document.createElement('div');

      Element.setStyle(this.nodes.hSizer, { width: sizerSize + 'px', lineHeight: '1px', position: 'absolute', cursor: 'e-resize' });
      Element.addClassName(this.nodes.hSizer, 'hSizer');

      this.nodes.container.appendChild(this.nodes.hSizer);

      Drag.init(this.nodes.hSizer, null, null, null, 0, 0);

      this.nodes.hSizer.onDragStart = (function(x, y) {
        this.sizer.startX = x;
        var div = this.args.scrollOn ? this.nodes.scrollContainer : this.nodes.body;
        this.sizer.startDimensions = Element.getDimensions(div);
      }).bind(this);

      this.nodes.hSizer.onDrag = (function(x, y) {
        this.redrawWindow(x, null, false);
      }).bind(this);

      this.nodes.hSizer.onDragEnd = (function(x, y) {
        this.redrawWindow(x, null, true);
        this.placeSizers();
      }).bind(this);
    }

    if (this.args.sizable.indexOf('h') > -1 && this.args.sizable.indexOf('v') > -1) {
      this.nodes.vhSizer = document.createElement('div');
      this.nodes.vhSizer.innerHTML = '&nbsp;';

      Element.setStyle(this.nodes.vhSizer, { width: sizerSize + 'px', height: sizerSize + 'px', lineHeight: '1px', position: 'absolute', cursor: 'se-resize' });
      Element.addClassName(this.nodes.vhSizer, 'vhSizer');

      this.nodes.container.appendChild(this.nodes.vhSizer);

      Drag.init(this.nodes.vhSizer);

      this.nodes.vhSizer.onDragStart = (function(x, y) {
        this.sizer.startX = x;
        this.sizer.startY = y;
        var div = this.args.scrollOn ? this.nodes.scrollContainer : this.nodes.body;
        this.sizer.startDimensions = Element.getDimensions(div);
      }).bind(this);

      this.nodes.vhSizer.onDrag = (function(x, y) {
        this.redrawWindow(x, y, false);
        //this.placeSizers();
      }).bind(this);

      this.nodes.vhSizer.onDragEnd = (function(x, y) {
        this.redrawWindow(x, y, true);
        this.placeSizers();
      }).bind(this);


    }

    this.placeSizers();
  },

  placeSizers: function() {
    var dimensions = Element.getDimensions(this.nodes.container);
    if (this.nodes.vSizer)  Element.setStyle(this.nodes.vSizer,  { top: dimensions.height - this.sizerSize + 'px', width: dimensions.width + 'px' });
    if (this.nodes.hSizer)  Element.setStyle(this.nodes.hSizer,  { left: dimensions.width - this.sizerSize + 'px', height: dimensions.height + 'px' });
    if (this.nodes.vhSizer) Element.setStyle(this.nodes.vhSizer, { left: dimensions.width - this.sizerSize + 'px', top: dimensions.height - this.sizerSize + 'px'});

  },

  redrawWindow: function(x, y, save) {
    var diff;
    var width;

    var divToResize = this.args.scrollOn ? this.nodes.scrollContainer : this.nodes.body;
    var direction = '';

    if (y != null) {
      diff = y - this.sizer.startY;
      if (diff != 0) direction = 'v';

      var height = this.sizer.startDimensions.height + diff;
      if (height < 0) height = 0;
      Element.setStyle(divToResize, { height: height + 'px' });

      if (save) {
        this.setSettingsProperty('height', height);
      }
    }
    if (x != null) {
      diff = x - this.sizer.startX;
      if (diff != 0) direction += 'h';
      width = this.sizer.startDimensions.width + diff;
      if (width < 0) width = 0;
      Element.setStyle(divToResize, { width: width + 'px' });

      if (save) {
        this.setSettingsProperty('width', width);
      }
    }

    var bPos = Element.viewportOffset(divToResize);
    var tPos = Element.viewportOffset(this.nodes.title);
    diff = bPos.left - tPos.left;
    width = Element.getWidth(divToResize);
    Element.setStyle(this.nodes.title, { width: width + diff + 'px' });

    if (save) saveSettings();

    if (!!this.scroller) this.scroller.redraw(); // TODO: This should be done in scoller code instead
    this.placeSizers();
    this.onResize(this, direction);
  },

  fitToContent: function(width, height) {
    var divToResize = this.args.scrollOn ? this.nodes.scrollContainer : this.nodes.body;

    if (height) {
      var hTmp = Element.getStyle(this.nodes.body, 'height');
      Element.setStyle(this.nodes.body, { height: 'auto' });
    }
    if (width) {
      var wTmp = Element.getStyle(this.nodes.body, 'width');
      Element.setStyle(this.nodes.body, { width: 'auto' });
    }

    var dimensions = Element.getDimensions(this.nodes.body);

    if (width) {
      Element.setStyle(this.nodes.body, { width: wTmp });
      Element.setStyle(divToResize, { width:  dimensions.width  + 'px' });
    }
    if (height) {
      Element.setStyle(this.nodes.body, { height: hTmp });
      Element.setStyle(divToResize, { height: dimensions.height + 'px' });
    }
    if (width || height) this.redrawWindow(null, null, false);
    //if (!!this.scroller) this.scroller.redraw();
  },

  initScroll: function() {
    if (this.args.scrollOn) {
      this.scroller = new pb.scroll(this.nodes.body, { direction: this.args.scroll });
      this.nodes.scrollContainer = this.nodes.body.parentNode.parentNode;
    }
  },

  close: function() {
    document.body.removeChild(this.nodes.container);

    this.onClose(this);
  },

  minimize: function() {
    //Element.addClassName(this.nodes.container, 'hidden');
    this.toggleState('m', 'minimize');
    this.onMinimize(this);
  },

  reload: function() {
    this.setContent(this.content);
  }


});
