import java.awt.*;
import java.util.HashMap;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import java.util.Random;

/**
 @author Justin Mobijohn and Zander
 * This class the main class of this application.  It creates a HashMap of
 * GameObjects, and basically calls Update and Draw for each element in the
 * HashMap.
 */
public class SpaceInvaders extends Canvas implements KeyListener {
    // The height and width have to be public, so that the collision
    // detection of the Missile class can determine if the missile when outside
    // our window:

    public static final int WIDTH = 800;
    public static final int HEIGHT = 600;
    private HashMap<String, GameObject> m_CurrentGameObjects;
    private boolean m_QuitFlag;
    private BufferStrategy m_Strategy;
    private float m_ElapsedTime;
    private GAME_STATE m_CurrentState;
    private static Random RANDOM_GENERATOR = new Random();

    enum GAME_STATE {

        PLAYING, GAMEOVER, YOUWIN
    }

    /**
     *
     * Default constructor for the game. Nothing special here.
     */
    public SpaceInvaders() {
        // Iniitalize all the private data members:
        this.m_CurrentGameObjects = new HashMap<String, GameObject>();
        this.m_QuitFlag = false;
        this.m_ElapsedTime = 1;
        // Create the window:
        JFrame window = new JFrame("Space Invaders");
        JPanel panel = (JPanelwindow.getContentPane();
        setBounds(00, WIDTH, HEIGHT);
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        panel.setPreferredSize(new Dimension(WIDTH, HEIGHT));
        panel.setLayout(null);
        panel.add(this);
        window.setBounds(00, WIDTH, HEIGHT);
        window.setVisible(true);
        window.setResizable(false);
        window.addKeyListener(this);

        createBufferStrategy(2);
        this.m_Strategy = getBufferStrategy();
        this.m_CurrentState = GAME_STATE.PLAYING;
    }

    /**
     *
     * We don't do anything with keyTyped.  We just need it here so that it
     * compiles.
     */
    @Override
    public void keyTyped(KeyEvent e) {
    }

    /**
     *
     * All input modifies the spaceship only:
     */
    @Override
    public void keyPressed(KeyEvent e) {
        ((SpaceShipthis.m_CurrentGameObjects.get("SpaceShip")).HandleKeyPress(e);
    }

    /**
     *
     * All input modifies the spaceship only:
     */
    @Override
    public void keyReleased(KeyEvent e) {
        ((SpaceShipthis.m_CurrentGameObjects.get("SpaceShip")).HandleKeyRelease(e);
    }

    /**
     *
     * InitGame initializes the game by creating GameObjects into the hashmap.
     */
    public void InitGame() {
        // Load into the HashMap all the GameObjects
        this.m_CurrentGameObjects.put("SpaceShip"new SpaceShip());
        this.m_CurrentGameObjects.put("Monster1"new Enemy());
        this.m_CurrentGameObjects.put("Monster2"new Enemy());
        this.m_CurrentGameObjects.put("Monster3"new Enemy());
        this.m_CurrentGameObjects.put("Monster4"new Enemy());
        this.m_CurrentGameObjects.put("Monster5"new Enemy());
        this.m_CurrentGameObjects.put("Monster6"new Enemy());
        this.m_CurrentGameObjects.put("Monster7"new Enemy());
        this.m_CurrentGameObjects.put("Monster8"new Enemy());
        this.m_CurrentGameObjects.put("Monster9"new Enemy());
        this.m_CurrentGameObjects.put("Monster10"new Enemy());
        this.m_CurrentGameObjects.put("Monster11"new Enemy());
        this.m_CurrentGameObjects.put("Monster12"new Enemy());
        this.m_CurrentGameObjects.put("Monster13"new Enemy());
    }

    /**
     *
     * This is the heart of the game. While the game is running, the loop
     * continously draws and updates the screen.
     */
    public void GameLoop() {
        // This is the heart of the game.
        // We continuously update and paint the screen:
        while (this.m_QuitFlag == false) {
            long startTime = System.currentTimeMillis();
            this.update(this.m_ElapsedTime);
            this.paintScreen();

            // Let's be friendly to the other apps on this machine:
            try {
                Thread.sleep(10);
            catch (InterruptedException e) {
            }
            try {
                long endTime = System.currentTimeMillis();
                float diff = endTime - startTime;
                this.m_ElapsedTime = diff / 1000F;
            catch (ArithmeticException e) {
            }
        }
    }

    /**
     *
     * Update function in turn calls each GameObject's update function, passsing
     * in the elapsedTime since we did a screen refresh.
     *
     @param elapsedTime The amount of time that has gone by since last screen
     * fresh.
     */
    public void update(float elapsedTime) {
        // Update the Spaceship and all enemmies:
        for (String gameObject : this.m_CurrentGameObjects.keySet()) {
            // No need to update dead monsters. (Should we remove them from list?)
            if (this.m_CurrentGameObjects.get(gameObject).IsAlive()) {
                this.m_CurrentGameObjects.get(gameObject).Update(elapsedTime);
            }
        }
        // Now update all the missiles:
        for (int i = GameObject.m_AllMissiles.size() 1; i >= 0; i--) {
            GameObject.m_AllMissiles.get(i).Update(elapsedTime);
            if (GameObject.m_AllMissiles.get(i).DetectCollision(this.m_CurrentGameObjects)) {
                GameObject.m_AllMissiles.remove(i);
            }
        }
        // Test to make sure the spaceship is alive:
        if (!this.m_CurrentGameObjects.get("SpaceShip").IsAlive()) {
            this.m_CurrentState = GAME_STATE.GAMEOVER;
        }
        // Test to see if we killed all monsters on the screen:
        boolean bAreMonstersStillAlive = false;
        for (String gameObject : this.m_CurrentGameObjects.keySet()) {
            if (this.m_CurrentGameObjects.get(gameObject).GetGameObjectType() == GameObject.GameObjectType.ENEMY) {
                if (this.m_CurrentGameObjects.get(gameObject).IsAlive()) {
                    bAreMonstersStillAlive = true;
                    break;
                }
            }
        }
        if (bAreMonstersStillAlive == false) {
            this.m_CurrentState = GAME_STATE.YOUWIN;
        }
        // Test to see if you are an enemy, and if so, test to see if you've
        // reached bottom:
        for (String gameObject : this.m_CurrentGameObjects.keySet()) {
            if (this.m_CurrentGameObjects.get(gameObject).GetGameObjectType() == GameObject.GameObjectType.ENEMY) {
                // We know it's a monster, so now we can type cast it safely:
                if (((Enemythis.m_CurrentGameObjects.get(gameObject)).GetHasReachedBottom()) {
                    this.m_CurrentState = GAME_STATE.GAMEOVER;
                }
            }
        }
    }

    /**
     *
     * Foreach GameObject in the HashMap, call it's draw.
     */
    public void paintScreen() {
        // First clear the background:
        Graphics g = this.m_Strategy.getDrawGraphics();
        g.setColor(Color.BLACK);
        g.fillRect(00, getWidth(), getHeight());

        switch (this.m_CurrentState) {
            case PLAYING:
                // Now draw each game object:
                for (String gameObject : this.m_CurrentGameObjects.keySet()) {
                    if (this.m_CurrentGameObjects.get(gameObject).IsAlive()) {
                        this.m_CurrentGameObjects.get(gameObject).Draw(g, this);
                    }
                }
                // Now update all the missiles:
                for (int i = 0; i < GameObject.m_AllMissiles.size(); i++) {
                    GameObject.m_AllMissiles.get(i).Draw(g, this);
                }
        g.setColor(Color.red);
        g.drawString("Life: "+this.m_CurrentGameObjects.get("SpaceShip").getLife()510);
                break;
            case GAMEOVER:
                g.setColor(Color.red);
                g.drawString("GAME OVER! YOU LOSE!"350300);
                break;
            case YOUWIN:
                g.setColor(new Color(RANDOM_GENERATOR.nextInt(256), RANDOM_GENERATOR.nextInt(256), RANDOM_GENERATOR.nextInt(256)));
                g.drawString("YOU'RE A WINNER!"350300);
                break;
        }
        this.m_Strategy.show();
    }

    /**
     *
     * The main entry point for the program.
     */
    public static void main(String[] args) {
        SpaceInvaders spaceInvaders = new SpaceInvaders();
        spaceInvaders.InitGame();
        spaceInvaders.GameLoop();
    }
}