package com.mantz_it.guitartunerlibrary;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.Path;

/**
 * <h1>Wear Guitar Tuner - Default Tuner Skin</h1>
 *
 * Module:      DefaultTunerSkin.java
 * Description: This is a simple and very basic skin without any additional features.
 *
 * @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 DefaultTunerSkin extends TunerSkin {

	protected Paint gradientPaint;
	protected Paint needleFillPaint;
	protected float maxAngle = 0.8f; 			// max angle of the scale (measured from the midpoint in radian)
	protected float sideLettersPosition = 0.7f;	// position of the side pitch letters. 0 is the middle of the
											// screen and 1 is the left/right edge of the screen

	/**
	 * constructor
	 */
	public DefaultTunerSkin() {
		super();
		gradientPaint = new Paint();

		gradientPaint.setAntiAlias(true);
		needleFillPaint = new Paint(); // <-- ADD THIS
		needleFillPaint.setAntiAlias(true); // <-- ADD THIS

		// Make the outline thinner for a cleaner look
		foregroundPaint.setStrokeWidth(3f); // <-- ADD THIS
		highlightPaint.setStrokeWidth(3f);
		animationEnabled = true;	// this skin supports animation. The surface will call draw(Canvas, GuitarTuner, int, int)
	}

	@Override
	public void updateWidthAndHeight(int width, int height) {
		super.updateWidthAndHeight(width, height);

		// Set the new, larger text size for all paints
		float newTextSize = height * 0.16f;
		foregroundPaint.setTextSize(newTextSize);
		invalidPaint.setTextSize(newTextSize);
		highlightPaint.setTextSize(newTextSize);
		highlightPaint.setColor(Color.parseColor("#28C902"));
		gradientPaint.setTextSize(newTextSize);

		// Set the colors and styles

		foregroundPaint.setColor(Color.parseColor("#D32F2F"));
		gradientPaint.setShader(new LinearGradient(0, 0, width / 2, 0, Color.DKGRAY, Color.LTGRAY, Shader.TileMode.MIRROR));
		gradientPaint.setStrokeWidth(5f);
	}

	@Override
	public void setRound(boolean round) {
		super.setRound(round);
	}

	@Override
	public void draw(Canvas c, GuitarTuner tuner) {
		// This method is called for non-animated drawing.
		// We'll just call the main draw method and tell it it's the first and only frame.
		draw(c, tuner, 0, 1);
	}

	// In DefaultTunerSkin.java
// REPLACE the second, more complex draw method with this one.
	
// In DefaultTunerSkin.java
// REPLACE the second, more complex draw method with this one.

	@Override
	public void draw(Canvas c, GuitarTuner tuner, int frameNumber, int framesPerCycle) {

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

		// 2. Draw the tick marks for the scale.
		drawScale(c);

		// 3. Only draw the needle and text if the tuner has valid data.
		if(tuner.isValid() && tuner.getTargetFrequency() > 0) {
			float targetFrequency = tuner.getTargetFrequency();
			int targetPitchIndex = tuner.getTargetPitchIndex();

			// 4. Draw the pitch letter.
			String centerLetter = tuner.pitchLetterFromIndex(targetPitchIndex);
			// Use the correct paint color based on whether the note is tuned
			Paint letterPaint = tuner.isTuned() ? highlightPaint : foregroundPaint;
			drawPitchLetter(c, centerLetter, 0, 0.17f, true, letterPaint);


			// vvvvvv THIS IS THE NEW "NEEDLE LOCK" LOGIC vvvvvv
			float angle;
			final double TUNED_THRESHOLD_CENTS = 2.5; // How many cents (+/-) to consider "perfectly tuned"

			// --- MANUALLY CALCULATE CENTS DIFFERENCE ---
			// This replaces the getFrequencyDifferenceInCents() method that doesn't exist.
			double detectedFrequency = tuner.getDetectedFrequency();
			double diffInCents = 1200 * Math.log(detectedFrequency / targetFrequency) / Math.log(2);
			// --- END OF MANUAL CALCULATION ---

			// Check if the difference is within our "dead zone" threshold
			if (Math.abs(diffInCents) < TUNED_THRESHOLD_CENTS) {
				// The note is close enough. Lock the needle to the center.
				angle = 0;
			} else {
				// The note is audibly sharp or flat. Calculate the angle normally.
				// This maps a +/- 50 cent difference to the full range of the needle.
				double clampedDiff = Math.max(-50.0, Math.min(50.0, diffInCents));
				angle = (float) (clampedDiff / 50.0) * maxAngle;
			}
			// ^^^^^^ END OF THE NEW LOGIC ^^^^^^


			// 6. Draw the needle. Use highlight paint if tuned.
			drawNeedle(c, angle, tuner.isTuned() ? highlightPaint : foregroundPaint);

		} else {
			// If data is not valid, draw a "---" message.
			drawPitchLetter(c, "---", 0, 0.17f, true, invalidPaint);
		}
	}


	/**
	 * Draws one pitch letter on the canvas.
	 * @param c				canvas to draw
	 * @param letter		the letter (e.g. "a2#")
	 * @param xPosition		horizontal position relative to the middle of the screen (-1 is left edge; 1 is right edge)
	 * @param yPosition		vertical position relative to the top of the screen: 0 is top edge; 1 is bottom edge)
	 * @param round			if set to true, the xPosition will also affect the yPosition to arrange the letters
	 *                      into a circle and make the side letters smaller.
	 * @param paint			paint that should be used
	 */
	protected void drawPitchLetter(Canvas c, String letter, float xPosition, float yPosition, boolean round, Paint paint) {
		Rect bounds = new Rect();
		paint.getTextBounds(letter, 0, letter.length(), bounds);

		// Calculate the X position to center the text
		float x = (width - bounds.width()) / 2f;

		// Use the yPosition directly without extra calculations
		float y = height * (yPosition);

		c.drawText(letter, 0, letter.length(), x, y, paint);
	}

	/**
	 * Draws the scale (21 dashes) on the canvas
	 * @param c		canvas to draw
	 */
	/**
	 * Draws the scale on the canvas
	 * @param c		canvas to draw
	 */
	protected void drawScale(Canvas c) {
		// --- NEW, BOLDER SCALE LOGIC ---

		// 1. Define the scale's position and size
		float pivotX = width / 2f;
		float pivotY = height * (0.88f); // Same pivot as the needle
		float scaleRadius = height * 0.6f; // The radius of our scale arc

		// 2. Define the main center dash
		float centerAngle = 0; // Straight up
		float startX = pivotX + (float) Math.sin(centerAngle) * (scaleRadius - height * 0.05f);
		float startY = pivotY - (float) Math.cos(centerAngle) * (scaleRadius - height * 0.05f);
		float endX = pivotX + (float) Math.sin(centerAngle) * (scaleRadius + height * 0.05f);
		float endY = pivotY - (float) Math.cos(centerAngle) * (scaleRadius + height * 0.05f);
		c.drawLine(startX, startY, endX, endY, gradientPaint);

		// 3. Draw the side dashes (fewer but bigger)
		int numSideDashes = 5; // We'll draw 5 dashes on each side
		for (int i = 1; i <= numSideDashes; i++) {
			// Determine the length of the dash. Make the last one longer.
			float dashLength = (i == numSideDashes) ? height * 0.05f : height * 0.025f;

			// Calculate the angle for the current dash on both sides
			float positiveAngle = (maxAngle * i / numSideDashes);
			float negativeAngle = -positiveAngle;

			// Calculate and draw the right-side dash
			startX = pivotX + (float) Math.sin(positiveAngle) * (scaleRadius - dashLength);
			startY = pivotY - (float) Math.cos(positiveAngle) * (scaleRadius - dashLength);
			endX = pivotX + (float) Math.sin(positiveAngle) * (scaleRadius + dashLength);
			endY = pivotY - (float) Math.cos(positiveAngle) * (scaleRadius + dashLength);
			c.drawLine(startX, startY, endX, endY, gradientPaint);

			// Calculate and draw the left-side dash
			startX = pivotX + (float) Math.sin(negativeAngle) * (scaleRadius - dashLength);
			startY = pivotY - (float) Math.cos(negativeAngle) * (scaleRadius - dashLength);
			endX = pivotX + (float) Math.sin(negativeAngle) * (scaleRadius + dashLength);
			endY = pivotY - (float) Math.cos(negativeAngle) * (scaleRadius + dashLength);
			c.drawLine(startX, startY, endX, endY, gradientPaint);
		}
	}

	/**
	 * Draws the needle on the screen at the given angle
	 * @param c			canvas to draw
	 * @param angle		angle in radian. 0 will result in a straight vertical needle.
	 * @param paint		paint that should be used
	 */
	/**
	 * Draws the needle on the screen at the given angle
	 * @param c			canvas to draw
	 * @param angle		angle in radian. 0 will result in a straight vertical needle.
	 * @param paint		paint that should be used for the outline of the needle.
	 */
	protected void drawNeedle(Canvas c, float angle, Paint paint) {
		// --- NEW SPEEDOMETER NEEDLE LOGIC ---

		// 1. Define the needle's dimensions
		float needleLength = height * 0.55f; // How long the needle is
		float needleBaseWidth = width * 0.05f; // How wide the base of the needle is

		// 2. Define the pivot point (the center of the base)
		float pivotX = width / 2f;
		float pivotY = height * (0.88f);

		// 3. Calculate the coordinates of the needle's tip
		float tipX = pivotX + (float) Math.sin(angle) * needleLength;
		float tipY = pivotY - (float) Math.cos(angle) * needleLength;

		// 4. Calculate the coordinates of the two base corners
		// These points are perpendicular to the needle's angle
		float baseCornerX1 = pivotX - (float) Math.cos(angle) * needleBaseWidth / 2;
		float baseCornerY1 = pivotY - (float) Math.sin(angle) * needleBaseWidth / 2;
		float baseCornerX2 = pivotX + (float) Math.cos(angle) * needleBaseWidth / 2;
		float baseCornerY2 = pivotY + (float) Math.sin(angle) * needleBaseWidth / 2;

		// 5. Create the path for our needle shape
		Path needlePath = new Path();
		needlePath.moveTo(tipX, tipY); // Move to the tip
		needlePath.lineTo(baseCornerX1, baseCornerY1); // Draw line to the first base corner
		needlePath.lineTo(baseCornerX2, baseCornerY2); // Draw line to the second base corner
		needlePath.close(); // Close the path to complete the triangle

		// 6. Set the fill color based on the outline color
		// This makes it automatically turn green when tuned!
		needleFillPaint.setColor(paint.getColor());
		needleFillPaint.setAlpha(100); // Make the fill slightly transparent (0-255)

		// 7. Draw the filled shape and then its outline
		c.drawPath(needlePath, needleFillPaint); // Draw the transparent fill
		c.drawPath(needlePath, paint); // Draw the solid outline

		// 8. Draw a circle at the pivot point to cover the base
		c.drawCircle(pivotX, pivotY, needleBaseWidth / 1.5f, paint);
	}
}
