package zero1hd.wavedecoder;

import java.io.DataInputStream;
import java.io.IOException;
import java.security.InvalidParameterException;

import com.badlogic.gdx.files.FileHandle;

public class WavDecoder {
	private FileHandle file;
	
	private int channels;
	private double sampleRate;
	private int dataSize;
	private int byteRate;
	private DataInputStream readStream;
	private String fileName;
	
	public WavDecoder(FileHandle file) throws IOException {
		this.file = file;
		initDataStream();
		getHeaderInfo();
	}
	
	public void getHeaderInfo() throws IOException {
		if (!readBytesToString(4).equals("RIFF")) { //4 for RIFF tag
			throw new InvalidParameterException("RIFF tag not found in header.");
		}
		dataSize = littleEndianIntBytes(); //4 for Chunk size

		if (!readBytesToString(4).equals("WAVE")) { //4 for WAVE tag
			throw new InvalidParameterException("WAVE format tag not found.");
		}
		
		if (!readBytesToString(4).equals("fmt ")) { //4 for 'fmt '
			throw new InvalidParameterException("fmt header not found.");
		}
		
		if (littleEndianIntBytes() != 16) { //subchunk1size (4 bytes)
			throw new InvalidParameterException("Data not pcm?");
		}
		
		if (readStream.readByte() != 1) { //1 pcm
			throw new InvalidParameterException("Data not pcm?");
		}
		readStream.skip(1); //1

		channels = readStream.readByte(); //1  channel count
		readStream.skip(1); //1 

		sampleRate = littleEndianIntBytes(); //4 sample rate
		
		byteRate = littleEndianIntBytes(); //4
		
		readStream.skip(4);
		
		String moreInfo = readBytesToString(4);
		if (moreInfo.equals("LIST")) { // 4
			readStream.skip(30);
			if (!readBytesToString(4).equals("data")) {
				throw new InvalidParameterException("failed to read data with extra info.");
			}
		} else if (!moreInfo.equals("data")) {
			throw new InvalidParameterException("failed to read data.");
		}
			
		readStream.skip(4);
	}
	
	public void initDataStream() {
		readStream = new DataInputStream(file.read());
	}
	
	public void closeStreams() throws IOException {
		readStream.close();
	}
	
	public String readBytesToString(int bytesToRead) throws IOException {
		byte byteString[] = new byte[bytesToRead];
		readStream.read(byteString);
		return new String(byteString);
	}

	public int littleEndianIntBytes() throws IOException {
		int data = readStream.readInt();
		return Integer.reverseBytes(data);
	}
	
	public short readLittleEndianShort() throws IOException {
		short data = readStream.readShort();
		return Short.reverseBytes(data);
	}
	
	public DataInputStream getReadStream() {
		return readStream;
	}
	
	public int getChannels() {
		return channels;
	}
	
	public int getByteRate() {
		return byteRate;
	}
	
	public int getDataSize() {
		return dataSize;
	}
	
	public double getSampleRate() {
		return sampleRate;
	}
	
	public long getDurationInSeconds() {
		return (long) (dataSize/byteRate);
	}
	
	public String getFileName() {
		return fileName;
	}
	
	public FileHandle getFile() {
		return file;
	}
	
	public int readSamples(float[] samples) {
		int samplesRead = 0;
		
		for (int i = 0; i < samples.length; i++) {
			try {
				float currentSample = 0;
				for (int channel = 0; channel < getChannels(); channel++) {
					currentSample += readLittleEndianShort();
				}
				currentSample /= getChannels()*Short.MAX_VALUE+1;
				samples[i] = currentSample;
				samplesRead++;
			} catch (IOException e) {
				try {
					closeStreams();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
				break;
			}
		}
		
		return samplesRead;
	}
	
	public void cleanAndClose() {
		try {
			readStream.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
