class WaveModule extends ModuleBase {

	@Override
	public int parse(InputStream stream, RepInfo info, int parseIndex) {
		initParse();
		info.setModule(this);

		_aesMetadata.setPrimaryIdentifier(info.getUri());
		if (info.getURLFlag()) {
			_aesMetadata.setOtherPrimaryIdentifierType("URI");
		} else {
			_aesMetadata.setPrimaryIdentifierType(AESAudioMetadata.FILE_NAME);
		}

		// We may have already done the checksums
		// while converting a temporary file.
		setupDataStream(stream, info);

		try {
			// Check the start of the file for the right opening bytes
			String firstFourChars = read4Chars(_dstream);
			if (firstFourChars.equals(RF64_SIGNATURE)) {
				info.setProfile("RF64");
				flagRF64 = true;
			} else if (!firstFourChars.equals(RIFF_SIGNATURE)) {
				info.setMessage(
						new ErrorMessage(MessageConstants.WAVE_HUL_1, 0));
				info.setWellFormed(false);
				return 0;
			}

			// Get the length of the Form chunk. This includes all
			// subsequent form fields and form subchunks, but excludes
			// the form chunk's header (its ID and the its length).
			riffSize = readUnsignedInt(_dstream);

			// Read the RIFF form type
			String formType = read4Chars(_dstream);
			if (!"WAVE".equals(formType)) {
				info.setMessage(new ErrorMessage(
						MessageConstants.WAVE_HUL_2,
						_nByte - RIFF_FORM_TYPE_LENGTH));
				info.setWellFormed(false);
				return 0;
			}

			// If we get this far, the signature is OK.
			info.setSigMatch(_name);
			info.setFormat(_format[0]);
			info.setMimeType(_mimeType[0]);

			if (flagRF64) {
				// For RF64 files the first chunk should be a Data Size 64 chunk
				// containing the extended data sizes for a number of elements.
				if (readChunk(info) && dataSize64ChunkSeen) {
					if (riffSize == LOOKUP_EXTENDED_DATA_SIZE) {
						// Even though RF64 can support files larger than
						// Long.MAX_VALUE, this module currently does not.
						if (Long.compareUnsigned(extendedRiffSize,
								Long.MAX_VALUE) > 0) {
							info.setMessage(new InfoMessage(
									MessageConstants.WAVE_HUL_22));
							info.setWellFormed(RepInfo.UNDETERMINED);
							return 0;
						}
					}
				} else {
					info.setMessage(new ErrorMessage(
							MessageConstants.WAVE_HUL_23,
							Chunk.HEADER_LENGTH + RIFF_FORM_TYPE_LENGTH));
					info.setWellFormed(false);
					return 0;
				}
			}

			while (getBytesRemaining() > 0) {
				if (!readChunk(info)) {
					break;
				}
			}

			if (getBytesRemaining() > 0) {
				// The file has been truncated or there
				// remains unexpected chunk data to skip
				remainingDataInfo(_dstream, info, getBytesRemaining(),
						firstFourChars);
			}

		} catch (EOFException eofe) {
			info.setWellFormed(false);
			String subMessage = String.format(
					MessageConstants.WAVE_HUL_3_SUB.getMessage(),
					getBytesRemaining());
			if (eofe.getMessage() != null) {
				subMessage += "; " + eofe.getMessage();
			}
			info.setMessage(new ErrorMessage(MessageConstants.WAVE_HUL_3,
					subMessage, _nByte));
		} catch (Exception e) { // TODO make this more specific
			e.printStackTrace();
			info.setWellFormed(false);
			JhoveMessage message = JhoveMessages.getMessageInstance(
					MessageConstants.WAVE_HUL_4.getId(),
					String.format(MessageConstants.WAVE_HUL_4.getMessage(),
							e.getClass().getName() + ", " + e.getMessage()));
			info.setMessage(new ErrorMessage(message, _nByte));
			return 0;
		}

		// Set duration from number of samples and rate.
		if (sampleCount > 0) {
			_aesMetadata.setDuration(sampleCount);
		}

		// Add note and label properties, if there's anything to report.
		if (!_labels.isEmpty()) {
			_propList.add(new Property("Labels", PropertyType.PROPERTY,
					PropertyArity.LIST, _labels));
		}
		if (!_labeledText.isEmpty()) {
			_propList.add(new Property("LabeledText", PropertyType.PROPERTY,
					PropertyArity.LIST, _labeledText));
		}
		if (!_notes.isEmpty()) {
			_propList.add(new Property("Notes", PropertyType.PROPERTY,
					PropertyArity.LIST, _notes));
		}
		if (!_samples.isEmpty()) {
			_propList.add(new Property("Samples", PropertyType.PROPERTY,
					PropertyArity.LIST, _samples));
		}
		if (_exifInfo != null) {
			_propList.add(_exifInfo.buildProperty());
		}

		if (!formatChunkSeen) {
			info.setMessage(new ErrorMessage(MessageConstants.WAVE_HUL_5));
			info.setWellFormed(false);
			return 0;
		}
		if (!dataChunkSeen) {
			info.setMessage(new ErrorMessage(MessageConstants.WAVE_HUL_24));
			info.setWellFormed(false);
			return 0;
		}

		// This file looks OK.
		if (_ckSummer != null) {
			skipDstreamToEnd(info);
			// Set the checksums in the report if they're calculated
			setChecksums(this._ckSummer, info);
		}

		info.setProperty(_metadata);

		// Indicate satisfied profiles.
		if (flagPCMWaveFormat) {
			info.setProfile("PCMWAVEFORMAT");
		}
		if (flagWaveFormatEx) {
			info.setProfile("WAVEFORMATEX");
		}
		if (flagWaveFormatExtensible) {
			info.setProfile("WAVEFORMATEXTENSIBLE");
		}
		if (broadcastExtChunkSeen && (
                        (waveCodec == FormatChunk.WAVE_FORMAT_MPEG && factChunkSeen)
                        || waveCodec == FormatChunk.WAVE_FORMAT_PCM)) {
                    info.setProfile("BWF");
		}
                return 0;
	}

	public  WaveModule();

}