package zero1hd.rhythmbullet;

import com.badlogic.gdx.Application;
import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.assets.loaders.ParticleEffectLoader;
import com.badlogic.gdx.assets.loaders.SoundLoader;
import com.badlogic.gdx.assets.loaders.TextureAtlasLoader;
import com.badlogic.gdx.assets.loaders.TextureLoader;
import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver;
import com.badlogic.gdx.assets.loaders.resolvers.ResolutionFileResolver.Resolution;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.ParticleEffect;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;

import zero1hd.rhythmbullet.util.AssetPack;
import zero1hd.rhythmbullet.util.GenericFileTypeHandler;
import zero1hd.rhythmbullet.util.InitialScreen;
import zero1hd.rhythmbullet.util.RoundingResolutionHandler;
import zero1hd.rhythmbullet.util.ScreenConfiguration;
import zero1hd.rhythmbullet.util.ResizeReadyScreen;


public class RhythmBullet extends Game {
	public static final int WORLD_WIDTH = 64;
	public static final int WORLD_HEIGHT = 48;
	public static final int SPAWN_CIRCLE_RADIUS = 6;
	public static int pixels_per_unit;
	private boolean initiated;
	private boolean resizing, simpleResizeOnce;
	private int screenWidth, screenHeight;
	public static final String VERSION = "(1.0.0) R1-PreAlpha";
	
	private AssetManager assetManager = new AssetManager();
	private Skin skin;
	private Preferences preferences;
	private RoundingResolutionHandler rRHandler;
	private InitialScreen initialScreen;
	private AssetPack assetPack;
	private ScreenConfiguration screenConfiguration;
	/**
	 * This should be called before passed to LWJGL. Setup for system-dependent items such as UI and assets.
	 * @param initialScreen the first screen to go to.
	 * @param assetPack the asset package to be used.
	 */
	public void setup(InitialScreen initialScreen, AssetPack assetPack, ScreenConfiguration screenConfiguration) {
		this.initialScreen = initialScreen;
		this.assetPack = assetPack;
		this.screenConfiguration = screenConfiguration;
		screenConfiguration.queueBorderless(true);
	}
	
	@Override
	public void create() {
		setScreen(initialScreen);
		Gdx.app.setLogLevel(Application.LOG_DEBUG);
		screenWidth = Gdx.graphics.getWidth();
		screenHeight = Gdx.graphics.getHeight();
		initialScreen.init();
		initialLoad();
	}
	
	private void initialLoad() {
		if (initiated) throw new IllegalStateException("Initiation cannot occur more than once.");
		
		simpleResizeOnce = true;
		skin = new Skin();
		assetPack.initiate();
		
		preferences = Gdx.app.getPreferences("RhythmBullet Preferences");
		
		Resolution[] resolution = {
				new Resolution(1280, 720, "1280x720"),
				new Resolution(1366, 768, "1366x768"),
				new Resolution(1280, 800, "1280x800"),
				new Resolution(1920, 1080, "1920x1080"),
				new Resolution(1920, 1200, "1920x1200"),
				new Resolution(2560, 1440, "2560x1440"),
				new Resolution(3840, 2160, "3840x2160")
				};
		
		InternalFileHandleResolver internalFileResolver = new InternalFileHandleResolver();
		rRHandler = new RoundingResolutionHandler(internalFileResolver, resolution);
		GenericFileTypeHandler genericFileFinder = new GenericFileTypeHandler(internalFileResolver);
		assetManager.setLoader(TextureAtlas.class, new TextureAtlasLoader(rRHandler));
		assetManager.setLoader(Texture.class, new TextureLoader(rRHandler));
		assetManager.setLoader(ParticleEffect.class, new ParticleEffectLoader(genericFileFinder));
		assetManager.setLoader(Sound.class, new SoundLoader(genericFileFinder));
		
		rRHandler.setResolution(getPreferences().getInteger("screen-width"), getPreferences().getInteger("screen-height"));
		queueAssets();
	}
	
	private void initialLoadComplete() {
		screenConfiguration.queueBorderless(preferences.getBoolean("borderless", false));

		skin.addRegions(assetManager.get("uiskin.atlas", TextureAtlas.class));
		pixels_per_unit = (int) (Float.valueOf(screenHeight)/Float.valueOf(WORLD_HEIGHT));
		if (getPreferences().getBoolean("fullscreen", true)) {
			Gdx.graphics.setFullscreenMode(Gdx.graphics.getDisplayMode());
		} else {
			Gdx.graphics.setWindowedMode(getPreferences().getInteger("screen-width"), getPreferences().getInteger("screen-height"));
		}
		assetPack.generateFonts(skin);
		assetPack.setupSkin(skin);
		assetPack.complete(assetManager);
		
	}
	
	@Override
	public void render() {
		checkAssetQueue();
		super.render();
	}
	
	public boolean checkAssetQueue() {
		if (assetManager.update()) {
			if (resizing) {
				Gdx.app.debug("Resize", "Post resize is starting...");
				if (skin != null) skin.dispose();
				skin = new Skin();
				skin.addRegions(assetManager.get("uiskin.atlas", TextureAtlas.class));
				
				assetPack.generateFonts(skin);
				assetPack.setupSkin(skin);
				assetPack.complete(assetManager);
				if (getScreen() instanceof ResizeReadyScreen) {
					((ResizeReadyScreen) getScreen()).postAssetLoad();
				} else {
					throw new IllegalStateException("Cannot perform window resize on a screen that isn't resize ready.");
				}
				Gdx.app.debug("Resize", "Post resize has ended.");
				resizing = false;
			} else if (!initiated) {
				initiated = true;
				initialLoadComplete();
				setScreen(((InitialScreen) initialScreen).advance(this));
			}
			return true;
		}
		return false;
	}
	
	
	@Override
	public void setScreen(Screen screen) {
		if (screen instanceof ResizeReadyScreen) {
			ResizeReadyScreen advancedResizeScreen = (ResizeReadyScreen) screen;
			try {
				advancedResizeScreen.preAssetLoad();
			} catch (NullPointerException cleanScreen) {
				Gdx.app.debug("Screen", "clean screen: " + advancedResizeScreen.getClass().getSimpleName());
				//Tried to perform pre-asset reload, but had uninitialized objects, meaning this is a new screen, or "clean" screen.
			} finally {
				advancedResizeScreen.postAssetLoad();
			}
		}
		super.setScreen(screen);
	}
	
	@Override
	public void resize(int width, int height) {
		if (screenWidth != width || screenHeight != height) {
			Gdx.app.debug("resize", "Current size:" + screenWidth + "x" + screenHeight + " new size: " + width + "x" + height);
			screenWidth = width;
			screenHeight = height;
			pixels_per_unit = (int) (Float.valueOf(screenHeight)/Float.valueOf(WORLD_HEIGHT));
			rRHandler.setResolution(width, height);
			preferences.putInteger("screen-width", width);
			preferences.putInteger("screen-height", height);
			preferences.flush();
			
			if (!simpleResizeOnce) {
				Gdx.app.debug("Resize", "complex pre-resize is happening. Resizing to " + width + "x" + height);
				if (getScreen() instanceof ResizeReadyScreen) {
					((ResizeReadyScreen) getScreen()).preAssetLoad();
				} else {
					throw new IllegalStateException("Cannot perform window resize on a screen that isn't using a resize ready screen.");
				}

				resizing = true;
				assetManager.clear();
				queueAssets();
			} else {
				simpleResizeOnce = false;
			}
			super.resize(width, height);
		}
	}
	
	public void queueAssets() {
		assetPack.queueTextures(assetManager);
		assetPack.queueSFX(assetManager);
		assetPack.queueParticles(assetManager);
	}
	
	public AssetManager getAssetManager() {
		return assetManager;
	}

	public Skin getSkin() {
		return skin;
	}

	public Preferences getPreferences() {
		return preferences;
	}

	@Override
	public void dispose() {
		Gdx.app.debug("Core", "disposing...");
		try {
			getScreen().dispose();
			getSkin().dispose();
			assetManager.dispose();
			assetPack.dispose();
		} catch (NullPointerException npe) {
			Gdx.app.debug("Core", "Disposal error occurred, possibly caused by failing to complete initialization.", npe);
		}
		super.dispose();
	}
	
	public ScreenConfiguration getScreenConfiguration() {
		return screenConfiguration;
	}
}
