/*---------------------------------------------------------------------------------------------
* 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 { Guid, Id64 } from "@itwin/core-bentley";
import { withEditTxn } from "../../EditTxn";
import { Loop, Path, Point3d, PolyfaceBuilder, Range3d, StrokeOptions, } from "@itwin/core-geometry";
import { Code, ColorDef, GeometryParams, GeometryStreamBuilder, IModel, readElementMeshes, } from "@itwin/core-common";
import { _nativeDb, GenericSchema, GeometryPart, PhysicalModel, PhysicalObject, PhysicalPartition, SnapshotDb, SpatialCategory, SubjectOwnsPartitionElements, } from "../../core-backend";
import { IModelTestUtils } from "../IModelTestUtils";
describe("generateElementMeshes", () => {
    let imodel;
    let modelId;
    let categoryId;
    before(() => {
        imodel = SnapshotDb.createEmpty(IModelTestUtils.prepareOutputFile("generateElementMeshes", `${Guid.createValue()}.bim`), {
            rootSubject: { name: "generateElementMeshes", description: "generateElementMeshes" },
        });
        GenericSchema.registerSchema();
        withEditTxn(imodel, (txn) => {
            const partitionId = txn.insertElement({
                classFullName: PhysicalPartition.classFullName,
                model: IModel.repositoryModelId,
                parent: new SubjectOwnsPartitionElements(IModel.rootSubjectId),
                code: PhysicalPartition.createCode(imodel, IModel.rootSubjectId, `PhysicalPartition_${Guid.createValue()}`),
            });
            const model = imodel.models.createModel({
                classFullName: PhysicalModel.classFullName,
                modeledElement: { id: partitionId },
            });
            modelId = txn.insertModel(model.toJSON());
            categoryId = SpatialCategory.insert(txn, IModel.dictionaryId, "cat", { color: ColorDef.blue.toJSON() });
        });
    });
    after(() => {
        imodel.close();
    });
    it("throws if source is not a geometric element", async () => {
        await expect(imodel[_nativeDb].generateElementMeshes({ source: "NotAnId" })).rejectedWith("Geometric element required");
    });
    function insertTriangleElement(origin = [0, 0, 0]) {
        const bldr = new GeometryStreamBuilder();
        bldr.appendGeometryParamsChange(new GeometryParams(categoryId));
        bldr.appendGeometry(Loop.createPolygon([new Point3d(0, 0, 0), new Point3d(1, 0, 0), new Point3d(0, 1, 0), new Point3d(0, 0, 0)]));
        return insertElement(bldr.geometryStream, origin);
    }
    function insertElement(geom, origin = [0, 0, 0]) {
        const props = {
            classFullName: PhysicalObject.classFullName,
            model: modelId,
            code: Code.createEmpty(),
            category: categoryId,
            geom,
            placement: {
                origin,
                angles: {},
            },
        };
        const elemId = withEditTxn(imodel, (txn) => txn.insertElement(props));
        expect(Id64.isValidId64(elemId)).to.be.true;
        return elemId;
    }
    it("produces a polyface", async () => {
        const source = insertTriangleElement();
        const bytes = await imodel[_nativeDb].generateElementMeshes({ source });
        const meshes = readElementMeshes(bytes);
        expect(meshes.length).to.equal(1);
        expect(meshes[0].range().isAlmostEqual(new Range3d(0, 0, 0, 1, 1, 0))).to.be.true;
    });
    it("applies element placement transform", async () => {
        const source = insertTriangleElement([5, 0, -2]);
        const bytes = await imodel[_nativeDb].generateElementMeshes({ source });
        const meshes = readElementMeshes(bytes);
        expect(meshes.length).to.equal(1);
        expect(meshes[0].range().isAlmostEqual(new Range3d(5, 0, -2, 6, 1, -2))).to.be.true;
    });
    it("applies part reference transform", async () => {
        const ptBldr = new GeometryStreamBuilder();
        ptBldr.appendGeometry(Loop.createPolygon([new Point3d(0, 0, 0), new Point3d(1, 0, 0), new Point3d(0, 1, 0), new Point3d(0, 0, 0)]));
        const partProps = {
            classFullName: GeometryPart.classFullName,
            model: IModel.dictionaryId,
            code: GeometryPart.createCode(imodel, IModel.dictionaryId, Guid.createValue()),
            geom: ptBldr.geometryStream,
        };
        const partId = withEditTxn(imodel, (txn) => txn.insertElement(partProps));
        expect(Id64.isValidId64(partId)).to.be.true;
        const elBldr = new GeometryStreamBuilder();
        elBldr.appendGeometryParamsChange(new GeometryParams(categoryId));
        elBldr.appendGeometryPart3d(partId, new Point3d(0, 0, 1));
        elBldr.appendGeometryPart3d(partId, new Point3d(0, 0, -1));
        const source = insertElement(elBldr.geometryStream, [2, -4, 0]);
        const bytes = await imodel[_nativeDb].generateElementMeshes({ source });
        const meshes = readElementMeshes(bytes);
        expect(meshes.length).to.equal(2);
        expect(meshes[0].range().isAlmostEqual(new Range3d(2, -4, 1, 3, -3, 1))).to.be.true;
        expect(meshes[1].range().isAlmostEqual(new Range3d(2, -4, -1, 3, -3, -1))).to.be.true;
    });
    it("produces multiple polyfaces", async () => {
        const bldr = new GeometryStreamBuilder();
        bldr.appendGeometryParamsChange(new GeometryParams(categoryId));
        bldr.appendGeometry(Loop.createPolygon([new Point3d(0, 0, 0), new Point3d(1, 0, 0), new Point3d(0, 1, 0), new Point3d(0, 0, 0)]));
        bldr.appendGeometry(Loop.createPolygon([new Point3d(0, 0, 5), new Point3d(1, 0, 5), new Point3d(0, 1, 5), new Point3d(0, 0, 5)]));
        const source = insertElement(bldr.geometryStream);
        const meshes = readElementMeshes(await imodel[_nativeDb].generateElementMeshes({ source }));
        expect(meshes.length).to.equal(2);
        expect(meshes[0].range().isAlmostEqual(new Range3d(0, 0, 0, 1, 1, 0))).to.be.true;
        expect(meshes[1].range().isAlmostEqual(new Range3d(0, 0, 5, 1, 1, 5))).to.be.true;
    });
    it("ignores open curves", async () => {
        const bldr = new GeometryStreamBuilder();
        bldr.appendGeometryParamsChange(new GeometryParams(categoryId));
        bldr.appendGeometry(Loop.createPolygon([new Point3d(0, 0, 0), new Point3d(1, 0, 0), new Point3d(0, 1, 0), new Point3d(0, 0, 0)]));
        bldr.appendGeometry(Path.create([new Point3d(0, 0, 0), new Point3d(1, 1, 1)]));
        bldr.appendGeometry(Loop.createPolygon([new Point3d(0, 0, 5), new Point3d(1, 0, 5), new Point3d(0, 1, 5), new Point3d(0, 0, 5)]));
        const source = insertElement(bldr.geometryStream);
        const meshes = readElementMeshes(await imodel[_nativeDb].generateElementMeshes({ source }));
        expect(meshes.length).to.equal(2);
        expect(meshes[0].range().isAlmostEqual(new Range3d(0, 0, 0, 1, 1, 0))).to.be.true;
        expect(meshes[1].range().isAlmostEqual(new Range3d(0, 0, 5, 1, 1, 5))).to.be.true;
    });
    it("omits normals and UVs", async () => {
        const opts = new StrokeOptions();
        opts.needNormals = opts.needParams = true;
        const pfBldr = PolyfaceBuilder.create(opts);
        pfBldr.addTriangleFacet([new Point3d(0, 0, 0), new Point3d(1, 0, 0), new Point3d(0, 1, 0)]);
        const pf = pfBldr.claimPolyface();
        expect(pf.pointCount).to.equal(3);
        expect(pf.normalCount).least(1);
        expect(pf.paramCount).least(1);
        const bldr = new GeometryStreamBuilder();
        bldr.appendGeometryParamsChange(new GeometryParams(categoryId));
        bldr.appendGeometry(pf);
        const source = insertElement(bldr.geometryStream, [10, 0, 0]);
        const bytes = await imodel[_nativeDb].generateElementMeshes({ source });
        const meshes = readElementMeshes(bytes);
        expect(meshes.length).to.equal(1);
        expect(meshes[0].pointCount).to.equal(3);
        expect(meshes[0].paramCount).to.equal(0);
        expect(meshes[0].normalCount).to.equal(0);
        expect(meshes[0].range().isAlmostEqual(new Range3d(10, 0, 0, 11, 1, 0))).to.be.true;
    });
});
//# sourceMappingURL=ElementMesh.test.js.map