/**
 * Algorithm for reorder graph.
 *
 */
function GraphReorder(graph, app)
{
    BaseAlgorithm.apply(this, arguments);
    if (graph != null)
    {
        this.edges = graph.edges;
        this.vertices = graph.vertices;
    }
}


// inheritance.
GraphReorder.prototype = Object.create(BaseAlgorithm.prototype);


GraphReorder.prototype.getName = function(local)
{
    return g_GraphReorder; //local == "ru" ? "Упорядочить граф" : "Arrange the graph";
}

GraphReorder.prototype.getId = function()
{
    return "OlegSh.GraphReorder";
}

// @return message for user.
GraphReorder.prototype.getMessage = function(local)
{
    return g_done;
}

GraphReorder.prototype.result = function(resultCallback)
{
    var result = {};
    result["version"] = 1;
    
    if (this.vertices.length == 0)
    {
        return result;
    }
    
    var velocityDamping    = 0.85;
    var diameter = (new VertexModel()).diameter;
    var maxDistance = diameter * 3;
    var gravityDistanceSqr =  10  * (maxDistance * maxDistance);
    var edgeGravityKof     =  10  / (maxDistance);
    var kCenterForce       =  10  / (maxDistance * 10);
    //var centerPoint = viewportSize.multiply(0.5);
    var velocityMax = maxDistance * 10;
    
    var centerPoint = new Point();
    for(i = 0; i < this.vertices.length; i++) // loop through vertices
    {
        centerPoint.add(this.vertices[i].position);
    }
    centerPoint.multiply(1.0 / this.vertices.length);

    var edgesMatrix = {};   
    for (var i = 0; i < this.edges.length; i++)
    {  
        edgesMatrix[this.edges[i].vertex1.id + this.edges[i].vertex2.id * 1000] = 1;
        edgesMatrix[this.edges[i].vertex2.id + this.edges[i].vertex1.id * 1000] = 1;
    }

    var k = 0;
    var bChanged = true;
    while (k < 1000 && bChanged)
    {
      var vertexData = [];
      for(i = 0; i < this.vertices.length; i++) // loop through vertices
      {
         // Has no in newVertices.
         var currentVertex = {};
         currentVertex.object    = this.vertices[i];
         currentVertex.net_force = new Point (0, 0);
         currentVertex.velocity   = new Point (0, 0);
         vertexData.push(currentVertex);

         for(j = 0; j < this.vertices.length; j++) // loop through other vertices
         {
            otherVertex = this.vertices[j];

            if (otherVertex == currentVertex.object) continue;

            // squared distance between "u" and "v" in 2D space
            var rsq = currentVertex.object.position.distanceSqr(otherVertex.position);

            {
              // counting the repulsion between two vertices
              var force = (currentVertex.object.position.subtract(otherVertex.position)).normalize(gravityDistanceSqr / rsq);
              currentVertex.net_force = currentVertex.net_force.add(force);
            }
         }

         for(j = 0; j < this.vertices.length; j++) // loop through edges
         {
            otherVertex = this.vertices[j];
            if (edgesMatrix.hasOwnProperty(currentVertex.object.id + 1000 * otherVertex.id))
            {
                var distance = currentVertex.object.position.distance(otherVertex.position);

                if (distance > maxDistance)
                {
                    // countin the attraction
                    var force = (otherVertex.position.subtract(currentVertex.object.position)).normalize(edgeGravityKof * (distance - maxDistance));
                    currentVertex.net_force = currentVertex.net_force.add(force);
                }
            }
         }

         // Calculate force to center of world.
         var distanceToCenter = centerPoint.distance(currentVertex.object.position);
         var force = centerPoint.subtract(currentVertex.object.position).normalize(distanceToCenter * kCenterForce);
         currentVertex.net_force = currentVertex.net_force.add(force);

         // counting the velocity (with damping 0.85)
         currentVertex.velocity = currentVertex.velocity.add(currentVertex.net_force);
     }

     bChanged = false;

     for(i = 0; i < vertexData.length; i++) // set new positions
     {
        var v = vertexData[i];
        var velocity = v.velocity;
        if (velocity.length() > velocityMax)
        {
            velocity = velocity.normalize(velocityMax);
        }
        v.object.position = v.object.position.add(velocity);
        if (velocity.length() >= 1)
        {
          bChanged = true;
        }
     }
     k++;
    }


    this.app.OnAutoAdjustViewport();
    this.app.SetDefaultHandler();
    // Looks like somthing going wrong and will use circle algorithm for reposition.
    //var bbox = this.getGraphBBox();

    return result;
}

GraphReorder.prototype.getObjectSelectedGroup = function(object)
{
    return 0;
}

GraphReorder.prototype.getPriority = function()
{
    return -8.5;
}

// Algorithm support multi graph
GraphReorder.prototype.IsSupportMultiGraph = function()
{
    return true;
}

GraphReorder.prototype.IsSupportNegativeWeight = function()
{
    return true;
}

// Factory for connected components.
function CreateAlgorithmGraphReorder(graph, app)
{
    return new GraphReorder(graph, app)
}

// Gerister connected component.
RegisterAlgorithm (CreateAlgorithmGraphReorder);
