//
// More tests for N-dimensional polygon querying
//

// Create a polygon of some shape (no holes)
// using turtle graphics.  Basically, will look like a very contorted octopus (quad-pus?) shape.
// There are no holes, but some edges will probably touch.

var numTests = 4;

for ( var test = 0; test < numTests; test++ ) {

    Random.srand( 1337 + test );

    var numTurtles = 4;
    var gridSize = [ 20, 20 ];
    var turtleSteps = 500;
    var bounds = [ Random.rand() * -1000000 + 0.00001, Random.rand() * 1000000 + 0.00001 ];
    var rotation = Math.PI * Random.rand();
    var bits = Math.floor( Random.rand() * 32 );

    printjson( { test : test, rotation : rotation, bits : bits });

    var rotatePoint = function( x, y ) {

        if( y == undefined ){
            y = x[1];
            x = x[0];
        }

        xp = x * Math.cos( rotation ) - y * Math.sin( rotation );
        yp = y * Math.cos( rotation ) + x * Math.sin( rotation );

        var scaleX = (bounds[1] - bounds[0]) / 360;
        var scaleY = (bounds[1] - bounds[0]) / 360;

        x *= scaleX;
        y *= scaleY;

        return [xp, yp];
    };

    var grid = [];
    for ( var i = 0; i < gridSize[0]; i++ ) {
        grid.push( new Array( gridSize[1] ) );
    }

    grid.toString = function() {

        var gridStr = "";
        for ( var j = grid[0].length - 1; j >= -1; j-- ) {
            for ( var i = 0; i < grid.length; i++ ) {
                if ( i == 0 )
                    gridStr += ( j == -1 ? " " : ( j % 10) ) + ": ";
                if ( j != -1 )
                    gridStr += "[" + ( grid[i][j] != undefined ? grid[i][j] : " " ) + "]";
                else
                    gridStr += " " + ( i % 10 ) + " ";
            }
            gridStr += "\n";
        }

        return gridStr;
    };

    var turtles = [];
    for ( var i = 0; i < numTurtles; i++ ) {

        var up = ( i % 2 == 0 ) ? i - 1 : 0;
        var left = ( i % 2 == 1 ) ? ( i - 1 ) - 1 : 0;

        turtles[i] = [
                [ Math.floor( gridSize[0] / 2 ), Math.floor( gridSize[1] / 2 ) ],
                [ Math.floor( gridSize[0] / 2 ) + left, Math.floor( gridSize[1] / 2 ) + up ] ];

        grid[turtles[i][1][0]][turtles[i][1][1]] = i;

    }

    grid[Math.floor( gridSize[0] / 2 )][Math.floor( gridSize[1] / 2 )] = "S";

    // print( grid.toString() )

    var pickDirections = function() {

        var up = Math.floor( Random.rand() * 3 );
        if ( up == 2 )
            up = -1;

        if ( up == 0 ) {
            var left = Math.floor( Random.rand() * 3 );
            if ( left == 2 )
                left = -1;
        } else
            left = 0;

        if ( Random.rand() < 0.5 ) {
            var swap = left;
            left = up;
            up = swap;
        }

        return [ left, up ];
    };

    for ( var s = 0; s < turtleSteps; s++ ) {

        for ( var t = 0; t < numTurtles; t++ ) {

            var dirs = pickDirections();
            var up = dirs[0];
            var left = dirs[1];

            var lastTurtle = turtles[t][turtles[t].length - 1];
            var nextTurtle = [ lastTurtle[0] + left, lastTurtle[1] + up ];

            if ( nextTurtle[0] >= gridSize[0] ||
                 nextTurtle[1] >= gridSize[1] ||
                 nextTurtle[0] < 0 ||
                 nextTurtle[1] < 0 )
                continue;

            if ( grid[nextTurtle[0]][nextTurtle[1]] == undefined ) {
                turtles[t].push( nextTurtle );
                grid[nextTurtle[0]][nextTurtle[1]] = t;
            }

        }
    }

    turtlePaths = [];
    for ( var t = 0; t < numTurtles; t++ ) {

        turtlePath = [];

        var nextSeg = function(currTurtle, prevTurtle) {

            var pathX = currTurtle[0]

            if ( currTurtle[1] < prevTurtle[1] ) {
                pathX = currTurtle[0] + 1;
                pathY = prevTurtle[1]
            } else if ( currTurtle[1] > prevTurtle[1] ) {
                pathX = currTurtle[0];
                pathY = currTurtle[1];
            } else if ( currTurtle[0] < prevTurtle[0] ) {
                pathX = prevTurtle[0];
                pathY = currTurtle[1];
            } else if ( currTurtle[0] > prevTurtle[0] ) {
                pathX = currTurtle[0];
                pathY = currTurtle[1] + 1;
            }

            // print( " Prev : " + prevTurtle + " Curr : " + currTurtle + " path
            // : "
            // + [pathX, pathY]);

            return [ pathX, pathY ]
        };

        for ( var s = 1; s < turtles[t].length; s++ ) {

            currTurtle = turtles[t][s];
            prevTurtle = turtles[t][s - 1];

            turtlePath.push( nextSeg( currTurtle, prevTurtle ) );

        }

        for ( var s = turtles[t].length - 2; s >= 0; s-- ) {

            currTurtle = turtles[t][s];
            prevTurtle = turtles[t][s + 1];

            turtlePath.push( nextSeg( currTurtle, prevTurtle ) );

        }

        // printjson( turtlePath )

        // End of the line is not inside our polygon.
        var lastTurtle = turtles[t][turtles[t].length - 1];
        grid[lastTurtle[0]][lastTurtle[1]] = undefined;

        fixedTurtlePath = [];
        for ( var s = 1; s < turtlePath.length; s++ ) {

            if ( turtlePath[s - 1][0] == turtlePath[s][0] &&
                 turtlePath[s - 1][1] == turtlePath[s][1] ) {
                continue;
            }

            var up = turtlePath[s][1] - turtlePath[s - 1][1];
            var right = turtlePath[s][0] - turtlePath[s - 1][0];
            var addPoint = ( up != 0 && right != 0 );

            if ( addPoint && up != right ) {
                fixedTurtlePath.push( [ turtlePath[s][0], turtlePath[s - 1][1] ] );
            } else if ( addPoint ) {
                fixedTurtlePath.push( [ turtlePath[s - 1][0], turtlePath[s][1] ] );
            }

            fixedTurtlePath.push( turtlePath[s] );
        }

        // printjson( fixedTurtlePath )

        turtlePaths.push( fixedTurtlePath );
    }

    // Uncomment to print polygon shape
    // print( grid.toString() )

    var polygon = [];
    for ( var t = 0; t < turtlePaths.length; t++ ) {
        for ( var s = 0; s < turtlePaths[t].length; s++ ) {
            polygon.push( rotatePoint( turtlePaths[t][s] ) );
        }
    }

    // Uncomment to print out polygon
    // printjson( polygon )

    t = db.polytest2;
    t.drop();

    // Test single and multi-location documents
    var pointsIn = 0;
    var pointsOut = 0;
    var allPointsIn = [];
    var allPointsOut = [];

    for ( var j = grid[0].length - 1; j >= 0; j-- ) {
        for ( var i = 0; i < grid.length; i++ ) {
            var point = rotatePoint( [ i + 0.5, j + 0.5 ] );

            t.insert( { loc : point } );
            if ( grid[i][j] != undefined ){
                allPointsIn.push( point );
                pointsIn++;
            }
            else{
                allPointsOut.push( point );
                pointsOut++;
            }
        }
    }

    var res = t.ensureIndex({ loc: "2d" }, { bits: 1 + bits, max: bounds[1], min: bounds[0] });
    assert.commandWorked( res );

    t.insert( { loc : allPointsIn } );
    t.insert( { loc : allPointsOut } );
    allPoints = allPointsIn.concat( allPointsOut );
    t.insert( { loc : allPoints } );

    print( "Points : " );
    printjson( { pointsIn : pointsIn, pointsOut : pointsOut } );
    //print( t.find( { loc : { "$within" : { "$polygon" : polygon } } } ).count() )

    assert.eq( gridSize[0] * gridSize[1] + 3, t.find().count() );
    assert.eq( 2 + pointsIn, t.find( { loc : { "$within" : { "$polygon" : polygon } } } ).count() );
}
