/*
 * =================================================
 * Copyright 2011 tagtraum industries incorporated
 * All rights reserved.
 * =================================================
 */
package com.tagtraum.jipes.audio;

import com.tagtraum.jipes.AbstractSignalProcessor;
import com.tagtraum.jipes.math.MultirateFilters;

import javax.sound.sampled.AudioFormat;
import java.io.IOException;

/**
 * Resamples mono audio data.
 * Note that not all conversions are supported.
 *
 * @author <a href="mailto:[email protected]">Hendrik Schreiber</a>
 * @see MultirateFilters.Resampler
 */
public class Resample extends AbstractSignalProcessor<AudioBuffer, AudioBuffer> {

    private RealAudioBuffer realAudioBuffer;
    private MultirateFilters.Resampler resampler;

    /**
     *
     * @param upFactor upsample factor
     * @param downFactor downsample factor
     * @throws IllegalArgumentException if the resample factor is not supported
     */
    public Resample(final int upFactor, final int downFactor) {
        setFactor(upFactor, downFactor);
    }

    /**
     *
     * @param upFactor up factor
     * @param downFactor down factor
     * @throws IllegalArgumentException if the resample factor is not supported
     */
    public void setFactor(final int upFactor, final int downFactor) throws IllegalArgumentException {
        if (upFactor < 1) throw new IllegalArgumentException("Up factor must be greater than 0: " + upFactor);
        if (downFactor < 1) throw new IllegalArgumentException("Down factor must be greater than 0: " + downFactor);
        this.resampler = new MultirateFilters.Resampler(upFactor, downFactor);
    }

    public float getFactor() {
        return resampler.getFactor();
    }

    private AudioFormat getProcessedAudioFormat(final AudioBuffer buffer) {
        final AudioFormat sourceAudioFormat = buffer.getAudioFormat();
        return new AudioFormat(sourceAudioFormat.getSampleRate() * resampler.getFactor(),
                sourceAudioFormat.getSampleSizeInBits(),
                sourceAudioFormat.getChannels(),
                AudioFormat.Encoding.PCM_SIGNED.equals(sourceAudioFormat.getEncoding()),
                sourceAudioFormat.isBigEndian());
    }

    @Override
    protected AudioBuffer processNext(final AudioBuffer buffer) throws IOException {
        if (buffer.getAudioFormat().getChannels() != 1) throw new IOException("Resample only supports single channel buffers. Actual audio format: " + buffer.getAudioFormat());
        final float[] floats = buffer.getData();
        final float[] resampled = resampler.map(floats);
        if (realAudioBuffer == null) {
            realAudioBuffer = new RealAudioBuffer((int) (buffer.getFrameNumber() * getFactor()), resampled, getProcessedAudioFormat(buffer));
        } else {
            realAudioBuffer.reuse((int)(buffer.getFrameNumber() * getFactor()), resampled, realAudioBuffer.getAudioFormat());
        }
        return realAudioBuffer;
    }

    @Override
    public boolean equals(final Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Resample that = (Resample) o;

        if (resampler.getFactor() != that.resampler.getFactor()) return false;

        return true;
    }

    @Override
    public int hashCode() {
        return resampler.getUpFactor() ^ resampler.getDownFactor() * 31;
    }

    @Override
    public String toString() {
        return "Resample{" +
                "factor=" + resampler.getUpFactor() + "/" + resampler.getDownFactor() +
                '}';
    }
}