package leigh.audio.bpm;

import ddf.minim.analysis.BeatDetect;
import leigh.mecha.log.MechaLogger;
import leigh.mecha.log.MechaLoggerFactory;
import leigh.mecha.smpte.Timecode;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

/**
 * Wrapper for the Minim beat detector implementation.
 * <p>
 * This implementation is intended to be used with 44.1khz mono WAV files.
 *
 * @author Alex Leigh
 */
public class MinimBeatDetector {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(MinimBeatDetector.class);

    public static ArrayList<Timecode> detect(File f) throws IOException, WavFileException {
        ArrayList<Timecode> beats = new ArrayList<>();
        int bufferSz = 256;

        WavFile wavFile = WavFile.openWavFile(f);
        long sampleRate = wavFile.getSampleRate();
        logger.info("WAV: [frames: {}] [sampleRate: {}]",
                wavFile.getNumFrames(),
                sampleRate);

        BeatDetect bd = new BeatDetect(bufferSz, wavFile.getSampleRate());
        //BeatDetect bd = new BeatDetect();
        //   bd.detectMode(BeatDetect.SOUND_ENERGY);
        // bd.setSensitivity(15);
        logger.info("Beat Detector. [timeSize: {}]");

        // Get the number of audio channels in the wav file
        int numChannels = wavFile.getNumChannels();

        if (numChannels != 1) {
            logger.error("File must be mono.");
            System.exit(0);
        }

        double[] buffer = new double[bufferSz];

        int framesRead;
        int sampleCounter = 0;

        do {
            // Read frames into buffer
            framesRead = wavFile.readFrames(buffer, bufferSz);
            float[] floatArray = new float[buffer.length];
            for (int i = 0; i < buffer.length; i++) {
                floatArray[i] = (float) buffer[i];
            }

            bd.detect(floatArray);

            if (bd.isOnset() || bd.isSnare() || bd.isKick()) {
                double tc = (double) sampleCounter / sampleRate;
                Timecode t = new Timecode((float) tc, Timecode.Type.TYPE_VIDEO_FILM);
                logger.info("Beat onset detected. [time: {}]", t.getCode());
                beats.add(t);
            }
            sampleCounter += buffer.length;
        }
        while (framesRead != 0);

        // Close the wavFile
        wavFile.close();

        return beats;

    }
}
