/*jshint globalstrict:false, strict:false */
/*global assertEqual */

////////////////////////////////////////////////////////////////////////////////
/// @brief test the statement class
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

var jsunity = require("jsunity");

var arangodb = require("org/arangodb");
var ArangoStatement = require("org/arangodb/arango-statement").ArangoStatement;
var db = arangodb.db;
var ERRORS = arangodb.errors;

// -----------------------------------------------------------------------------
// --SECTION--                                                     explain tests
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////

function ExplainSuite () {
  'use strict';
  var cn = "UnitTestsExplain";

  return {

////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////

    setUp : function () {
      db._drop(cn);
      db._create(cn);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////

    tearDown : function () {
      db._drop(cn);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////

    testExplainError : function () {
      var st = new ArangoStatement(db, { query : "for u in" });
      try {
        st.explain();
      }
      catch (e) {
        assertEqual(ERRORS.ERROR_QUERY_PARSE.code, e.errorNum);
      }
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////

    testExplainNoBindError : function () {
      var st = new ArangoStatement(db, { query : "for i in [ 1 ] return @f" });
      try {
        st.explain();
      }
      catch (e) {
        assertEqual(ERRORS.ERROR_QUERY_BIND_PARAMETER_MISSING.code, e.errorNum);
      }
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////

    testExplainWithBind : function () {
      var st = new ArangoStatement(db, { query : "for i in [ 1 ] return @f", bindVars: { f : 99 } });
      var nodes = st.explain().plan.nodes, node;

      node = nodes[0];
      assertEqual("SingletonNode", node.type);

      node = nodes[1];
      assertEqual("CalculationNode", node.type);

      node = nodes[2];
      assertEqual("CalculationNode", node.type);

      node = nodes[3];
      assertEqual("EnumerateListNode", node.type);

      node = nodes[4];
      assertEqual("ReturnNode", node.type);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////

    testExplainWithBindCollection : function () {
      var st = new ArangoStatement(db, { query : "for i in @@cn return i", bindVars: { "@cn": cn } });
      var nodes = st.explain().plan.nodes, node;

      node = nodes[0];
      assertEqual("SingletonNode", node.type);

      node = nodes[1];
      assertEqual("EnumerateCollectionNode", node.type);
      assertEqual("i", node.outVariable.name);
      assertEqual(cn, node.collection);

      node = nodes[2];
      assertEqual("RemoteNode", node.type);

      node = nodes[3];
      assertEqual("GatherNode", node.type);

      node = nodes[4];
      assertEqual("ReturnNode", node.type);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////

    testExplainOk1 : function () {
      var st = new ArangoStatement(db, { query : "for u in [ 1, 2, 3 ] return u" });
      var nodes = st.explain().plan.nodes, node;

      node = nodes[0];
      assertEqual("SingletonNode", node.type);

      node = nodes[1];
      assertEqual("CalculationNode", node.type);

      node = nodes[2];
      assertEqual("EnumerateListNode", node.type);
      assertEqual("u", node.outVariable.name);

      node = nodes[3];
      assertEqual("ReturnNode", node.type);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////

    testExplainOk2 : function () {
      var st = new ArangoStatement(db, { query : "for u in [ 1, 2, 3 ] filter u != 1 for f in u return f" });
      var nodes = st.explain().plan.nodes, node;

      node = nodes[0];
      assertEqual("SingletonNode", node.type);

      node = nodes[1];
      assertEqual("CalculationNode", node.type);

      node = nodes[2];
      assertEqual("EnumerateListNode", node.type);

      node = nodes[3];
      assertEqual("CalculationNode", node.type);

      node = nodes[4];
      assertEqual("FilterNode", node.type);

      node = nodes[5];
      assertEqual("EnumerateListNode", node.type);

      node = nodes[6];
      assertEqual("ReturnNode", node.type);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////

    testExplainRemove1 : function () {
      var st = new ArangoStatement(db, { query : "for u in " + cn + " remove u in " + cn });
      var nodes = st.explain().plan.nodes, node;

      node = nodes[0];
      assertEqual("SingletonNode", node.type);

      node = nodes[1];
      assertEqual("EnumerateCollectionNode", node.type);
      assertEqual("u", node.outVariable.name);
      assertEqual(cn, node.collection);

      node = nodes[2];
      assertEqual("RemoveNode", node.type);
      assertEqual(cn, node.collection);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////

    testExplainRemove2 : function () {
      var st = new ArangoStatement(db, { query : "for u in @@cn remove u in @@cn", bindVars: { "@cn" : cn } });
      var nodes = st.explain().plan.nodes, node;

      node = nodes[0];
      assertEqual("SingletonNode", node.type);

      node = nodes[1];
      assertEqual("EnumerateCollectionNode", node.type);
      assertEqual("u", node.outVariable.name);
      assertEqual(cn, node.collection);

      node = nodes[2];
      assertEqual("RemoveNode", node.type);
      assertEqual(cn, node.collection);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////

    testExplainInsert1 : function () {
      var st = new ArangoStatement(db, { query : "for u in @@cn insert u in @@cn", bindVars: { "@cn": cn } });
      var nodes = st.explain().plan.nodes, node;

      node = nodes[0];
      assertEqual("SingletonNode", node.type);

      node = nodes[1];
      assertEqual("EnumerateCollectionNode", node.type);
      assertEqual("u", node.outVariable.name);
      assertEqual(cn, node.collection);

      node = nodes[2];
      assertEqual("RemoteNode", node.type);

      node = nodes[3];
      assertEqual("GatherNode", node.type);

      node = nodes[4];
      assertEqual("DistributeNode", node.type);

      node = nodes[5];
      assertEqual("RemoteNode", node.type);

      node = nodes[6];
      assertEqual("InsertNode", node.type);
      assertEqual(cn, node.collection);

      node = nodes[7];
      assertEqual("RemoteNode", node.type);

      node = nodes[8];
      assertEqual("GatherNode", node.type);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////

    testExplainInsert2 : function () {
      var st = new ArangoStatement(db, { query : "for u in " + cn + " insert u in " + cn });
      var nodes = st.explain().plan.nodes, node;

      node = nodes[0];
      assertEqual("SingletonNode", node.type);

      node = nodes[1];
      assertEqual("EnumerateCollectionNode", node.type);
      assertEqual("u", node.outVariable.name);
      assertEqual(cn, node.collection);

      node = nodes[2];
      assertEqual("RemoteNode", node.type);

      node = nodes[3];
      assertEqual("GatherNode", node.type);

      node = nodes[4];
      assertEqual("DistributeNode", node.type);

      node = nodes[5];
      assertEqual("RemoteNode", node.type);

      node = nodes[6];
      assertEqual("InsertNode", node.type);
      assertEqual(cn, node.collection);

      node = nodes[7];
      assertEqual("RemoteNode", node.type);

      node = nodes[8];
      assertEqual("GatherNode", node.type);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////

    testExplainUpdate1 : function () {
      var st = new ArangoStatement(db, {
        query : "for u in @@cn update u._key with u in @@cn",
        bindVars: { "@cn": cn }
      });
      var nodes = st.explain().plan.nodes, node;

      node = nodes[0];
      assertEqual("SingletonNode", node.type);

      node = nodes[1];
      assertEqual("EnumerateCollectionNode", node.type);
      assertEqual("u", node.outVariable.name);
      assertEqual(cn, node.collection);

      node = nodes[2];
      assertEqual("CalculationNode", node.type);

      node = nodes[3];
      assertEqual("RemoteNode", node.type);

      node = nodes[4];
      assertEqual("GatherNode", node.type);

      node = nodes[5];
      assertEqual("DistributeNode", node.type);

      node = nodes[6];
      assertEqual("RemoteNode", node.type);

      node = nodes[7];
      assertEqual("UpdateNode", node.type);
      assertEqual(cn, node.collection);

      node = nodes[8];
      assertEqual("RemoteNode", node.type);

      node = nodes[9];
      assertEqual("GatherNode", node.type);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////

    testExplainUpdate2 : function () {
      var st = new ArangoStatement(db, { query : "for u in " + cn + " update u._key with u in " + cn });
      var nodes = st.explain().plan.nodes, node;

      node = nodes[0];
      assertEqual("SingletonNode", node.type);

      node = nodes[1];
      assertEqual("EnumerateCollectionNode", node.type);
      assertEqual("u", node.outVariable.name);
      assertEqual(cn, node.collection);

      node = nodes[2];
      assertEqual("CalculationNode", node.type);

      node = nodes[3];
      assertEqual("RemoteNode", node.type);

      node = nodes[4];
      assertEqual("GatherNode", node.type);

      node = nodes[5];
      assertEqual("DistributeNode", node.type);

      node = nodes[6];
      assertEqual("RemoteNode", node.type);

      node = nodes[7];
      assertEqual("UpdateNode", node.type);
      assertEqual(cn, node.collection);

      node = nodes[8];
      assertEqual("RemoteNode", node.type);

      node = nodes[9];
      assertEqual("GatherNode", node.type);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////

    testExplainUpdate3 : function () {
      var st = new ArangoStatement(db, { query : "for u in @@cn update u in @@cn", bindVars: { "@cn": cn } });
      var nodes = st.explain().plan.nodes, node;

      node = nodes[0];
      assertEqual("SingletonNode", node.type);

      node = nodes[1];
      assertEqual("EnumerateCollectionNode", node.type);
      assertEqual("u", node.outVariable.name);
      assertEqual(cn, node.collection);

      node = nodes[2];
      assertEqual("RemoteNode", node.type);

      node = nodes[3];
      assertEqual("GatherNode", node.type);

      node = nodes[4];
      assertEqual("DistributeNode", node.type);

      node = nodes[5];
      assertEqual("RemoteNode", node.type);

      node = nodes[6];
      assertEqual("UpdateNode", node.type);
      assertEqual(cn, node.collection);

      node = nodes[7];
      assertEqual("RemoteNode", node.type);

      node = nodes[8];
      assertEqual("GatherNode", node.type);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////

    testExplainUpdate4 : function () {
      var st = new ArangoStatement(db, { query : "for u in " + cn + " update u in " + cn });
      var nodes = st.explain().plan.nodes, node;

      node = nodes[0];
      assertEqual("SingletonNode", node.type);

      node = nodes[1];
      assertEqual("EnumerateCollectionNode", node.type);
      assertEqual("u", node.outVariable.name);
      assertEqual(cn, node.collection);


      node = nodes[2];
      assertEqual("RemoteNode", node.type);

      node = nodes[3];
      assertEqual("GatherNode", node.type);

      node = nodes[4];
      assertEqual("DistributeNode", node.type);

      node = nodes[5];
      assertEqual("RemoteNode", node.type);

      node = nodes[6];
      assertEqual("UpdateNode", node.type);
      assertEqual(cn, node.collection);

      node = nodes[7];
      assertEqual("RemoteNode", node.type);

      node = nodes[8];
      assertEqual("GatherNode", node.type);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////

    testExplainReplace1 : function () {
      var st = new ArangoStatement(db, {
        query : "for u in @@cn replace u._key with u in @@cn",
        bindVars: { "@cn": cn }
      });
      var nodes = st.explain().plan.nodes, node;

      node = nodes[0];
      assertEqual("SingletonNode", node.type);

      node = nodes[1];
      assertEqual("EnumerateCollectionNode", node.type);
      assertEqual("u", node.outVariable.name);
      assertEqual(cn, node.collection);

      node = nodes[2];
      assertEqual("CalculationNode", node.type);

      node = nodes[3];
      assertEqual("RemoteNode", node.type);

      node = nodes[4];
      assertEqual("GatherNode", node.type);

      node = nodes[5];
      assertEqual("DistributeNode", node.type);

      node = nodes[6];
      assertEqual("RemoteNode", node.type);

      node = nodes[7];
      assertEqual("ReplaceNode", node.type);
      assertEqual(cn, node.collection);

      node = nodes[8];
      assertEqual("RemoteNode", node.type);

      node = nodes[9];
      assertEqual("GatherNode", node.type);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////

    testExplainReplace2 : function () {
      var st = new ArangoStatement(db, { query : "for u in " + cn + " replace u._key with u in " + cn });
      var nodes = st.explain().plan.nodes, node;

      node = nodes[0];
      assertEqual("SingletonNode", node.type);

      node = nodes[1];
      assertEqual("EnumerateCollectionNode", node.type);
      assertEqual("u", node.outVariable.name);
      assertEqual(cn, node.collection);

      node = nodes[2];
      assertEqual("CalculationNode", node.type);


      node = nodes[3];
      assertEqual("RemoteNode", node.type);

      node = nodes[4];
      assertEqual("GatherNode", node.type);

      node = nodes[5];
      assertEqual("DistributeNode", node.type);

      node = nodes[6];
      assertEqual("RemoteNode", node.type);

      node = nodes[7];
      assertEqual("ReplaceNode", node.type);
      assertEqual(cn, node.collection);

      node = nodes[8];
      assertEqual("RemoteNode", node.type);

      node = nodes[9];
      assertEqual("GatherNode", node.type);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////

    testExplainReplace3 : function () {
      var st = new ArangoStatement(db, { query : "for u in @@cn replace u in @@cn", bindVars: { "@cn": cn } });
      var nodes = st.explain().plan.nodes, node;

      node = nodes[0];
      assertEqual("SingletonNode", node.type);

      node = nodes[1];
      assertEqual("EnumerateCollectionNode", node.type);
      assertEqual("u", node.outVariable.name);
      assertEqual(cn, node.collection);

      node = nodes[2];
      assertEqual("RemoteNode", node.type);

      node = nodes[3];
      assertEqual("GatherNode", node.type);

      node = nodes[4];
      assertEqual("DistributeNode", node.type);

      node = nodes[5];
      assertEqual("RemoteNode", node.type);

      node = nodes[6];
      assertEqual("ReplaceNode", node.type);
      assertEqual(cn, node.collection);

      node = nodes[7];
      assertEqual("RemoteNode", node.type);

      node = nodes[8];
      assertEqual("GatherNode", node.type);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////

    testExplainReplace4 : function () {
      var st = new ArangoStatement(db, { query : "for u in " + cn + " replace u in " + cn });
      var nodes = st.explain().plan.nodes, node;

      node = nodes[0];
      assertEqual("SingletonNode", node.type);

      node = nodes[1];
      assertEqual("EnumerateCollectionNode", node.type);
      assertEqual("u", node.outVariable.name);
      assertEqual(cn, node.collection);

      node = nodes[2];
      assertEqual("RemoteNode", node.type);

      node = nodes[3];
      assertEqual("GatherNode", node.type);

      node = nodes[4];
      assertEqual("DistributeNode", node.type);

      node = nodes[5];
      assertEqual("RemoteNode", node.type);

      node = nodes[6];
      assertEqual("ReplaceNode", node.type);
      assertEqual(cn, node.collection);

      node = nodes[7];
      assertEqual("RemoteNode", node.type);

      node = nodes[8];
      assertEqual("GatherNode", node.type);
    }

  };
}

// -----------------------------------------------------------------------------
// --SECTION--                                                              main
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////

jsunity.run(ExplainSuite);
return jsunity.done();

// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:
