/*global defineSuite*/
defineSuite([
        'Core/WallGeometry',
        'Core/Cartesian3',
        'Core/Ellipsoid',
        'Core/Math',
        'Core/VertexFormat',
        'Specs/createPackableSpecs'
    ], function(
        WallGeometry,
        Cartesian3,
        Ellipsoid,
        CesiumMath,
        VertexFormat,
        createPackableSpecs) {
    "use strict";

    var ellipsoid = Ellipsoid.WGS84;

    it('throws with no positions', function() {
        expect(function() {
            return new WallGeometry();
        }).toThrowDeveloperError();
    });

    it('throws when positions and minimumHeights length do not match', function() {
        expect(function() {
            return new WallGeometry({
                positions : new Array(2),
                minimumHeights : new Array(3)
            });
        }).toThrowDeveloperError();
    });

    it('throws when positions and maximumHeights length do not match', function() {
        expect(function() {
            return new WallGeometry({
                positions : new Array(2),
                maximumHeights : new Array(3)
            });
        }).toThrowDeveloperError();
    });

    it('throws with less than 2 positions', function() {
        expect(function() {
            return WallGeometry.createGeometry(new WallGeometry({
                vertexFormat : VertexFormat.POSITION_ONLY,
                positions    : ([Cartesian3.fromDegrees(49.0, 18.0, 1000.0)])
            }));
        }).toThrowDeveloperError();
    });

    it('createGeometry returnes undefined with less than 2 unique positions', function() {
        var geometry = WallGeometry.createGeometry(new WallGeometry({
            positions : Cartesian3.fromDegreesArrayHeights([
                49.0, 18.0, 1000.0,
                49.0, 18.0, 5000.0,
                49.0, 18.0, 1000.0
            ])
        }));
        expect(geometry).not.toBeDefined();
    });

    it('does not throw when positions are unique but close', function() {
        WallGeometry.createGeometry(new WallGeometry({
            vertexFormat : VertexFormat.POSITION_ONLY,
            positions    : Cartesian3.fromDegreesArray([
                -47.93121266896352,-15.771192496304398,
                -47.93119792786269,-15.771148001875085
            ])
        }));
    });

    it('creates positions relative to ellipsoid', function() {
        var w = WallGeometry.createGeometry(new WallGeometry({
            vertexFormat : VertexFormat.POSITION_ONLY,
            positions    : Cartesian3.fromDegreesArrayHeights([
                49.0, 18.0, 1000.0,
                50.0, 18.0, 1000.0
            ])
        }));

        var positions = w.attributes.position.values;
        expect(positions.length).toEqual(2 * 2 * 3);
        expect(w.indices.length).toEqual(2 * 3);

        var cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 0));
        expect(cartographic.height).toEqualEpsilon(0.0, CesiumMath.EPSILON8);

        cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 3));
        expect(cartographic.height).toEqualEpsilon(1000.0, CesiumMath.EPSILON8);
    });

    it('creates positions with minimum and maximum heights', function() {
        var w = WallGeometry.createGeometry(new WallGeometry({
            vertexFormat : VertexFormat.POSITION_ONLY,
            positions    : Cartesian3.fromDegreesArrayHeights([
                49.0, 18.0, 1000.0,
                50.0, 18.0, 1000.0
            ]),
            minimumHeights : [1000.0, 2000.0],
            maximumHeights : [3000.0, 4000.0]
        }));

        var positions = w.attributes.position.values;
        expect(positions.length).toEqual(2 * 2 * 3);
        expect(w.indices.length).toEqual(2 * 3);

        var cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 0));
        expect(cartographic.height).toEqualEpsilon(1000.0, CesiumMath.EPSILON8);

        cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 3));
        expect(cartographic.height).toEqualEpsilon(3000.0, CesiumMath.EPSILON8);

        cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 6));
        expect(cartographic.height).toEqualEpsilon(2000.0, CesiumMath.EPSILON8);

        cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 9));
        expect(cartographic.height).toEqualEpsilon(4000.0, CesiumMath.EPSILON8);
    });

    it('cleans positions with duplicates', function() {
        var w = WallGeometry.createGeometry(new WallGeometry({
            vertexFormat : VertexFormat.POSITION_ONLY,
            positions    : Cartesian3.fromDegreesArrayHeights([
                49.0, 18.0, 1000.0,
                49.0, 18.0, 2000.0,
                50.0, 18.0, 1000.0,
                50.0, 18.0, 1000.0,
                50.0, 18.0, 1000.0,
                51.0, 18.0, 1000.0,
                51.0, 18.0, 1000.0
            ])
        }));

        var positions = w.attributes.position.values;
        expect(positions.length).toEqual(4 * 2 * 3);
        expect(w.indices.length).toEqual((4 * 2 - 2) * 3);

        var cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 0));
        expect(cartographic.height).toEqualEpsilon(0.0, CesiumMath.EPSILON8);

        cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 3));
        expect(cartographic.height).toEqualEpsilon(2000.0, CesiumMath.EPSILON8);
    });

    it('does not clean positions that add up past EPSILON14', function() {
        var eightyPercentOfEpsilon14 = 0.8 * CesiumMath.EPSILON14;
        var inputPositions = Cartesian3.fromRadiansArrayHeights([
            1.0, 1.0, 1000.0,
            1.0, 1.0 + eightyPercentOfEpsilon14, 1000.0,
            1.0, 1.0 + (2 * eightyPercentOfEpsilon14), 1000.0,
            1.0, 1.0 + (3 * eightyPercentOfEpsilon14), 1000.0
        ]);
        var w = WallGeometry.createGeometry(new WallGeometry({
            vertexFormat : VertexFormat.POSITION_ONLY,
            positions    : inputPositions
        }));
        expect(w).toBeDefined();

        var expectedPositions = Cartesian3.fromRadiansArrayHeights([
            1.0, 1.0, 1000.0,
            1.0, 1.0 + (2 * eightyPercentOfEpsilon14), 1000.0
        ]);
        var expectedW = WallGeometry.createGeometry(new WallGeometry({
            vertexFormat : VertexFormat.POSITION_ONLY,
            positions    : expectedPositions
        }));
        var positions = w.attributes.position.values;
        expect(positions.length).toEqual(expectedW.attributes.position.values.length);
    });

    it('cleans selects maximum height from duplicates', function() {
        var w = WallGeometry.createGeometry(new WallGeometry({
            vertexFormat : VertexFormat.POSITION_ONLY,
            positions    : Cartesian3.fromDegreesArrayHeights([
                49.0, 18.0, 1000.0,
                50.0, 18.0, 1000.0,
                50.0, 18.0, 6000.0,
                50.0, 18.0, 10000.0,
                51.0, 18.0, 1000.0
            ])
        }));

        var positions = w.attributes.position.values;
        expect(positions.length).toEqual(4 * 2 * 3);
        expect(w.indices.length).toEqual((4 * 2 - 2) * 3);

        var cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 0));
        expect(cartographic.height).toEqualEpsilon(0.0, CesiumMath.EPSILON8);

        cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 9));
        expect(cartographic.height).toEqualEpsilon(10000.0, CesiumMath.EPSILON8);
    });

    it('creates all attributes', function() {
        var w = WallGeometry.createGeometry(new WallGeometry({
            vertexFormat : VertexFormat.ALL,
            positions    : Cartesian3.fromDegreesArrayHeights([
                49.0, 18.0, 1000.0,
                50.0, 18.0, 1000.0,
                51.0, 18.0, 1000.0
            ])
        }));

        expect(w.attributes.position.values.length).toEqual(4 * 2 * 3);
        expect(w.attributes.normal.values.length).toEqual(4 * 2 * 3);
        expect(w.attributes.tangent.values.length).toEqual(4 * 2 * 3);
        expect(w.attributes.binormal.values.length).toEqual(4 * 2 * 3);
        expect(w.attributes.st.values.length).toEqual(4 * 2 * 2);
        expect(w.indices.length).toEqual((4 * 2 - 2) * 3);
    });

    it('creates correct texture coordinates', function() {
        var w = WallGeometry.createGeometry(new WallGeometry({
            vertexFormat : VertexFormat.ALL,
            positions    : Cartesian3.fromDegreesArrayHeights([
                49.0, 18.0, 1000.0,
                50.0, 18.0, 1000.0,
                51.0, 18.0, 1000.0
            ])
        }));

        expect(w.attributes.st.values.length).toEqual(4 * 2 * 2);
        expect(w.attributes.st.values).toEqual([
            0.0, 0.0,
            0.0, 1.0,
            0.5, 0.0,
            0.5, 1.0,
            0.5, 0.0,
            0.5, 1.0,
            1.0, 0.0,
            1.0, 1.0
        ]);
    });

    it('fromConstantHeights throws without positions', function() {
        expect(function() {
            return WallGeometry.fromConstantHeights();
        }).toThrowDeveloperError();
    });

    it('creates positions with constant minimum and maximum heights', function() {
        var min = 1000.0;
        var max = 2000.0;

        var w = WallGeometry.createGeometry(WallGeometry.fromConstantHeights({
            vertexFormat : VertexFormat.POSITION_ONLY,
            positions    : Cartesian3.fromDegreesArrayHeights([
                49.0, 18.0, 1000.0,
                50.0, 18.0, 1000.0
            ]),
            minimumHeight : min,
            maximumHeight : max
        }));

        var positions = w.attributes.position.values;
        expect(positions.length).toEqual(2 * 2 * 3);
        expect(w.indices.length).toEqual(2 * 3);

        var cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 0));
        expect(cartographic.height).toEqualEpsilon(min, CesiumMath.EPSILON8);

        cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 3));
        expect(cartographic.height).toEqualEpsilon(max, CesiumMath.EPSILON8);

        cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 6));
        expect(cartographic.height).toEqualEpsilon(min, CesiumMath.EPSILON8);

        cartographic = ellipsoid.cartesianToCartographic(Cartesian3.fromArray(positions, 9));
        expect(cartographic.height).toEqualEpsilon(max, CesiumMath.EPSILON8);
    });

    var positions = [new Cartesian3(1.0, 0.0, 0.0), new Cartesian3(0.0, 1.0, 0.0), new Cartesian3(0.0, 0.0, 1.0)];
    var wall = new WallGeometry({
        positions : positions,
        vertexFormat : VertexFormat.POSITION_ONLY,
        granularity : 0.01,
        ellipsoid: Ellipsoid.UNIT_SPHERE
    });
    var packedInstance = [3.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.01];
    createPackableSpecs(WallGeometry, wall, packedInstance);
});

