package com.mantz_it.guitartunerlibrary;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;

import java.text.DecimalFormat;

/**
 * <h1>Wear Guitar Tuner - Debug Tuner Skin</h1>
 *
 * Module:      DebugTunerSkin.java
 * Description: This skin is used for debugging and testing. It shows the raw fft and HPS data.
 *
 * @author Dennis Mantz
 *
 * Copyright (C) 2014 Dennis Mantz
 * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
public class DebugTunerSkin extends TunerSkin {

	protected Paint fftPaint;

	@Override
	public void updateWidthAndHeight(int width, int height) {
		super.updateWidthAndHeight(width, height);
		fftPaint = new Paint();
		// The main FFT wave will be the new, better red.
		fftPaint.setColor(Color.parseColor("#D32F2F"));
		// The HPS (more accurate) wave will be your brand green.
		highlightPaint.setColor(Color.parseColor("#28C902"));
		foregroundPaint.setTextSize(height*0.1f);
		invalidPaint.setTextSize(height*0.1f);
	}

	@Override
	public void draw(Canvas c, GuitarTuner tuner) {
		// narrow to the range: 50Hz-500Hz:
		int startFrequency = 50;
		int endFrequency = 500;
		int startIndex = (int) (startFrequency / tuner.getHzPerSample());
		int endIndex = (int) (endFrequency / tuner.getHzPerSample());
		float samplesPerPx = (float) (endIndex - startIndex) / (float) width;        // number of fft samples per one pixel
		float hzPerPx = tuner.getHzPerSample() * samplesPerPx;    // frequency span (in Hz) of one pixel

		// Clear the canvas
		c.drawRect(0, 0, width, height, backgroundPaint);

		// --- START: FINAL LOGIC ---
		// ALWAYS draw the spectrums with their main colors.
		// We are now IGNORING tuner.isValid() for the wave colors
		// so we can always see the live microphone input.
		drawSpectrum(c, fftPaint, tuner.getMag(), startIndex, endIndex, -9f, -2f, tuner.getHzPerSample());
		drawSpectrum(c, highlightPaint, tuner.getHPS(), startIndex, endIndex, -35f, -15f, tuner.getHzPerSample());

		// BUT, we still only draw the analysis (lines, text) if the tuner has a valid, stable note.
		// This prevents incorrect labels from appearing during silence.
		if (tuner.isValid() && tuner.getDetectedFrequency() > 0) {
			float detectedFrequency = tuner.getDetectedFrequency();
			int pitchIndex = tuner.getTargetPitchIndex();
			Paint paint = foregroundPaint; // Use the standard foreground paint for the text

			// draw a line at the detected pitch:
			int frequencyPosition = (int) ((detectedFrequency - startFrequency) / hzPerPx);
			c.drawLine(frequencyPosition, 0, frequencyPosition, height, paint);

			// draw frequency (in hz)
			float yPos = height * 0.3f;
			String text = new DecimalFormat("###.# Hz").format(detectedFrequency);
			Rect bounds = new Rect();
			paint.getTextBounds(text, 0, text.length(), bounds);
			int labelPosition = frequencyPosition <= width / 2 ? frequencyPosition + 5 : frequencyPosition - bounds.width() - 5;
			c.drawText(text, 0, text.length(), labelPosition, yPos, paint);

			// draw pitch in letters
			yPos += bounds.height() * 1.1f;
			text = tuner.pitchLetterFromIndex(pitchIndex) + new DecimalFormat(" (###.# Hz)").format(tuner.pitchIndexToFrequency(pitchIndex));
			paint.getTextBounds(text, 0, text.length(), bounds);
			labelPosition = frequencyPosition <= width / 2 ? frequencyPosition + 5 : frequencyPosition - bounds.width() - 5;
			c.drawText(text, 0, text.length(), labelPosition, yPos, paint);
		}
	}

	/**
	 * Draws the given samples as spectrum (fft) on the canvas.
	 *
	 * @param c				canvas to draw on
	 * @param paint			paint instance that will be used for drawing
	 * @param values		sample values (fft data)
	 * @param start			first index in values that should be drawn
	 * @param end			last index in values that should be drawn
	 * @param minDB			lowest dB value on the vertical scale
	 * @param maxDB			highest dB value on the vertical scale
	 * @param hzPerSample	width (in Hz) of one FFT bin (one index) in the values array
	 */
	private void drawSpectrum(Canvas c, Paint paint, float[] values, int start, int end, float minDB, float maxDB, float hzPerSample) {
		float previousY		 = height;	// y coordinate of the previously processed pixel
		float currentY;					// y coordinate of the currently processed pixel
		float samplesPerPx 	= (float) (end - start) / (float) width;		// number of fft samples per one pixel
		float dbDiff 		= maxDB - minDB;
		float dbWidth 		= height / dbDiff; 	// Size (in pixel) per 1dB in the fft
		float avg;				// Used to calculate the average of multiple values in mag (horizontal average)
		int counter;			// Used to calculate the average of multiple values in mag

		// Draw FFT pixel by pixel:
		// We start at 1 because of integer round off error
		for (int i = 1; i < width; i++) {
			// Calculate the average value for this pixel (horizontal average - not the time domain average):
			avg = 0;
			counter = 0;
			for (int j = (int)(i*samplesPerPx); j < (i+1)*samplesPerPx; j++) {
				avg += values[j + start];
				counter++;
			}
			avg = avg / counter;

			// FFT:
			currentY = height - (avg - minDB) * dbWidth;
			if(currentY < 0 )
				currentY = 0;
			if(currentY > height)
				currentY = height;

			c.drawLine(i-1,previousY,i,currentY, paint);
			previousY = currentY;

			// We have to draw the last line to the bottom if we're in the last round:
			if(i+1 == width)
				c.drawLine(i,previousY,i+1,height, paint);
		}
	}
}
