var slice = [].slice;

Poltergeist.Node = (function() {

var fn, j, len, name, ref;

Node.DELEGATES = ['allText', 'visibleText', 'getAttribute', 'value', 'set', 'setAttribute', 'isObsolete', 'removeAttribute', 'isMultiple', 'select', 'tagName', 'find', 'getAttributes', 'isVisible', 'isInViewport', 'position', 'trigger', 'parentId', 'parentIds', 'mouseEventTest', 'scrollIntoView', 'isDOMEqual', 'isDisabled', 'deleteText', 'containsSelection', 'path', 'getProperty'];

function Node(page, id) {
  this.page = page;
  this.id = id;
}

Node.prototype.parent = function() {
  return new Poltergeist.Node(this.page, this.parentId());
};

ref = Node.DELEGATES;
fn = function(name) {
  return Node.prototype[name] = function() {
    var args;
    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
    return this.page.nodeCall(this.id, name, args);
  };
};
for (j = 0, len = ref.length; j < len; j++) {
  name = ref[j];
  fn(name);
}

Node.prototype.mouseEventPosition = function(offset) {
  var area_offset, image, middle, pos, viewport;
  if (offset == null) {
    offset = {};
  }
  viewport = this.page.viewportSize();
  if (image = this._getAreaImage()) {
    pos = image.position();
    if (area_offset = this._getAreaOffsetRect()) {
      pos.left = pos.left + area_offset.x;
      pos.right = pos.left + area_offset.width;
      pos.top = pos.top + area_offset.y;
      pos.bottom = pos.top + area_offset.height;
    }
  } else {
    pos = this.position();
  }
  middle = function(start, end, size) {
    return start + ((Math.min(end, size) - start) / 2);
  };
  if ((offset['x'] != null) && (offset['y'] != null)) {
    return {
      x: pos.left + offset['x'],
      y: pos.top + offset['y']
    };
  } else {
    return {
      x: middle(pos.left, pos.right, viewport.width),
      y: middle(pos.top, pos.bottom, viewport.height)
    };
  }
};

Node.prototype.mouseEvent = function(name, keys, offset) {
  var area_image, modifier_keys, modifiers_code, pos, scroll_pos, test;
  if (area_image = this._getAreaImage()) {
    area_image.scrollIntoView();
  } else {
    this.scrollIntoView();
  }
  pos = this.mouseEventPosition(offset);
  test = this.mouseEventTest(pos.x, pos.y);
  if (test.status === 'success') {
    modifier_keys = (keys || []).join(',').replace('control', 'ctrl');
    modifiers_code = this.page.keyModifierCode(modifier_keys);
    if (name === 'rightclick') {
      this.page.mouseEvent('click', pos.x, pos.y, 'right', modifiers_code);
      if (phantom.version.major === 2 && phantom.version.minor >= 1) {
        this.page.sendEvent('contextmenu', pos.x, pos.y, 'right', modifiers_code);
      } else {
        scroll_pos = this.page.scrollPosition();
        this.trigger('contextmenu', {
          screenX: pos.x,
          screenY: pos.y,
          clientX: pos.x + scroll_pos['left'],
          clientY: pos.y + scroll_pos['top'],
          ctrlKey: modifier_keys.indexOf('ctrl') !== -1,
          altKey: modifier_keys.indexOf('alt') !== -1,
          metaKey: modifier_keys.indexOf('meta') !== -1,
          shiftKey: modifier_keys.indexOf('shift') !== -1,
          button: 2
        });
      }
    } else {
      this.page.mouseEvent(name, pos.x, pos.y, 'left', modifiers_code);
    }
    return pos;
  } else {
    throw new Poltergeist.MouseEventFailed(name, test.selector, pos);
  }
};

Node.prototype.dragTo = function(other) {
  var otherPosition, position;
  this.scrollIntoView();
  position = this.mouseEventPosition();
  otherPosition = other.mouseEventPosition();
  this.page.mouseEvent('mousedown', position.x, position.y);
  return this.page.mouseEvent('mouseup', otherPosition.x, otherPosition.y);
};

Node.prototype.dragBy = function(x, y) {
  var final_pos, position;
  this.scrollIntoView();
  position = this.mouseEventPosition();
  final_pos = {
    x: position.x + x,
    y: position.y + y
  };
  this.page.mouseEvent('mousedown', position.x, position.y);
  return this.page.mouseEvent('mouseup', final_pos.x, final_pos.y);
};

Node.prototype.isEqual = function(other) {
  return this.page === other.page && this.isDOMEqual(other.id);
};

Node.prototype._getAreaOffsetRect = function() {
  var centerX, centerY, coord, coords, i, maxX, maxY, minX, minY, radius, rect, shape, x, xs, y, ys;
  shape = this.getAttribute('shape').toLowerCase();
  coords = (function() {
    var k, len1, ref1, results;
    ref1 = this.getAttribute('coords').split(',');
    results = [];
    for (k = 0, len1 = ref1.length; k < len1; k++) {
      coord = ref1[k];
      results.push(parseInt(coord, 10));
    }
    return results;
  }).call(this);
  return rect = (function() {
    switch (shape) {
      case 'rect':
      case 'rectangle':
        x = coords[0], y = coords[1];
        return {
          x: x,
          y: y,
          width: coords[2] - x,
          height: coords[3] - y
        };
      case 'circ':
      case 'circle':
        centerX = coords[0], centerY = coords[1], radius = coords[2];
        return {
          x: centerX - radius,
          y: centerY - radius,
          width: 2 * radius,
          height: 2 * radius
        };
      case 'poly':
      case 'polygon':
        xs = (function() {
          var k, ref1, results;
          results = [];
          for (i = k = 0, ref1 = coords.length; k < ref1; i = k += 2) {
            results.push(coords[i]);
          }
          return results;
        })();
        ys = (function() {
          var k, ref1, results;
          results = [];
          for (i = k = 1, ref1 = coords.length; k < ref1; i = k += 2) {
            results.push(coords[i]);
          }
          return results;
        })();
        minX = Math.min.apply(Math, xs);
        maxX = Math.max.apply(Math, xs);
        minY = Math.min.apply(Math, ys);
        maxY = Math.max.apply(Math, ys);
        return {
          x: minX,
          y: minY,
          width: maxX - minX,
          height: maxY - minY
        };
    }
  })();
};

Node.prototype._getAreaImage = function() {
  var image_node_id, map, mapName;
  if ('area' === this.tagName().toLowerCase()) {
    map = this.parent();
    if (map.tagName().toLowerCase() !== 'map') {
      throw new Error('the area is not within a map');
    }
    mapName = map.getAttribute('name');
    if (mapName == null) {
      throw new Error("area's parent map must have a name");
    }
    mapName = '#' + mapName.toLowerCase();
    image_node_id = this.page.find('css', "img[usemap='" + mapName + "']")[0];
    if (image_node_id == null) {
      throw new Error("no image matches the map");
    }
    return this.page.get(image_node_id);
  }
};

return Node;

})();