content: package io.github.rowak.nanoleafdesktop.spotify;
  
  import com.wrapper.spotify.model_objects.miscellaneous.AudioAnalysis;
  import com.wrapper.spotify.model_objects.miscellaneous.AudioAnalysisMeasure;
  import com.wrapper.spotify.model_objects.miscellaneous.AudioAnalysisSection;
  import com.wrapper.spotify.model_objects.miscellaneous.AudioAnalysisSegment;
  
  public class SpecificAudioAnalysis {
  	
  	private int time;
  	private float sensitivity;
  	private AudioAnalysis analysis;
  	
  	public static SpecificAudioAnalysis getAnalysis(
  			AudioAnalysis analysis, int time, float sensitivity) {
  		final SpecificAudioAnalysis specificAnalysis =
  				new SpecificAudioAnalysis();
  		
  		specificAnalysis.analysis = analysis;
  		specificAnalysis.time = time;
  		specificAnalysis.sensitivity = sensitivity;
  		
  		return specificAnalysis;
  	}
  	
  	public int getTime() {
  		return time;
  	}
  	
  	public AudioAnalysisMeasure getBar() {
  		return getMeasureAtTime(analysis, analysis.getBars(), time,
  				sensitivity, 0, analysis.getBars().length-1);
  	}
  	
  	public AudioAnalysisMeasure getBeat() {
  		return getMeasureAtTime(analysis, analysis.getBeats(), time,
  				sensitivity, 0, analysis.getBeats().length-1);
  	}
  	
  	public AudioAnalysisMeasure getTatum() {
  		return getMeasureAtTime(analysis, analysis.getTatums(), time,
  				sensitivity, 0, analysis.getTatums().length-1);
  	}
  	
  	public AudioAnalysisSegment getSegment() {
  		return getSegmentInTimeRange(analysis, analysis.getSegments(), time,
  				sensitivity, 0, analysis.getSegments().length-1);
  	}
  	
  	public AudioAnalysisSection getSection() {
  		return getSectionInTimeRange(analysis, analysis.getSections(), time,
  				sensitivity, 0, analysis.getSections().length-1);
  	}
  	
  	private static AudioAnalysisMeasure getMeasureAtTime(AudioAnalysis analysis,
  			AudioAnalysisMeasure[] measures, int time, float sensitivity, int left, int right) {
  		if (right >= left) {
  			int middle = left+(right-left)/2;
  			double start = round(measures[middle].getStart(), 1);
  			double pos = round((float)time/1000, 1);
  			if (start == pos && isInSensitivityRange(
  					measures[middle].getConfidence(), sensitivity)) {
  				return measures[middle];
  			}
  			
  			if (start > pos) {
  				return getMeasureAtTime(analysis, measures, time, sensitivity, left, middle-1);
  			}
  			
  			return getMeasureAtTime(analysis, measures, time, sensitivity, middle+1, right);
  		}
  		return null;
  	}
  	
  	private static AudioAnalysisSection getSectionInTimeRange(AudioAnalysis analysis,
  			AudioAnalysisSection[] sections, int time, float sensitivity, int left, int right) {
  		if (right >= left) {
  			int middle = left+(right-left)/2;
  			AudioAnalysisMeasure measure = sections[middle].getMeasure();
  			double start = round(measure.getStart(), 1);
  			double end = start + round(measure.getDuration(), 1);
  			double pos = round((float)time/1000, 1);
  			if (start < pos && pos < end && isInSensitivityRange(
  					measure.getConfidence(), sensitivity)) {
  				return sections[middle];
  			}
  			
  			if (end > pos) {
  				return getSectionInTimeRange(analysis, sections, time,
  						sensitivity, left, middle-1);
  			}
  			
  			return getSectionInTimeRange(analysis, sections, time,
  					sensitivity, middle+1, right);
  		}
  		return null;
  	}
  	
  	private static AudioAnalysisSegment getSegmentInTimeRange(AudioAnalysis analysis,
  			AudioAnalysisSegment[] sections, int time, float sensitivity, int left, int right) {
  		if (right >= left) {
  			int middle = left+(right-left)/2;
  			AudioAnalysisMeasure measure = sections[middle].getMeasure();
  			double start = round(measure.getStart(), 1);
  			double end = start + round(measure.getDuration(), 1);
  			double pos = round((float)time/1000, 1);
  			if (start < pos && pos < end && isInSensitivityRange(
  					measure.getConfidence(), sensitivity)) {
  				return sections[middle];
  			}
  			
  			if (end > pos) {
  				return getSegmentInTimeRange(analysis, sections, time,
  						sensitivity, left, middle-1);
  			}
  			
  			return getSegmentInTimeRange(analysis, sections, time,
  					sensitivity, middle+1, right);
  		}
  		return null;
  	}
  	
  	private static double round(float num, int decimals) {
  		return (double)Math.round((double)num * 10d)/10d;
  	}
  	
  	private static boolean isInSensitivityRange(float confidence, float sensitivity) {
  		return confidence >= (float)(10-sensitivity)/10f;
  	}
  }
  