package dk.itu.mario.scene;

import java.awt.GraphicsConfiguration;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;

import dk.itu.mario.MarioInterface.GamePlay;
import dk.itu.mario.MarioInterface.LevelGenerator;
import dk.itu.mario.engine.Art;
import dk.itu.mario.engine.BgRenderer;
import dk.itu.mario.engine.DataRecorder;
import dk.itu.mario.engine.LevelRenderer;
import dk.itu.mario.engine.MarioComponent;
import dk.itu.mario.engine.sonar.FixedSoundSource;
import dk.itu.mario.engine.sprites.CoinAnim;
import dk.itu.mario.engine.sprites.FireFlower;
import dk.itu.mario.engine.sprites.Mario;
import dk.itu.mario.engine.sprites.Mushroom;
import dk.itu.mario.engine.sprites.Particle;
import dk.itu.mario.engine.sprites.Sprite;
import dk.itu.mario.engine.util.FileHandler;
import dk.itu.mario.level.BgLevelGenerator;
import dk.itu.mario.level.Level;
import dk.itu.mario.level.RandomLevel;

public class LevelSceneCustom extends LevelScene {
	private boolean isCustom;
	private LevelGenerator clg;

	public LevelSceneCustom(GraphicsConfiguration graphicsConfiguration,
			MarioComponent renderer, long seed, int levelDifficulty, int type,
			boolean isCustom, LevelGenerator levelGenerator) {
		super(graphicsConfiguration, renderer, seed/*, levelDifficulty, type*/);
		this.isCustom = isCustom;
		this.clg = levelGenerator;
	}

	public void init() {
		try {
			Level.loadBehaviors(new DataInputStream(LevelSceneCustom.class
					.getResourceAsStream("/res/tiles.dat")));
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(0);
		}

		GamePlay gp = GamePlay.read("player.txt");
		File xmlInfoFile = new File("DetailedInfo.xml");
		DataRecorder dataRecorder;
		if (xmlInfoFile.exists()) {
			try {
				dataRecorder = DataRecorder.fromXML(xmlInfoFile);
			} catch (IOException ioe) {
				System.out.println("Reading from DataRecorder failed due to: ");
				ioe.printStackTrace(System.out);
				System.out.println("Initializing blank record");
				dataRecorder = DataRecorder.BLANK_RECORD;
			}
		} else {
			dataRecorder = DataRecorder.BLANK_RECORD;;
			String detailedInfo = FileHandler.readFile("DetailedInfo.txt");
			System.out.println("Read detailedInfo from DetailedInfo.txt (not parsed): ");
			System.out.println(detailedInfo);
		}
		
		int levelType = clg.generateLevelType(gp,dataRecorder);
		currentLevel = (Level) clg.generateLevel(gp,dataRecorder);

		try {
			level = currentLevel.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}

		Art.startMusic(levelType);

		paused = false;
		Sprite.spriteContext = this;
		sprites.clear();

		layer = new LevelRenderer(level, graphicsConfiguration, 320, 240);
		for (int i = 0; i < 2; i++) {
			int scrollSpeed = 4 >> i;
			int w = ((level.getWidth() * 16) - 320) / scrollSpeed + 320;
			int h = ((level.getHeight() * 16) - 240) / scrollSpeed + 240;
			Level bgLevel = BgLevelGenerator.createLevel(w / 32 + 1,
					h / 32 + 1, i == 0, levelType);
			bgLayer[i] = new BgRenderer(bgLevel, graphicsConfiguration, 320,
					240, scrollSpeed);
		}

		mario = new Mario(this);
		sprites.add(mario);
		startTime = 1;

		timeLeft = 200 * 15;

		tick = 0;

		if (!isCustom && recorder == null)
			recorder = new DataRecorder(this, (RandomLevel) level, keys);

		gameStarted = false;
	}

	public void tick() {
		super.tick();

		if (recorder != null && !gameStarted) {
			recorder.startLittleRecord();
			recorder.startTime();
			gameStarted = true;
		}
		if (recorder != null)
			recorder.tickRecord();
	}

	public void winActions() {
		if (recorder != null)
			recorder.fillGamePlayMetrics((RandomLevel) level);

		marioComponent.win();
	}

	public void deathActions() {
		if (Mario.lives <= 0) {// has no more lives
			if (recorder != null)
				recorder.fillGamePlayMetrics((RandomLevel) level);
			marioComponent.lose();
		} else
			// mario still has lives to play :)--> have a new beginning
			reset();
	}

	public void bump(int x, int y, boolean canBreakBricks) {
		byte block = level.getBlock(x, y);

		if ((Level.TILE_BEHAVIORS[block & 0xff] & Level.BIT_BUMPABLE) > 0) {
			bumpInto(x, y - 1);
			level.setBlock(x, y, (byte) 4);

			if (((Level.TILE_BEHAVIORS[block & 0xff]) & Level.BIT_SPECIAL) > 0) {
				sound.play(Art.samples[Art.SAMPLE_ITEM_SPROUT],
						new FixedSoundSource(x * 16 + 8, y * 16 + 8), 1, 1, 1);
				if (!Mario.large) {
					addSprite(new Mushroom(this, x * 16 + 8, y * 16 + 8));
				} else {
					addSprite(new FireFlower(this, x * 16 + 8, y * 16 + 8));
				}

				if (recorder != null) {
					recorder.blockPowerDestroyRecord();
				}
			} else {
				// TODO should only record hidden coins (in boxes)
				if (recorder != null) {
					recorder.blockCoinDestroyRecord();
				}

				Mario.getCoin();
				sound.play(Art.samples[Art.SAMPLE_GET_COIN],
						new FixedSoundSource(x * 16 + 8, y * 16 + 8), 1, 1, 1);
				addSprite(new CoinAnim(x, y));
			}
		}

		if ((Level.TILE_BEHAVIORS[block & 0xff] & Level.BIT_BREAKABLE) > 0) {
			bumpInto(x, y - 1);
			if (canBreakBricks) {
				if (recorder != null) {
					recorder.blockEmptyDestroyRecord();
				}

				sound.play(Art.samples[Art.SAMPLE_BREAK_BLOCK],
						new FixedSoundSource(x * 16 + 8, y * 16 + 8), 1, 1, 1);
				level.setBlock(x, y, (byte) 0);
				for (int xx = 0; xx < 2; xx++)
					for (int yy = 0; yy < 2; yy++)
						addSprite(new Particle(x * 16 + xx * 8 + 4, y * 16 + yy
								* 8 + 4, (xx * 2 - 1) * 4, (yy * 2 - 1) * 4 - 8));
			}

		}
	}

	public void bumpInto(int x, int y) {
		byte block = level.getBlock(x, y);
		if (((Level.TILE_BEHAVIORS[block & 0xff]) & Level.BIT_PICKUPABLE) > 0) {
			Mario.getCoin();
			sound.play(Art.samples[Art.SAMPLE_GET_COIN], new FixedSoundSource(
					x * 16 + 8, y * 16 + 8), 1, 1, 1);
			level.setBlock(x, y, (byte) 0);
			addSprite(new CoinAnim(x, y + 1));

			// TODO no idea when this happens... maybe remove coin count
			if (recorder != null)
				recorder.recordCoin();
		}

		for (Sprite sprite : sprites) {
			sprite.bumpCheck(x, y);
		}
	}

}