import { arrayToString } from './arrayToString.js';

export class FeatureTable {

	constructor( buffer, start, headerLength, binLength ) {

		this.buffer = buffer;
		this.binOffset = start + headerLength;
		this.binLength = binLength;

		let header = null;
		if ( headerLength !== 0 ) {

			const headerData = new Uint8Array( buffer, start, headerLength );
			header = JSON.parse( arrayToString( headerData ) );

		} else {

			header = {};

		}
		this.header = header;

	}

	getKeys() {

		return Object.keys( this.header );

	}

	getData( key, count, defaultComponentType = null, defaultType = null ) {

		const header = this.header;

		if ( ! ( key in header ) ) {

			return null;

		}

		const feature = header[ key ];
		if ( ! ( feature instanceof Object ) ) {

			return feature;

		} else if ( Array.isArray( feature ) ) {

			return feature;

		} else {

			const { buffer, binOffset, binLength } = this;
			const byteOffset = feature.byteOffset || 0;
			const featureType = feature.type || defaultType;
			const featureComponentType = feature.componentType || defaultComponentType;

			if ( 'type' in feature && defaultType && feature.type !== defaultType ) {

				throw new Error( 'FeatureTable: Specified type does not match expected type.' );

			}

			let stride;
			switch ( featureType ) {

				case 'SCALAR':
					stride = 1;
					break;

				case 'VEC2':
					stride = 2;
					break;

				case 'VEC3':
					stride = 3;
					break;

				case 'VEC4':
					stride = 4;
					break;

				default:
					throw new Error( `FeatureTable : Feature type not provided for "${ key }".` );

			}

			let data;
			const arrayStart = binOffset + byteOffset;
			const arrayLength = count * stride;

			switch ( featureComponentType ) {

				case 'BYTE':
					data = new Int8Array( buffer, arrayStart, arrayLength );
					break;

				case 'UNSIGNED_BYTE':
					data = new Uint8Array( buffer, arrayStart, arrayLength );
					break;

				case 'SHORT':
					data = new Int16Array( buffer, arrayStart, arrayLength );
					break;

				case 'UNSIGNED_SHORT':
					data = new Uint16Array( buffer, arrayStart, arrayLength );
					break;

				case 'INT':
					data = new Int32Array( buffer, arrayStart, arrayLength );
					break;

				case 'UNSIGNED_INT':
					data = new Uint32Array( buffer, arrayStart, arrayLength );
					break;

				case 'FLOAT':
					data = new Float32Array( buffer, arrayStart, arrayLength );
					break;

				case 'DOUBLE':
					data = new Float64Array( buffer, arrayStart, arrayLength );
					break;

				default:
					throw new Error( `FeatureTable : Feature component type not provided for "${ key }".` );

			}

			const dataEnd = arrayStart + arrayLength * data.BYTES_PER_ELEMENT;
			if ( dataEnd > binOffset + binLength ) {

				throw new Error( 'FeatureTable: Feature data read outside binary body length.' );

			}

			return data;

		}

	}

}

export class BatchTable extends FeatureTable {

	constructor( buffer, batchSize, start, headerLength, binLength ) {

		super( buffer, start, headerLength, binLength );
		this.batchSize = batchSize;

	}

	getData( key, componentType = null, type = null ) {

		return super.getData( key, this.batchSize, componentType, type );

	}

}
