// I3DM File Format
// https://github.com/CesiumGS/3d-tiles/blob/master/specification/TileFormats/Instanced3DModel/README.md

import { FeatureTable, BatchTable } from '../utilities/FeatureTable.js';
import { arrayToString } from '../utilities/arrayToString.js';
import { LoaderBase } from './LoaderBase.js';
import { readMagicBytes } from '../utilities/readMagicBytes.js';

export class I3DMLoaderBase extends LoaderBase {

	parse( buffer ) {

		const dataView = new DataView( buffer );

		// 32-byte header

		// 4 bytes
		const magic = readMagicBytes( dataView );

		console.assert( magic === 'i3dm' );

		// 4 bytes
		const version = dataView.getUint32( 4, true );

		console.assert( version === 1 );

		// 4 bytes
		const byteLength = dataView.getUint32( 8, true );

		console.assert( byteLength === buffer.byteLength );

		// 4 bytes
		const featureTableJSONByteLength = dataView.getUint32( 12, true );

		// 4 bytes
		const featureTableBinaryByteLength = dataView.getUint32( 16, true );

		// 4 bytes
		const batchTableJSONByteLength = dataView.getUint32( 20, true );

		// 4 bytes
		const batchTableBinaryByteLength = dataView.getUint32( 24, true );

		// 4 bytes
		const gltfFormat = dataView.getUint32( 28, true );

		// Feature Table
		const featureTableStart = 32;
		const featureTableBuffer = buffer.slice(
			featureTableStart,
			featureTableStart + featureTableJSONByteLength + featureTableBinaryByteLength,
		);
		const featureTable = new FeatureTable(
			featureTableBuffer,
			0,
			featureTableJSONByteLength,
			featureTableBinaryByteLength,
		);

		// Batch Table
		const batchTableStart = featureTableStart + featureTableJSONByteLength + featureTableBinaryByteLength;
		const batchTableBuffer = buffer.slice(
			batchTableStart,
			batchTableStart + batchTableJSONByteLength + batchTableBinaryByteLength,
		);
		const batchTable = new BatchTable(
			batchTableBuffer,
			featureTable.getData( 'INSTANCES_LENGTH' ),
			0,
			batchTableJSONByteLength,
			batchTableBinaryByteLength,
		);

		const glbStart = batchTableStart + batchTableJSONByteLength + batchTableBinaryByteLength;
		const bodyBytes = new Uint8Array( buffer, glbStart, byteLength - glbStart );

		let glbBytes = null;
		let promise = null;
		if ( gltfFormat ) {

			glbBytes = bodyBytes;
			promise = Promise.resolve();

		} else {

			const externalUri = this.resolveExternalURL( arrayToString( bodyBytes ) );
			promise = fetch( externalUri, this.fetchOptions )
				.then( res => {

					if ( ! res.ok ) {

						throw new Error( `I3DMLoaderBase : Failed to load file "${ externalUri }" with status ${ res.status } : ${ res.statusText }` );

					}

					return res.arrayBuffer();

				} )
				.then( buffer => {

					glbBytes = new Uint8Array( buffer );

				} );

		}

		return promise.then( () => {

			return {
				version,
				featureTable,
				batchTable,
				glbBytes,
			};

		} );

	}

}

