text: var LGraphObject = require('./LGraphObject');
  var Integer = require('./util/Integer');
  var RectangleD = require('./util/RectangleD');
  var LayoutConstants = require('./LayoutConstants');
  var RandomSeed = require('./util/RandomSeed');
  var PointD = require('./util/PointD');
  
  function LNode(gm, loc, size, vNode) {
    //Alternative constructor 1 : LNode(LGraphManager gm, Point loc, Dimension size, Object vNode)
    if (size == null && vNode == null) {
      vNode = loc;
    }
  
    LGraphObject.call(this, vNode);
  
    //Alternative constructor 2 : LNode(Layout layout, Object vNode)
    if (gm.graphManager != null)
      gm = gm.graphManager;
  
    this.estimatedSize = Integer.MIN_VALUE;
    this.inclusionTreeDepth = Integer.MAX_VALUE;
    this.vGraphObject = vNode;
    this.edges = [];
    this.graphManager = gm;
  
    if (size != null && loc != null)
      this.rect = new RectangleD(loc.x, loc.y, size.width, size.height);
    else
      this.rect = new RectangleD();
  }
  
  LNode.prototype = Object.create(LGraphObject.prototype);
  for (var prop in LGraphObject) {
    LNode[prop] = LGraphObject[prop];
  }
  
  LNode.prototype.getEdges = function ()
  {
    return this.edges;
  };
  
  LNode.prototype.getChild = function ()
  {
    return this.child;
  };
  
  LNode.prototype.getOwner = function ()
  {
  //  if (this.owner != null) {
  //    if (!(this.owner == null || this.owner.getNodes().indexOf(this) > -1)) {
  //      throw "assert failed";
  //    }
  //  }
  
    return this.owner;
  };
  
  LNode.prototype.getWidth = function ()
  {
    return this.rect.width;
  };
  
  LNode.prototype.setWidth = function (width)
  {
    this.rect.width = width;
  };
  
  LNode.prototype.getHeight = function ()
  {
    return this.rect.height;
  };
  
  LNode.prototype.setHeight = function (height)
  {
    this.rect.height = height;
  };
  
  LNode.prototype.getCenterX = function ()
  {
    return this.rect.x + this.rect.width / 2;
  };
  
  LNode.prototype.getCenterY = function ()
  {
    return this.rect.y + this.rect.height / 2;
  };
  
  LNode.prototype.getCenter = function ()
  {
    return new PointD(this.rect.x + this.rect.width / 2,
            this.rect.y + this.rect.height / 2);
  };
  
  LNode.prototype.getLocation = function ()
  {
    return new PointD(this.rect.x, this.rect.y);
  };
  
  LNode.prototype.getRect = function ()
  {
    return this.rect;
  };
  
  LNode.prototype.getDiagonal = function ()
  {
    return Math.sqrt(this.rect.width * this.rect.width +
            this.rect.height * this.rect.height);
  };
  
  /**
   * This method returns half the diagonal length of this node.
   */
  LNode.prototype.getHalfTheDiagonal = function () {
    return Math.sqrt(this.rect.height * this.rect.height +
        this.rect.width * this.rect.width) / 2;
  };
  
  LNode.prototype.setRect = function (upperLeft, dimension)
  {
    this.rect.x = upperLeft.x;
    this.rect.y = upperLeft.y;
    this.rect.width = dimension.width;
    this.rect.height = dimension.height;
  };
  
  LNode.prototype.setCenter = function (cx, cy)
  {
    this.rect.x = cx - this.rect.width / 2;
    this.rect.y = cy - this.rect.height / 2;
  };
  
  LNode.prototype.setLocation = function (x, y)
  {
    this.rect.x = x;
    this.rect.y = y;
  };
  
  LNode.prototype.moveBy = function (dx, dy)
  {
    this.rect.x += dx;
    this.rect.y += dy;
  };
  
  LNode.prototype.getEdgeListToNode = function (to)
  {
    var edgeList = [];
    var edge;
    var self = this;
  
    self.edges.forEach(function(edge) {
      
      if (edge.target == to)
      {
        if (edge.source != self)
          throw "Incorrect edge source!";
  
        edgeList.push(edge);
      }
    });
  
    return edgeList;
  };
  
  LNode.prototype.getEdgesBetween = function (other)
  {
    var edgeList = [];
    var edge;
    
    var self = this;
    self.edges.forEach(function(edge) {
  
      if (!(edge.source == self || edge.target == self))
        throw "Incorrect edge source and/or target";
  
      if ((edge.target == other) || (edge.source == other))
      {
        edgeList.push(edge);
      }
    });
  
    return edgeList;
  };
  
  LNode.prototype.getNeighborsList = function ()
  {
    var neighbors = new Set();
    
    var self = this;
    self.edges.forEach(function(edge) {
  
      if (edge.source == self)
      {
        neighbors.add(edge.target);
      }
      else
      {
        if (edge.target != self) {
          throw "Incorrect incidency!";
        }
      
        neighbors.add(edge.source);
      }
    });
  
    return neighbors;
  };
  
  LNode.prototype.withChildren = function ()
  {
    var withNeighborsList = new Set();
    var childNode;
    var children;
  
    withNeighborsList.add(this);
  
    if (this.child != null)
    {
      var nodes = this.child.getNodes();
      for (var i = 0; i < nodes.length; i++)
      {
        childNode = nodes[i];
        children = childNode.withChildren();
        children.forEach(function(node) {
          withNeighborsList.add(node);
        });
      }
    }
  
    return withNeighborsList;
  };
  
  LNode.prototype.getNoOfChildren = function ()
  {
    var noOfChildren = 0;
    var childNode;
  
    if(this.child == null){
      noOfChildren = 1;
    }
    else
    {
      var nodes = this.child.getNodes();
      for (var i = 0; i < nodes.length; i++)
      {
        childNode = nodes[i];
  
        noOfChildren += childNode.getNoOfChildren();
      }
    }
    
    if(noOfChildren == 0){
      noOfChildren = 1;
    }
    return noOfChildren;
  };
  
  LNode.prototype.getEstimatedSize = function () {
    if (this.estimatedSize == Integer.MIN_VALUE) {
      throw "assert failed";
    }
    return this.estimatedSize;
  };
  
  LNode.prototype.calcEstimatedSize = function () {
    if (this.child == null)
    {
      return this.estimatedSize = (this.rect.width + this.rect.height) / 2;
    }
    else
    {
      this.estimatedSize = this.child.calcEstimatedSize();
      this.rect.width = this.estimatedSize;
      this.rect.height = this.estimatedSize;
  
      return this.estimatedSize;
    }
  };
  
  LNode.prototype.scatter = function () {
    var randomCenterX;
    var randomCenterY;
  
    var minX = -LayoutConstants.INITIAL_WORLD_BOUNDARY;
    var maxX = LayoutConstants.INITIAL_WORLD_BOUNDARY;
    randomCenterX = LayoutConstants.WORLD_CENTER_X +
            (RandomSeed.nextDouble() * (maxX - minX)) + minX;
  
    var minY = -LayoutConstants.INITIAL_WORLD_BOUNDARY;
    var maxY = LayoutConstants.INITIAL_WORLD_BOUNDARY;
    randomCenterY = LayoutConstants.WORLD_CENTER_Y +
            (RandomSeed.nextDouble() * (maxY - minY)) + minY;
  
    this.rect.x = randomCenterX;
    this.rect.y = randomCenterY
  };
  
  LNode.prototype.updateBounds = function () {
    if (this.getChild() == null) {
      throw "assert failed";
    }
    if (this.getChild().getNodes().length != 0)
    {
      // wrap the children nodes by re-arranging the boundaries
      var childGraph = this.getChild();
      childGraph.updateBounds(true);
  
      this.rect.x = childGraph.getLeft();
      this.rect.y = childGraph.getTop();
  
      this.setWidth(childGraph.getRight() - childGraph.getLeft());
      this.setHeight(childGraph.getBottom() - childGraph.getTop());
      
      // Update compound bounds considering its label properties    
      if(LayoutConstants.NODE_DIMENSIONS_INCLUDE_LABELS){
          
        var width = childGraph.getRight() - childGraph.getLeft();
        var height = childGraph.getBottom() - childGraph.getTop();
  
        if(this.labelWidth){
          if(this.labelPosHorizontal == "left"){
            this.rect.x -= (this.labelWidth);
            this.setWidth(width + this.labelWidth);
          }
          else if(this.labelPosHorizontal == "center" && this.labelWidth > width){
            this.rect.x -= (this.labelWidth - width) / 2;
            this.setWidth(this.labelWidth);
          }
          else if(this.labelPosHorizontal == "right"){
            this.setWidth(width + this.labelWidth);
          }        
        }
  
        if(this.labelHeight){
          if(this.labelPosVertical == "top"){
            this.rect.y -= (this.labelHeight);
            this.setHeight(height + this.labelHeight);
          }
          else if(this.labelPosVertical == "center" && this.labelHeight > height){
            this.rect.y -= (this.labelHeight - height) / 2;
            this.setHeight(this.labelHeight);
          }
          else if(this.labelPosVertical == "bottom"){
            this.setHeight(height + this.labelHeight);
          }
        }
      }
    }
  };
  
  LNode.prototype.getInclusionTreeDepth = function ()
  {
    if (this.inclusionTreeDepth == Integer.MAX_VALUE) {
      throw "assert failed";
    }
    return this.inclusionTreeDepth;
  };
  
  LNode.prototype.transform = function (trans)
  {
    var left = this.rect.x;
  
    if (left > LayoutConstants.WORLD_BOUNDARY)
    {
      left = LayoutConstants.WORLD_BOUNDARY;
    }
    else if (left < -LayoutConstants.WORLD_BOUNDARY)
    {
      left = -LayoutConstants.WORLD_BOUNDARY;
    }
  
    var top = this.rect.y;
  
    if (top > LayoutConstants.WORLD_BOUNDARY)
    {
      top = LayoutConstants.WORLD_BOUNDARY;
    }
    else if (top < -LayoutConstants.WORLD_BOUNDARY)
    {
      top = -LayoutConstants.WORLD_BOUNDARY;
    }
  
    var leftTop = new PointD(left, top);
    var vLeftTop = trans.inverseTransformPoint(leftTop);
  
    this.setLocation(vLeftTop.x, vLeftTop.y);
  };
  
  LNode.prototype.getLeft = function ()
  {
    return this.rect.x;
  };
  
  LNode.prototype.getRight = function ()
  {
    return this.rect.x + this.rect.width;
  };
  
  LNode.prototype.getTop = function ()
  {
    return this.rect.y;
  };
  
  LNode.prototype.getBottom = function ()
  {
    return this.rect.y + this.rect.height;
  };
  
  LNode.prototype.getParent = function ()
  {
    if (this.owner == null)
    {
      return null;
    }
  
    return this.owner.getParent();
  };
  
  module.exports = LNode;
  