/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Tile
 */
import { assert } from "@itwin/core-bentley";
import { TileFormat, TileHeader } from "./TileIO";
/** Known version of the [glTF format](https://www.khronos.org/gltf/).
 * @internal
 */
export var GltfVersions;
(function (GltfVersions) {
    GltfVersions[GltfVersions["Version1"] = 1] = "Version1";
    GltfVersions[GltfVersions["Version2"] = 2] = "Version2";
    GltfVersions[GltfVersions["CurrentVersion"] = 1] = "CurrentVersion";
    GltfVersions[GltfVersions["Gltf1SceneFormat"] = 0] = "Gltf1SceneFormat";
})(GltfVersions || (GltfVersions = {}));
/** @internal */
export var GltfV2ChunkTypes;
(function (GltfV2ChunkTypes) {
    GltfV2ChunkTypes[GltfV2ChunkTypes["JSON"] = 1313821514] = "JSON";
    GltfV2ChunkTypes[GltfV2ChunkTypes["Binary"] = 5130562] = "Binary";
})(GltfV2ChunkTypes || (GltfV2ChunkTypes = {}));
function consumeNextChunk(stream) {
    if (stream.isAtTheEnd)
        return undefined;
    const offset = stream.curPos + 8;
    const length = stream.readUint32();
    if (stream.isAtTheEnd)
        return undefined;
    const type = stream.readUint32();
    stream.advance(length);
    return stream.isPastTheEnd ? false : { offset, length, type };
}
/** @internal */
export class GlbHeader extends TileHeader {
    gltfLength = 0;
    jsonChunk = { offset: 0, length: 0 };
    binaryChunk;
    additionalChunks = [];
    get isValid() {
        return TileFormat.Gltf === this.format;
    }
    constructor(stream) {
        super(stream);
        this.gltfLength = stream.readUint32();
        const jsonLength = stream.readUint32();
        const word5 = stream.readUint32();
        // Early versions of the reality data tile publisher incorrectly put version 2 into header - handle these old tiles
        // validating the chunk type.
        if (this.version === GltfVersions.Version2 && word5 === GltfVersions.Gltf1SceneFormat)
            this.version = GltfVersions.Version1;
        this.jsonChunk = { offset: stream.curPos, length: jsonLength };
        switch (this.version) {
            case GltfVersions.Version1:
                if (GltfVersions.Gltf1SceneFormat !== word5) {
                    this.invalidate();
                    return;
                }
                const binaryOffset = stream.curPos + jsonLength;
                this.binaryChunk = { offset: binaryOffset, length: this.gltfLength - binaryOffset };
                break;
            case GltfVersions.Version2:
                if (word5 !== GltfV2ChunkTypes.JSON) {
                    this.invalidate();
                    return;
                }
                stream.advance(jsonLength);
                if (stream.isPastTheEnd) {
                    this.invalidate();
                    return;
                }
                let chunk;
                while (chunk = consumeNextChunk(stream)) {
                    switch (chunk.type) {
                        case GltfV2ChunkTypes.JSON:
                            // Only one JSON chunk permitted and it must be the first.
                            this.invalidate();
                            return;
                        case GltfV2ChunkTypes.Binary:
                            // At most one binary chunk permitted and it must be the second if present.
                            if (this.binaryChunk || this.additionalChunks.length) {
                                this.invalidate();
                                return;
                            }
                            this.binaryChunk = { offset: chunk.offset, length: chunk.length };
                            break;
                        default:
                            // Any other chunk type should be ignored - for use by extensions.
                            this.additionalChunks.push(chunk);
                            break;
                    }
                }
                if (false === chunk) {
                    this.invalidate();
                    return;
                }
                assert(undefined === chunk);
                assert(stream.isAtTheEnd);
                break;
            default:
                this.invalidate();
                break;
        }
    }
}
/** @internal */
export class GltfHeader extends TileHeader {
    gltfLength;
    scenePosition = 0;
    sceneStrLength = 0;
    binaryPosition = 0;
    get isValid() { return TileFormat.Gltf === this.format; }
    constructor(stream) {
        super(stream);
        this.gltfLength = stream.readUint32();
        this.sceneStrLength = stream.readUint32();
        const value5 = stream.readUint32();
        // Early versions of the reality data tile publisher incorrectly put version 2 into header - handle these old tiles
        // validating the chunk type.
        if (this.version === GltfVersions.Version2 && value5 === GltfVersions.Gltf1SceneFormat)
            this.version = GltfVersions.Version1;
        if (this.version === GltfVersions.Version1) {
            const gltfSceneFormat = value5;
            if (GltfVersions.Gltf1SceneFormat !== gltfSceneFormat) {
                this.invalidate();
                return;
            }
            this.scenePosition = stream.curPos;
            this.binaryPosition = stream.curPos + this.sceneStrLength;
        }
        else if (this.version === GltfVersions.Version2) {
            const sceneChunkType = value5;
            this.scenePosition = stream.curPos;
            stream.curPos = stream.curPos + this.sceneStrLength;
            const binaryLength = stream.readUint32();
            const binaryChunkType = stream.readUint32();
            if (GltfV2ChunkTypes.JSON !== sceneChunkType || GltfV2ChunkTypes.Binary !== binaryChunkType || 0 === binaryLength) {
                this.invalidate();
                return;
            }
            this.binaryPosition = stream.curPos;
        }
        else {
            this.invalidate();
        }
    }
}
//# sourceMappingURL=GltfTileIO.js.map