/* Copyright (c) 2005 Tim Taylor (see LICENSE.txt) */

/* FIXME: assumes position styles are specified in 'px' */

ToolMan._coordinatesFactory = {

  create : function(x, y) {
    // FIXME: Safari won't parse 'throw' and aborts trying to do anything with this file
    //if (isNaN(x) || isNaN(y)) throw "invalid x,y: " + x + "," + y
    return new _ToolManCoordinate(this, x, y)
  },

  origin : function() {
    return this.create(0, 0)
  },

  /*
   * FIXME: Safari 1.2, returns (0,0) on absolutely positioned elements
   */
  topLeftPosition : function(element) {
    var left = parseInt(ToolMan.css().readStyle(element, "left"))
    var left = isNaN(left) ? 0 : left
    var top = parseInt(ToolMan.css().readStyle(element, "top"))
    var top = isNaN(top) ? 0 : top

    return this.create(left, top)
  },

  bottomRightPosition : function(element) {
    return this.topLeftPosition(element).plus(this._size(element))
  },

  topLeftOffset : function(element) {
    var offset = this._offset(element)

    var parent = element.offsetParent
    while (parent) {
      offset = offset.plus(this._offset(parent))
      parent = parent.offsetParent
    }
    return offset
  },

  bottomRightOffset : function(element) {
    return this.topLeftOffset(element).plus(
        this.create(element.offsetWidth, element.offsetHeight))
  },

  scrollOffset : function() {
    if (window.pageXOffset) {
      return this.create(window.pageXOffset, window.pageYOffset)
    } else if (document.documentElement) {
      return this.create(
          document.body.scrollLeft + document.documentElement.scrollLeft,
          document.body.scrollTop + document.documentElement.scrollTop)
    } else if (document.body.scrollLeft >= 0) {
      return this.create(document.body.scrollLeft, document.body.scrollTop)
    } else {
      return this.create(0, 0)
    }
  },

  clientSize : function() {
    if (window.innerHeight >= 0) {
      return this.create(window.innerWidth, window.innerHeight)
    } else if (document.documentElement) {
      return this.create(document.documentElement.clientWidth,
          document.documentElement.clientHeight)
    } else if (document.body.clientHeight >= 0) {
      return this.create(document.body.clientWidth,
          document.body.clientHeight)
    } else {
      return this.create(0, 0)
    }
  },

  /**
   * mouse coordinate relative to the window (technically the
   * browser client area) i.e. the part showing your page
   *
   * NOTE: in Safari the coordinate is relative to the document
   */
  mousePosition : function(event) {
    event = ToolMan.events().fix(event)
    return this.create(event.clientX, event.clientY)
  },

  /**
   * mouse coordinate relative to the document
   */
  mouseOffset : function(event) {
    event = ToolMan.events().fix(event)
    if (event.pageX >= 0 || event.pageX < 0) {
      return this.create(event.pageX, event.pageY)
    } else if (event.clientX >= 0 || event.clientX < 0) {
      return this.mousePosition(event).plus(this.scrollOffset())
    }
  },

  _size : function(element) {
  /* TODO: move to a Dimension class */
    return this.create(element.offsetWidth, element.offsetHeight)
  },

  _offset : function(element) {
    return this.create(element.offsetLeft, element.offsetTop)
  }
}

function _ToolManCoordinate(factory, x, y) {
  this.factory = factory
  this.x = isNaN(x) ? 0 : x
  this.y = isNaN(y) ? 0 : y
}

_ToolManCoordinate.prototype = {
  toString : function() {
    return "(" + this.x + "," + this.y + ")"
  },

  plus : function(that) {
    return this.factory.create(this.x + that.x, this.y + that.y)
  },

  minus : function(that) {
    return this.factory.create(this.x - that.x, this.y - that.y)
  },

  min : function(that) {
    return this.factory.create(
        Math.min(this.x , that.x), Math.min(this.y , that.y))
  },

  max : function(that) {
    return this.factory.create(
        Math.max(this.x , that.x), Math.max(this.y , that.y))
  },

  constrainTo : function (one, two) {
    var min = one.min(two)
    var max = one.max(two)

    return this.max(min).min(max)
  },

  distance : function (that) {
    return Math.sqrt(Math.pow(this.x - that.x, 2) + Math.pow(this.y - that.y, 2))
  },

  reposition : function(element) {
    element.style["top"] = this.y + "px"
    element.style["left"] = this.x + "px"
  }
}
