function LWO3Parser( IFFParser ) {

	this.IFF = IFFParser;

}

LWO3Parser.prototype = {

	constructor: LWO3Parser,

	parseBlock: function () {

		this.IFF.debugger.offset = this.IFF.reader.offset;
		this.IFF.debugger.closeForms();

		var blockID = this.IFF.reader.getIDTag();
		var length = this.IFF.reader.getUint32(); // size of data in bytes

		this.IFF.debugger.dataOffset = this.IFF.reader.offset;
		this.IFF.debugger.length = length;

		// Data types may be found in either LWO2 OR LWO3 spec
		switch ( blockID ) {

			case 'FORM': // form blocks may consist of sub -chunks or sub-forms
				this.IFF.parseForm( length );
				break;

			// SKIPPED CHUNKS
			// MISC skipped
			case 'ICON': // Thumbnail Icon Image
			case 'VMPA': // Vertex Map Parameter
			case 'BBOX': // bounding box
			// case 'VMMD':
			// case 'VTYP':

			// normal maps can be specified, normally on models imported from other applications. Currently ignored
			case 'NORM':

			// ENVL FORM skipped
			case 'PRE ':
			case 'POST':
			case 'KEY ':
			case 'SPAN':

			// CLIP FORM skipped
			case 'TIME':
			case 'CLRS':
			case 'CLRA':
			case 'FILT':
			case 'DITH':
			case 'CONT':
			case 'BRIT':
			case 'SATR':
			case 'HUE ':
			case 'GAMM':
			case 'NEGA':
			case 'IFLT':
			case 'PFLT':

			// Image Map Layer skipped
			case 'PROJ':
			case 'AXIS':
			case 'AAST':
			case 'PIXB':
			case 'STCK':

			// Procedural Textures skipped
			case 'VALU':

			// Gradient Textures skipped
			case 'PNAM':
			case 'INAM':
			case 'GRST':
			case 'GREN':
			case 'GRPT':
			case 'FKEY':
			case 'IKEY':

			// Texture Mapping Form skipped
			case 'CSYS':

				// Surface CHUNKs skipped
			case 'OPAQ': // top level 'opacity' checkbox
			case 'CMAP': // clip map

			// Surface node CHUNKS skipped
			// These mainly specify the node editor setup in LW
			case 'NLOC':
			case 'NZOM':
			case 'NVER':
			case 'NSRV':
			case 'NCRD':
			case 'NMOD':
			case 'NSEL':
			case 'NPRW':
			case 'NPLA':
			case 'VERS':
			case 'ENUM':
			case 'TAG ':

			// Car Material CHUNKS
			case 'CGMD':
			case 'CGTY':
			case 'CGST':
			case 'CGEN':
			case 'CGTS':
			case 'CGTE':
			case 'OSMP':
			case 'OMDE':
			case 'OUTR':
			case 'FLAG':

			case 'TRNL':
			case 'SHRP':
			case 'RFOP':
			case 'RSAN':
			case 'TROP':
			case 'RBLR':
			case 'TBLR':
			case 'CLRH':
			case 'CLRF':
			case 'ADTR':
			case 'GLOW':
			case 'LINE':
			case 'ALPH':
			case 'VCOL':
			case 'ENAB':
				this.IFF.debugger.skipped = true;
				this.IFF.reader.skip( length );
				break;

			// Texture node chunks (not in spec)
			case 'IPIX': // usePixelBlending
			case 'IMIP': // useMipMaps
			case 'IMOD': // imageBlendingMode
			case 'AMOD': // unknown
			case 'IINV': // imageInvertAlpha
			case 'INCR': // imageInvertColor
			case 'IAXS': // imageAxis ( for non-UV maps)
			case 'IFOT': // imageFallofType
			case 'ITIM': // timing for animated textures
			case 'IWRL':
			case 'IUTI':
			case 'IINX':
			case 'IINY':
			case 'IINZ':
			case 'IREF': // possibly a VX for reused texture nodes
				if ( length === 4 ) this.IFF.currentNode[ blockID ] = this.IFF.reader.getInt32();
				else this.IFF.reader.skip( length );
				break;

			case 'OTAG':
				this.IFF.parseObjectTag();
				break;

			case 'LAYR':
				this.IFF.parseLayer( length );
				break;

			case 'PNTS':
				this.IFF.parsePoints( length );
				break;

			case 'VMAP':
				this.IFF.parseVertexMapping( length );
				break;

			case 'POLS':
				this.IFF.parsePolygonList( length );
				break;

			case 'TAGS':
				this.IFF.parseTagStrings( length );
				break;

			case 'PTAG':
				this.IFF.parsePolygonTagMapping( length );
				break;

			case 'VMAD':
				this.IFF.parseVertexMapping( length, true );
				break;

			// Misc CHUNKS
			case 'DESC': // Description Line
				this.IFF.currentForm.description = this.IFF.reader.getString();
				break;

			case 'TEXT':
			case 'CMNT':
			case 'NCOM':
				this.IFF.currentForm.comment = this.IFF.reader.getString();
				break;

			// Envelope Form
			case 'NAME':
				this.IFF.currentForm.channelName = this.IFF.reader.getString();
				break;

			// Image Map Layer
			case 'WRAP':
				this.IFF.currentForm.wrap = { w: this.IFF.reader.getUint16(), h: this.IFF.reader.getUint16() };
				break;

			case 'IMAG':
				var index = this.IFF.reader.getVariableLengthIndex();
				this.IFF.currentForm.imageIndex = index;
				break;

			// Texture Mapping Form
			case 'OREF':
				this.IFF.currentForm.referenceObject = this.IFF.reader.getString();
				break;

			case 'ROID':
				this.IFF.currentForm.referenceObjectID = this.IFF.reader.getUint32();
				break;

			// Surface Blocks
			case 'SSHN':
				this.IFF.currentSurface.surfaceShaderName = this.IFF.reader.getString();
				break;

			case 'AOVN':
				this.IFF.currentSurface.surfaceCustomAOVName = this.IFF.reader.getString();
				break;

			// Nodal Blocks
			case 'NSTA':
				this.IFF.currentForm.disabled = this.IFF.reader.getUint16();
				break;

			case 'NRNM':
				this.IFF.currentForm.realName = this.IFF.reader.getString();
				break;

			case 'NNME':
				this.IFF.currentForm.refName = this.IFF.reader.getString();
				this.IFF.currentSurface.nodes[ this.IFF.currentForm.refName ] = this.IFF.currentForm;
				break;

			// Nodal Blocks : connections
			case 'INME':
				if ( ! this.IFF.currentForm.nodeName ) this.IFF.currentForm.nodeName = [];
				this.IFF.currentForm.nodeName.push( this.IFF.reader.getString() );
				break;

			case 'IINN':
				if ( ! this.IFF.currentForm.inputNodeName ) this.IFF.currentForm.inputNodeName = [];
				this.IFF.currentForm.inputNodeName.push( this.IFF.reader.getString() );
				break;

			case 'IINM':
				if ( ! this.IFF.currentForm.inputName ) this.IFF.currentForm.inputName = [];
				this.IFF.currentForm.inputName.push( this.IFF.reader.getString() );
				break;

			case 'IONM':
				if ( ! this.IFF.currentForm.inputOutputName ) this.IFF.currentForm.inputOutputName = [];
				this.IFF.currentForm.inputOutputName.push( this.IFF.reader.getString() );
				break;

			case 'FNAM':
				this.IFF.currentForm.fileName = this.IFF.reader.getString();
				break;

			case 'CHAN': // NOTE: ENVL Forms may also have CHAN chunk, however ENVL is currently ignored
				if ( length === 4 ) this.IFF.currentForm.textureChannel = this.IFF.reader.getIDTag();
				else this.IFF.reader.skip( length );
				break;

			// LWO2 Spec chunks: these are needed since the SURF FORMs are often in LWO2 format
			case 'SMAN':
				var maxSmoothingAngle = this.IFF.reader.getFloat32();
				this.IFF.currentSurface.attributes.smooth = ( maxSmoothingAngle < 0 ) ? false : true;
				break;

			// LWO2: Basic Surface Parameters
			case 'COLR':
				this.IFF.currentSurface.attributes.Color = { value: this.IFF.reader.getFloat32Array( 3 ) };
				this.IFF.reader.skip( 2 ); // VX: envelope
				break;

			case 'LUMI':
				this.IFF.currentSurface.attributes.Luminosity = { value: this.IFF.reader.getFloat32() };
				this.IFF.reader.skip( 2 );
				break;

			case 'SPEC':
				this.IFF.currentSurface.attributes.Specular = { value: this.IFF.reader.getFloat32() };
				this.IFF.reader.skip( 2 );
				break;

			case 'DIFF':
				this.IFF.currentSurface.attributes.Diffuse = { value: this.IFF.reader.getFloat32() };
				this.IFF.reader.skip( 2 );
				break;

			case 'REFL':
				this.IFF.currentSurface.attributes.Reflection = { value: this.IFF.reader.getFloat32() };
				this.IFF.reader.skip( 2 );
				break;

			case 'GLOS':
				this.IFF.currentSurface.attributes.Glossiness = { value: this.IFF.reader.getFloat32() };
				this.IFF.reader.skip( 2 );
				break;

			case 'TRAN':
				this.IFF.currentSurface.attributes.opacity = this.IFF.reader.getFloat32();
				this.IFF.reader.skip( 2 );
				break;

			case 'BUMP':
				this.IFF.currentSurface.attributes.bumpStrength = this.IFF.reader.getFloat32();
				this.IFF.reader.skip( 2 );
				break;

			case 'SIDE':
				this.IFF.currentSurface.attributes.side = this.IFF.reader.getUint16();
				break;

			case 'RIMG':
				this.IFF.currentSurface.attributes.reflectionMap = this.IFF.reader.getVariableLengthIndex();
				break;

			case 'RIND':
				this.IFF.currentSurface.attributes.refractiveIndex = this.IFF.reader.getFloat32();
				this.IFF.reader.skip( 2 );
				break;

			case 'TIMG':
				this.IFF.currentSurface.attributes.refractionMap = this.IFF.reader.getVariableLengthIndex();
				break;

			case 'IMAP':
				this.IFF.currentSurface.attributes.imageMapIndex = this.IFF.reader.getUint32();
				break;

			case 'IUVI': // uv channel name
				this.IFF.currentNode.UVChannel = this.IFF.reader.getString( length );
				break;

			case 'IUTL': // widthWrappingMode: 0 = Reset, 1 = Repeat, 2 = Mirror, 3 = Edge
				this.IFF.currentNode.widthWrappingMode = this.IFF.reader.getUint32();
				break;
			case 'IVTL': // heightWrappingMode
				this.IFF.currentNode.heightWrappingMode = this.IFF.reader.getUint32();
				break;

			default:
				this.IFF.parseUnknownCHUNK( blockID, length );

		}

		if ( blockID != 'FORM' ) {

			this.IFF.debugger.node = 1;
			this.IFF.debugger.nodeID = blockID;
			this.IFF.debugger.log();

		}

		if ( this.IFF.reader.offset >= this.IFF.currentFormEnd ) {

			this.IFF.currentForm = this.IFF.parentForm;

		}

	}

};

export { LWO3Parser };
