package zero1hd.rhythmbullet.audio.map;

import org.apache.commons.math3.random.MersenneTwister;

import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.utils.FloatArray;

import zero1hd.rhythmbullet.RhythmBullet;
import zero1hd.rhythmbullet.audio.AudioDataPackage;
import zero1hd.rhythmbullet.entity.EntityManager;
import zero1hd.rhythmbullet.entity.coordinator.CoordinatorManager;
import zero1hd.rhythmbullet.util.MiniEvents;
import zero1hd.rhythmbullet.util.MiniSender;

public class RhythmMapAlgorithm implements Runnable {
	private MiniSender sender;
	
	private EntityManager em;
	private CoordinatorManager cm;
	
	private FloatArray bassPeaks;
	private FloatArray mPeaks;
	private FloatArray umPeaks;
	private MersenneTwister rand;
	private GamePlayMap map;
	
	private float avgBass;
	private float avgUM;
	private float avgSPB;
	private float umMax, bassMax;
	
	private float speedMod, healthMod, difficultyMod;
	
	private float windowPerSecond;
	
	private volatile int progress;
	public RhythmMapAlgorithm(EntityManager em, CoordinatorManager cm, AudioDataPackage adp, float speedMod, float healthMod, float difficultyMod) {
		this.cm = cm;
		this.em = em;
		
		sender = new MiniSender();
		
		bassPeaks = adp.getBassPeaks();
		mPeaks = adp.getmPeaks();
		umPeaks = adp.getuMPeaks();
		map = new GamePlayMap(adp.getMusicInfo(), umPeaks.size);
		rand = new MersenneTwister(adp.getPUID());
		avgSPB = adp.getAvgSPB();
		windowPerSecond = 1/adp.getSecPerWin();
		
		this.speedMod = speedMod;
		this.healthMod = healthMod;
		this.difficultyMod = difficultyMod;
		
		umMax = adp.getuMMaxval();
		avgUM = adp.getuMAvg();

		bassMax = adp.getBassMaxVal();
		avgBass = adp.getBassAvg();
	}
	
	
	EntitySpawnInfo esi;
	@Override
	public void run() {
		map.beginBuild();
		for (int index = 0; index < bassPeaks.size; index++) {
			if (bassPeaks.get(index) != 0 || umPeaks.get(index) != 0) {
				if (bassPeaks.get(index) != 0) {
					//If there is a value of some sorts that is not zero, we generate some beat for the map
					if (index > 1.5*windowPerSecond && bassPeaks.get(index) > avgBass) {
						//If bass peak is greater than the bass peak average, then:
						int normalIndexPos = map.setIndex((int) (map.getIndex() - windowPerSecond*1.5f));
						float waitTime = 1.5f;
						float endRadius = (bassPeaks.get(index)/bassMax)*(RhythmBullet.GAME_AREA_HEIGHT/4f);
						
						esi = map.addEntity(em.voidCircle, null);
						esi.parameters.put("warningTime", waitTime);
						esi.parameters.put("endRadius", endRadius);
						esi.parameters.put("growthRate", endRadius/(avgSPB*0.8f));
						esi.parameters.put("x", rand.nextFloat()*RhythmBullet.GAME_AREA_WIDTH);
						esi.parameters.put("y", rand.nextFloat()*RhythmBullet.GAME_AREA_HEIGHT);
						
						map.setIndex(normalIndexPos);
					} else {
						
					}
				}
				
				if (umPeaks.get(index) != 0) {
					if (umPeaks.get(index) >= avgUM*1.75f) {
						//If upper midrange peaks are greater than average, the:						
						esi = map.addEntity(em.flake, null);
						
						float xSpawn = (rand.nextFloat()*RhythmBullet.GAME_AREA_WIDTH) < RhythmBullet.GAME_AREA_WIDTH/2f ? 0 : RhythmBullet.GAME_AREA_WIDTH;
						float ySpawn = (rand.nextFloat()*RhythmBullet.GAME_AREA_HEIGHT < RhythmBullet.GAME_AREA_HEIGHT/2f ? 0 : RhythmBullet.GAME_AREA_HEIGHT);
						
						float angle = rand.nextFloat()*90;
						if (xSpawn == 0) {
							if (ySpawn != 0) {
								angle += 270;
							}
						} else {
							if (ySpawn == 0) {
								angle += 90;
							} else {
								angle += 180;
							}
						}
						esi.parameters.put("shardCount", (1f+(difficultyMod/100f))*4f);
						esi.parameters.put("x", xSpawn);
						esi.parameters.put("y", ySpawn);
						esi.parameters.put("rate", (5/avgSPB)*speedMod);
						esi.parameters.put("fuse", avgSPB*10f);
						esi.parameters.put("angle", angle);
					} else {
						
					}
				}
				
				
				if (rand.nextFloat() < 0.15f) {
					switch(rand.nextInt(10)) {
					case 0:
						break;
					}
				}
			}
			map.nextWindowData();

			progress = MathUtils.round(100f*index/bassPeaks.size);
			
			if (bassPeaks.get(index) > avgBass) {
				map.setHUDType((byte) 1);
			} else if (umPeaks.get(index) > avgUM) {
				map.setHUDType((byte) 2);
			}
			
			sender.send(MiniEvents.MAPGEN_ITERATED);
		}
		map.endBuild();
		sender.send(MiniEvents.MAP_GENERATED);
	}
	
	public synchronized GamePlayMap getMap() {
		return map;
	}
	
	public synchronized int getProgress() {
		return progress;
	}
	
	public MiniSender getSender() {
		return sender;
	}
}
