/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { expect } from "chai";
import { assert, ByteStream, utf8ToString } from "@itwin/core-bentley";
import { CurrentImdlVersion, ElementGeometry, FeatureTableHeader, GeometryStreamIterator, GltfHeader, ImdlHeader, } from "@itwin/core-common";
import { _nativeDb, GeometricElement3d } from "../../core-backend";
import { IModelTestUtils } from "../IModelTestUtils";
import { LineSegment3d } from "@itwin/core-geometry";
describe("ElementGraphics", () => {
    let imodel;
    before(() => {
        imodel = IModelTestUtils.createSnapshotFromSeed(IModelTestUtils.prepareOutputFile("ElementGraphics", "mirukuru.ibim"), IModelTestUtils.resolveAssetFile("mirukuru.ibim"));
    });
    after(() => {
        if (imodel && imodel.isOpen)
            imodel.close();
    });
    it("obtains graphics for elements", async () => {
        const elementId = "0x29";
        const element = imodel.elements.tryGetElement(elementId);
        expect(element).not.to.be.undefined;
        expect(element).instanceof(GeometricElement3d);
        const request = {
            id: "test",
            elementId,
            toleranceLog10: -2,
            formatVersion: CurrentImdlVersion.Major,
        };
        const result = await imodel[_nativeDb].generateElementGraphics(request);
        expect(result.status).to.equal(0 /* ElementGraphicsStatus.Success */);
        assert(result.status === 0 /* ElementGraphicsStatus.Success */);
        const content = result.content;
        expect(content).not.to.be.undefined;
        expect(content instanceof Uint8Array).to.be.true;
        expect(content.length).least(40);
    });
    it("obtains graphics for dynamics from json format geometry stream", async () => {
        const elementId = "0x29";
        const element = imodel.elements.tryGetElement({ id: elementId, wantGeometry: true });
        expect(element).not.to.be.undefined;
        expect(element).instanceof(GeometricElement3d);
        expect(element?.geom).not.to.be.undefined;
        expect(element?.placement).not.to.be.undefined;
        const request = {
            id: "test",
            elementId,
            toleranceLog10: -2,
            formatVersion: CurrentImdlVersion.Major,
            type: "3d",
            placement: element.placement,
            categoryId: element.category,
            geometry: { format: "json", data: element.geom },
        };
        const result = await imodel[_nativeDb].generateElementGraphics(request);
        expect(result.status).to.equal(0 /* ElementGraphicsStatus.Success */);
        assert(result.status === 0 /* ElementGraphicsStatus.Success */);
        const content = result.content;
        expect(content).not.to.be.undefined;
        expect(content instanceof Uint8Array).to.be.true;
        expect(content.length).least(40);
    });
    it("obtains graphics for dynamics from flatbuffers format geometry stream", async () => {
        const elementId = "0x29";
        const element = imodel.elements.tryGetElement({ id: elementId, wantGeometry: true });
        expect(element).not.to.be.undefined;
        expect(element).instanceof(GeometricElement3d);
        expect(element?.geom).not.to.be.undefined;
        expect(element?.placement).not.to.be.undefined;
        const entries = [];
        const it = new GeometryStreamIterator(element.geom, element.category);
        for (const entry of it) {
            if ("geometryQuery" !== entry.primitive.type)
                continue;
            if (!ElementGeometry.appendGeometryParams(entry.geomParams, entries))
                continue;
            const geomEntry = ElementGeometry.fromGeometryQuery(entry.primitive.geometry);
            expect(geomEntry).not.to.be.undefined;
            entries.push(geomEntry);
        }
        const request = {
            id: "test",
            elementId,
            toleranceLog10: -2,
            formatVersion: CurrentImdlVersion.Major,
            type: "3d",
            placement: element.placement,
            categoryId: element.category,
            geometry: { format: "flatbuffer", data: entries },
        };
        const result = await imodel[_nativeDb].generateElementGraphics(request);
        expect(result.status).to.equal(0 /* ElementGraphicsStatus.Success */);
        assert(result.status === 0 /* ElementGraphicsStatus.Success */);
        const content = result.content;
        expect(content).not.to.be.undefined;
        expect(content instanceof Uint8Array).to.be.true;
        expect(content.length).least(40);
    });
    it("supports an unlimited number of flatbuffer geometry stream entries", async () => {
        async function getElementGraphics(numCopies) {
            const elementId = "0x29";
            const element = imodel.elements.tryGetElement({ id: elementId, wantGeometry: true });
            expect(element).not.to.be.undefined;
            expect(element).instanceof(GeometricElement3d);
            expect(element?.geom).not.to.be.undefined;
            expect(element?.placement).not.to.be.undefined;
            const entries = [];
            const it = new GeometryStreamIterator(element.geom, element.category);
            for (const entry of it) {
                if ("geometryQuery" !== entry.primitive.type)
                    continue;
                for (let i = 0; i < numCopies; i++) {
                    const segment = LineSegment3d.createXYXY(i, i, i + 1, i);
                    const geomEntry = ElementGeometry.fromGeometryQuery(segment);
                    expect(geomEntry).not.to.be.undefined;
                    entries.push(geomEntry);
                }
                break;
            }
            const request = {
                id: "test",
                elementId,
                toleranceLog10: -2,
                formatVersion: CurrentImdlVersion.Major,
                type: "3d",
                placement: element.placement,
                categoryId: element.category,
                geometry: { format: "flatbuffer", data: entries },
            };
            const result = await imodel[_nativeDb].generateElementGraphics(request);
            expect(result.status).to.equal(0 /* ElementGraphicsStatus.Success */);
            assert(result.status === 0 /* ElementGraphicsStatus.Success */);
            const content = result.content;
            expect(content).not.to.be.undefined;
            expect(content instanceof Uint8Array).to.be.true;
            expect(content.length).least(40);
            return content;
        }
        let prevGraphics = [];
        let prevRangeDiagonalMagnitude = 0;
        for (const numCopies of [1, 2, 3, 10, 100, 1000, 2000, 2047, 2048, 2049, 2050, 2500, 2501, 2600, 3000, 10000]) {
            const tileBytes = await getElementGraphics(numCopies);
            const newGraphics = Array.from(tileBytes);
            expect(newGraphics).not.to.deep.equal(prevGraphics);
            // Extract metadata from the tile graphics.
            const stream = ByteStream.fromUint8Array(tileBytes);
            const header = new ImdlHeader(stream);
            const featureTableStartPos = stream.curPos;
            const featureTableHeader = FeatureTableHeader.readFrom(stream);
            expect(featureTableHeader).not.to.be.undefined;
            stream.curPos = featureTableStartPos + featureTableHeader.length;
            const gltfHeader = new GltfHeader(stream);
            expect(gltfHeader.isValid).to.be.true;
            stream.curPos = gltfHeader.scenePosition;
            const sceneStrData = stream.nextBytes(gltfHeader.sceneStrLength);
            const sceneStr = utf8ToString(sceneStrData);
            expect(sceneStr).not.to.be.undefined;
            const json = JSON.parse(sceneStr);
            // The tile should have two unique vertices per line segment in the input geometry stream.
            expect(json.meshes.Mesh_Root.primitives[0].vertices.count).to.equal(numCopies * 2);
            expect(header.contentRange.diagonal().magnitude()).greaterThan(prevRangeDiagonalMagnitude);
            prevRangeDiagonalMagnitude = header.contentRange.diagonal().magnitude();
            expect(newGraphics.length).greaterThan(prevGraphics.length);
            prevGraphics = newGraphics;
        }
    });
    it("produces expected errors", async () => {
        const testCases = [
            [5 /* ElementGraphicsStatus.ElementNotFound */, { elementId: "0" }],
            [5 /* ElementGraphicsStatus.ElementNotFound */, { elementId: "0x12345678" }],
            [5 /* ElementGraphicsStatus.ElementNotFound */, { elementId: undefined }],
            [3 /* ElementGraphicsStatus.InvalidJson */, { id: undefined }],
            [3 /* ElementGraphicsStatus.InvalidJson */, { toleranceLog10: undefined }],
            [3 /* ElementGraphicsStatus.InvalidJson */, { toleranceLog10: 12.5 }],
            [3 /* ElementGraphicsStatus.InvalidJson */, { toleranceLog10: "tol" }],
            [0 /* ElementGraphicsStatus.Success */, { formatVersion: undefined }],
            [4 /* ElementGraphicsStatus.UnknownMajorFormatVersion */, { formatVersion: CurrentImdlVersion.Major + 1 }],
            [4 /* ElementGraphicsStatus.UnknownMajorFormatVersion */, { formatVersion: "latest" }],
        ];
        for (const testCase of testCases) {
            const request = {
                id: "test",
                elementId: "0x29",
                toleranceLog10: -2,
                formatVersion: CurrentImdlVersion.Major,
                ...testCase[1],
            };
            const result = await imodel[_nativeDb].generateElementGraphics(request);
            expect(result.status).to.equal(testCase[0]);
            if (result.status === 0 /* ElementGraphicsStatus.Success */)
                expect(result.content).not.to.be.undefined;
        }
    });
});
//# sourceMappingURL=ElementGraphics.test.js.map