/**
 * Title:        Pommeja taivaalta
 * Description:  Geneerinen räiskintäpeli Javalla
 * Copyright:    Copyright (c) 2003
 *
 * @author Juho Makkonen
 * @version 1.0
 */


import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.applet.Applet;
import java.applet.AudioClip;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Random;
import java.util.Vector;
import java.util.Date;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JPanel;


/**
 * Luokka <code>Pommipaneeli</code> on luokan 
 * <code>Jpanel</code> laajennus. Pommipaneeliin
 * piirretään kaikki pelissä tapahtuvat animaatiot. Luokka
 * toteuttaa rajapinnan <code>Runnable</code>, eli sillä
 * on metodi <code>run</code> ja kyseistä metodia suorittava
 * säie.
 */ 
public class Pommipaneeli extends JPanel implements Runnable {

    /** Luokkakohtainen vakio satunnaisluku. */
    private static final Random satunnaisluku = new Random();

    /** Luokkakohtainen vakio viive, aika jonka säie nukkuu. */
    private static final int viive = 100;

    /** Säie on elossa niin kauan kuin peliJatkuu = true. */
    private boolean peliJatkuu = true;

    /** Kertoo, pitääkö tulosrivi värjätä. */
    private boolean peliOhi = false;

    /** Aika, jonka laukaus on näkyvissä. */
    private int laukausNakyvissa = 0;
    
    /** Käytetään pommien lisäyksen viiveen määrityksessä. */
    private int odotusAika = 10;

    /** Käytetään pommien lisäyksen viiveen määrityksessä. */
    private int odotusAika2 = 5000;

    /** Tuhottujen pommien määrä. */
    private int tuhotutPommit = 0;

    /** Kaupunkiin osuneiden pommien määrä. */
    private int osumatKaupunkiin = 0;

    /** Pelaajan pisteet. */
    private int pisteet = 0;

    /** Laukauksen lentoaika. */
    private int lentoAika = 0;

    /** Ydinlatausten määrä. */
    private int ydinlataukset = 2;

    /** Matkalla olevan laukauksen x-koordinaatti. */
    private int laukauksenX;

    /** Matkalla olevan laukauksen y-koordinaatti. */
    private int laukauksenY;

    /** Matkalla olevan laukauksen liikkumissuunta */
    private int laukauksensuuntaX;

    /** Matkalla olevan laukauksen liikkumissuunta. */
    private int laukauksensuuntaY;
   
    /** Kulunut peliaika sekunnin kymmenesosissa. */
    private int peliaika = 0;

    /** Kuluneen peliajan sekuntiosuus. */
    private int sekuntiAika = 0;

    /** Kuluneen peliajan minuuttiosuus. */ 
    private int minuuttiAika = 0;

    /** Peli, johon pommipaneeli kuuluu. */
    private Peli peli;

    /** Säie, jonka suoritus jatkuu koko ajan. */
    private Thread t;
  
    /** Puskurissa olevan kuvan koko. */ 
    private Dimension puskuriKuvanKoko;
    
    /** Varsinainen kuva */
    private Image puskuriKuva;

    /** Konteksti, jolla piirretään puskuriin. */
    private Graphics puskuriGrafiikat;

    /** Ruudulla näkyvät pommit. */
    private Vector pommit;

    /** Hiirellä laukaistu laukaus. */
    private Laukaus laukaus = null;

    /** Taivaalta putoava pommi. */
    private Pommi pommi;

    /** Pelaajan nimi. */
    private String pelaajanNimi;

    /** Tulosriville talletetaan pelaajan tulos. */
    private Tulosrivi tulosrivi;

    /** Tulostaulukkoon talletetaan pelaajan tulosrivi. */
    private Tulostaulukko tulostaulukko;

    /** Kymmenen parhaan tuloksen taulukko. */
    private ParhaatTulokset parhaatTulokset;

    /** Parhaat tulokset näytetään tuloskehyksessä. */
    private JDialog tuloskehys;
 
    /** Näyttää peliajan merkkijonona. */
    private String aika;

    /** Näyttää pelin tilan ikkunan yläreunassa. */
    private String valiaikatiedot;

    /** Ääniefekti. */
    private AudioClip aaniefekti;


    /**
     * Luo uuden pommipaneelin.
     * 
     * @param peli peli, jossa pommipaneeli sijaitsee
     */
    public Pommipaneeli(Peli peli) {
	
	// Asetetaan ominaismuuttujan <code>peli</code>
	// arvoksi parametrina annettu peli.
	this.peli = peli;

	// Asetetaan paneelin koko.	
	this.setSize(700,525);

	// Asetetaan taustaväriksi turkoosi.
	this.setBackground(new Color(0,255,255));
	
	// Lisätään pommipaneelille hiiren
	// painikkeita kuunteleva kuuntelija.
	this.addMouseListener(new Kuuntelija());

	// Määritetään laukauksen lähtöpaikka.
	laukauksenX = getSize().width /2;
	laukauksenY = getSize().height;

	// Alustetaan pommien joukko.
	pommit = new Vector();
    
    }   


    /**
     * Palauttaa ruudulla näkyvät pommit.
     * 
     * @return ruudulla näkyvät pommit
     */
    public Vector annaPommit() {
	return pommit;
    }


    /**
     * Palauttaa tuhotut pommit.
     *
     * @return tuhotut pommit
     */
    public int annaTuhotutPommit() {
	return tuhotutPommit;
    }


    /**
     * Kasvattaa tuhottujen pommien määrää yhdellä,
     * jos kaupunkiin osuneiden pommien  määrä on
     * alle 5.
     */ 
    public void lisaaTuhottuihinPommeihin() {
	if (osumatKaupunkiin < 5) {
	    tuhotutPommit++;
	}
    }


    /**
     * Palauttaa pelaajan pisteet.
     *
     * @return pisteet
     */
    public int annaPisteet() {
	return pisteet;
    }


    /**
     * Lisää pelaajan pisteisiin parametrina annetun
     * pistemäärän, jos peli on käynnissä ja 
     * kaupunkiin osuneiden pommien määrä on pienempi
     * kuin 5.
     *
     * @param pistemaara pisteiden lisäys
     */
    public void asetaPisteet(int pistemaara) {
	if ((peli.annaPelinTila() == 1) && 
	    (osumatKaupunkiin < 5)) {
	    pisteet += pistemaara;
	}
    } 


    /**
     * Palauttaa kaupunkiin osuneiden pommien määrän.
     *
     * @return kaupunkiin osuneiden
     * pommien määrä
     */
    public int annaOsumat() {
	return osumatKaupunkiin;
    }


    /** 
     * Kasvattaa kaupunkiin osuneiden pommien määrää
     * yhdellä, jos se on alle 5.
     */     
    public void lisaaOsumiin() {
	if (osumatKaupunkiin < 5) {
	    osumatKaupunkiin++;
	}
    }


    /**
     * Palauttaa jäljellä olevien ydinlatausten määrän.
     *
     * @return jäljellä olevat ydinlataukset
     */
    public int annaYdinlataukset() {
	return ydinlataukset;
    }

    public void asetaYdinlataukset(int maara) {
	ydinlataukset = maara;
    }

  
    /**
     * Alustaa säikeen ja käynnistää sen toiminnan.
     */
    public void luoSaie() {	
	t = new Thread(this);
	t.start();
    }
    
    
    /**
     * Liittymän <code>Runnable</code> toteuttamiseksi
     * tehtävä metodi. Kun säikeen toiminta käynnistyy,
     * se alkaa suorittaa tätä metodia.
     */
    public void run() {

	// Metodin suorittamista jatketaan niin kauan kuin
	// muuttujan <code>peliJatkuu</code> arvo on
	// <code>true</code>, eli käytännössä kunnes
	// peli-ikkuna suljetaan.
	while (peliJatkuu) {

	    // Tarkastetaan, onko peli juuri päättynyt.
	    testaaLopetetaanko();

	    // Metodin toteutus vaihteelee riippuen pelin
	    // tilasta, joka voi olla 0, 1 tai 2.
	    switch (peli.annaPelinTila()) {

		// Jos pelin tila on 0 eli "ei käynnissä".
	    case 0:
		
		// Päivitetään näyttö.
		repaint();

		// Odotetaan sekunti.
		try {
		    Thread.sleep(1000);
		} catch (InterruptedException ie) {
		    ie.printStackTrace();
		}

		// Päättää <code>switch-<code>lauseen suorituksen.
		break;

		// Jos pelin tila on 1 eli "käynnissä".
	    case 1:

		// Yritetään lisätä uusi pommi.
		lisaaPommi();

		// Jos laukaus on olemassa, muttei näkyvissä,
		// tarkistetaan onko <code>lentoaika</code>
		// suurempi kuin 0. Jos on, vähennetään sen
		// arvosta 30 ja päivitetään matkalla olevan 
		// laukauksen  sijainti. Jos
		// <code>lentoaika</code> on pienempi kuin
		// 0, asetetaan sen arvoksi 0, alustetaan
		// matkalla olevan laukauksen sijainnin
		// määrittävät muuttujat ja asetetaan laukaus
		// näkyväksi.
		if (laukaus != null && !laukaus.onNakyvissa()) {
		    if (lentoAika > 0) {
			lentoAika -= 30;
			laukauksenX -= laukauksensuuntaX;
			laukauksenY -= laukauksensuuntaY;
		    } else {
			lentoAika = 0;
			laukauksenX = getSize().width /2;
			laukauksenY = getSize().height;
			laukaus.asetaNakyvyys(true);
		    }		    
		}

		// Lukitaan ensin vektori pommit, ja käydään sitten
		// sen sisältö läpi. Jos joku pommeista on laukauksen
		// alueella, tuhotaan kyseinen pommi.
		synchronized(pommit) {
		    for (int i=0;i<pommit.size();i++) {
			pommi = (Pommi)pommit.elementAt(i);
			if (laukaus != null && laukaus.onNakyvissa()) {
			    laukaus.testaaTuhotaanko(pommi);
			}
		    }
		}

		// Jos laukaus on näkyvissä viimeistä kertaa,
		// lasketaan sen pistesaalis.
		if (laukausNakyvissa == 9) {
		    laukaus.laskePisteet();
		}

		// Lukitaan uudestaan vektori pommit ja käydään
		// taas sen sisältö läpi. Liikutetaan jokaista
		// pommia. Huom! Tämä operaatio ei onnistu
		// samassa silmukassa kuin pommien tuhoamisen
		// testaus, koska vektorin koko saattaa muuttua
		// välissä.
		synchronized(pommit) {
		    for (int i=0;i<pommit.size();i++) {
			pommi = (Pommi)pommit.elementAt(i);
			pommi.liikutaPommia();
		    }
		}
	    	   
		// Tarkastetaan pelin tila, ja päivitetään
		// pelin tilan ilmaisevaa merkkijonoa
		// sen mukaan.
		tarkastaTilanne();

		// Päivitetään tehdyt muutokset näytölle.
		repaint();

		// Nukutaan sekunnin kymmenesosa.
		try {
		    Thread.sleep(100);
		} catch (InterruptedException ie) {
		    ie.printStackTrace();
		}

		// Päättää <code>switch</code>-lauseen suorituksen.
		break;

		// Jos pelin tila on 2 eli "keskeytetty".
	    case 2:

		// Nukutaan sekunti, jonka jälkeen palataan
		// tarkistamaan pelin tila.
		try {
		    Thread.sleep(1000);
		} catch (InterruptedException ie) {
		    ie.printStackTrace();
		}

		// Päättää <code>switch</code>-lauseen suorituksen.
		break;
	    }
	
	}
    
    }
    


    /**
     * Tarkastaa pelin tilan: pelaajan pisteet, kuluneen ajan
     * sekä tuhottujen pommien, kaupunkiin osuneiden pommien
     * ja ydinlatausten määrän.
     */
    public void tarkastaTilanne() {
	
	// Kasvatetaan muuttujan <code>peliaika</code> arvoa yhdellä. 
	peliaika++;

	// Koska <code>peliaika</code> kasvaa yhdellä jokaisen sekunnin
	// kymmenyksen aikana, kasvaa sekuntien määrä aina, kun
	// <code>peliaika</code> on jaollinen kymmenellä.
	if (peliaika % 10 == 0) {
	    sekuntiAika++;
	}

	// Merkkijonon <code>aika</code> arvo riippuu siitä, kuinka
	// monta minuuttia ja sekuntia peli on jatkunut.
	if (sekuntiAika < 10) {
	    aika = (" Peliaika 0" + minuuttiAika + ":0"
		    + sekuntiAika + " ");
	} else if (sekuntiAika < 60) {
	    aika = (" Peliaika 0" + minuuttiAika + ":"
		    + sekuntiAika + " ");
	} else {
	    minuuttiAika++;
	    sekuntiAika = 0;
	    aika = (" Peliaika 0" + minuuttiAika + ":00 ");		    
	}

	// Muuttujan <code>valiaikatiedot</code> arvoksi asetetaan
	// merkkijono, joka näyttää kuluneen ajan, pelaajan pisteet,
	// tuhotut pommit, osumat kaupunkiin ja jäljellä olevat
	// ydinlataukset.
	valiaikatiedot = (aika + "Pisteet " + annaPisteet()+
			  " Osumat kaupunkiin "
			  + annaOsumat() + " Tuhotut pommit " +
			  annaTuhotutPommit() + " Ydinlataukset "
			  + annaYdinlataukset());
    
    }
    

    /**
     * Lisää peliin pommin, jos ehdot täyttyvät.
     */
    public void lisaaPommi() {

	// Ehto 1:
	// muuttujan <code>odotusAika</code> tulee
	// olla jaollinen viidellä.
	if (odotusAika % 5 == 0) {

	    // Muuttuja <code>odotetaan</code> on satunnainen
	    // kokonaisluku väliltä 0-<code>odotusAika2</code> 
	    int odotetaan = satunnaisluku.nextInt(odotusAika2);

	    // Ehto 2:
	    // Jos <code>odotetaan</code> on pienempi kuin
	    // 1500, lisätään peliin uusi pommi.
	    if (odotetaan < 1500) {
		Pommi pommac = new Pommi(this);
		pommit.addElement(pommac);
	    }

	}

	// Jos <code>odotusAika2</code> on pienempi kuin 2000,
	// vähennetään sen arvosta 2.
	if (odotusAika2 < 2000) {
	    odotusAika2 -= 2;
	}

	// Jos <code>odotusAika</code> on suurempi kuin 1,
	// vähennetään sen arvosta 1. Muussa tapauksessa
	// asetetaan sen arvoksi 5.
	if (odotusAika > 1) {
	    odotusAika -= 1;
	} else {
	    odotusAika = 5;
	}

    }


    /**
     * Tarkastaa, onko ehto pelin päättymiseksi täyttynyt
     * eli onko kaupunkiin osunut vähintään 5 pommia.
     * Jos näin on, lopetetaan peli, kysytään pelaajan
     * nimi, talletetaan hänen tuloksensa, näytetään
     * kymmenen parhaan tuloksen lista ja alustetaan
     * pelin tila.
     */
    public void testaaLopetetaanko() {

	if (osumatKaupunkiin >= 5) {
	    peli.asetaPelinTila(0);
	    peliPaattyi();
	    talletaTulokset();
	    naytaTulokset();
	    alustaPeli();
	}

    }	 
	    

    /**
     * Kun peli on päättynyt, kysytään pelaajan nimi. Nimen tulee olla
     * vähintään 1 ja korkeintaan 12 merkkiä pitkä, eikä se saa
     * koostua pelkistä välilyönneistä.
     */
    public void peliPaattyi() {

	peliOhi = true;
	String paatos = "Peli päättyi!\nAnna nimesi (enintään 12 merkkiä)";
	String otsikko = "Peli päättyi";
	String valilyonnit = "";

	// Jos pelaajan antama nimi ei täytä edellämainittuja ehtoja,
	// kysytään nimi uudestaan.
	while (pelaajanNimi == null || pelaajanNimi.length() == 0
	       || pelaajanNimi.length() > 12 ||
	       pelaajanNimi.equalsIgnoreCase(valilyonnit)) { 
	       pelaajanNimi = JOptionPane.showInputDialog(this,paatos,otsikko,1);
	       
	       // Tarkastetaan, ettei pelaajan nimi koostu pelkistä
	       // välilyönneistä.
	       if (pelaajanNimi != null && pelaajanNimi.length() > 0) {
		   valilyonnit = " ";
		   while (valilyonnit.length() != pelaajanNimi.length()) {
		       valilyonnit += " ";
		   }
	       }

	}	
       
    }


    /**
     * Tallettaa pelaajan tulosrivin tulostaulukkoon, jos rivi mahtuu
     * kymmenen parhaan rivin joukkoon.
     */
    public void talletaTulokset() {

	// Luodaan uusi tulostaulukko tiedostosta "Tulokset.txt".
	try {
	    tulostaulukko = new Tulostaulukko("Tulokset.txt");
	} catch (IOException ioe) {
	    ioe.printStackTrace();
	}
	
	// Muutetaan <code>peliaika</code> sopivaan muotoon, jotta
	// se näkyisi oikein tulosrivillä.
	peliaika = (minuuttiAika * 60 + sekuntiAika);

	// Luodaan pelaajan tuloksista uusi tulosrivi.
	tulosrivi = new Tulosrivi(pelaajanNimi,new Date(),peliaika,
				  annaTuhotutPommit(),annaPisteet());

	// Yritetään lisätä rivi tulostaulukkoon.
	tulostaulukko.lisaaRivi(tulosrivi);

	// Kerrotaan tulostaulukolle, että sen on värjättävä pelaajan
	// rivi punaiseksi, jos tämä mahtui kymmenen parhaan joukkoon.
	tulostaulukko.asetaVariVaihtuu(true);

	// Tallennetaan tulokset tulostaulukkoon.
	try {
	    tulostaulukko.tulosta("Tulokset.txt",10);
	} catch (IOException ioe) {
	    ioe.printStackTrace();
	}

	// Näytetään dialogi, joka kertoo tiedon siitä, pääsikö pelaaja
	// kymmenen parhaan joukkoon vai ei ja tekikö hän mahdollisesti
	// uuden ennätyksen.
	if (tulostaulukko.tulikoYkkostila() == 1) {
	    JOptionPane.showMessageDialog(this, "Teit uuden ennätyksen!"
					  ,"Uusi ennätys",
					  JOptionPane.WARNING_MESSAGE);
	} else if (tulostaulukko.mahtuukoMukaan() == true) {
	    JOptionPane.showMessageDialog(this, "Pääsit 10 parhaan joukkoon!"
					  ,"Top 10",
					  JOptionPane.WARNING_MESSAGE);
	} else {
	    String eiriita = "Valitettavasti tuloksesi ei riitä 10 parhaan joukkoon.";
	    JOptionPane.showMessageDialog(this, eiriita,"Ei sijoitusta",
					  JOptionPane.WARNING_MESSAGE);
	}

    }
	

    /**
     * Näyttää kymmenen parhaan tuloksen taulukon. Jos peli on juuri
     * päättynyt ja pelaaja on päässyt kymmenen parhaan joukkoon,
     * hänen tuloksensa näkyy taulukossa punaisella.
     */
    public void naytaTulokset() {

	// Taulukko voidaan näyttää, jos peli ei ole käynnissä
	// tai jos peli on keskeytetty.
	if (peli.annaPelinTila() == 0 || peli.annaPelinTila() == 2) {

	    // Tarkastetaan, onko peli juuri päättynyt. Jos ei,
	    // luodaan uusi tulostaulukko ja tulostetaan sen
	    // tiedot tiedostoon, jolloin pelaajan rivi ei enää
	    // näy punaisella.
	    if (peliOhi == false) {
		try {
		    Tulostaulukko t = new Tulostaulukko("Tulokset.txt");
		    t.tulosta("Tulokset.txt",10);
		} catch (java.io.IOException ioe) {
		    ioe.printStackTrace();
		}	  
	    }

	    // Alustetaan parhaat tulokset -taulukko.
	    try {
		parhaatTulokset = new ParhaatTulokset("Tulokset.txt");
	    } catch (java.io.IOException ioe) {
		ioe.printStackTrace();
	    }	  	    
	    
	    // Alustetaan <code>tuloskehys</code> ja sijoitetaan se
	    // suurin piirtein näytön keskelle.
	    tuloskehys = new JDialog(peli,"Tulokset top 10",true);
	    Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
	    tuloskehys.setLocation((dim.width - 625) / 2,
				   (dim.height - 200) / 2);

	    // Lisätään taulukko tuloskehykseen ja näytetään kehys.
	    tuloskehys.getContentPane().add(parhaatTulokset);
	    tuloskehys.pack();
	    tuloskehys.setVisible(true);
	}
    }
    

    /**
     * Asettaa pelin aikana mahdollisesti muuttuneiden muuttujien
     * arvot alkuperäisiksi.
     */
    public void alustaPeli() {
	odotusAika = 10;
	odotusAika2 = 5000;
	peliOhi = false;
	laukaus = null;
	pommit.removeAllElements();
	pisteet = 0;
	peliaika = 0;
	tuhotutPommit = 0;
	osumatKaupunkiin = 0;
	sekuntiAika = 0;
	minuuttiAika = 0;
	laukausNakyvissa = 0;
	pelaajanNimi = null;
	valiaikatiedot = null;
	asetaYdinlataukset(2);
	laukauksenX = getSize().width /2;
	laukauksenY = getSize().height;
    }


    /**
     * Piirtää laukausta ilmentävän suuren punaisen ympyrän,
     * kun laukaus on ehtinyt perille.
     *
     * @param g grafiikkakonteksti, johon piirtäminen tapahtuu
     */
    public void piirraLaukaus(Graphics g) {

	// Jos muuttujan <code>laukausNakyvissa</code> arvo
	// on pienempi kuin 10, piirretään laukaus ja
	// kasvatetaan muuttujan arvoa yhdellä. Muussa
	// tapauksessa peitetään punainen ympyrä taustavärillä
	// ja alustetaan laukauksen tila.
	if (laukausNakyvissa<10) {
	    laukausNakyvissa++;
	    g.setColor(getBackground());
	    g.fillRect(laukaus.annaX()-20,laukaus.annaY()-20,40,40);
	    g.setColor(Color.red);
	    g.fillOval(laukaus.annaX()-20,laukaus.annaY()-20,40,40);
	} else {
	    g.setColor(getBackground());
	    g.fillRect(laukaus.annaX()-20,laukaus.annaY()-20,40,40);
	    laukaus = null;
	    laukausNakyvissa = 0;
	}	

    }


    /**
     * Metodi, joka syrjäytetään, kun piirretään jotain näytölle.
     *
     * @param g grafiikkakonteksti, johon piirtäminen tapahtuu
     */

    public void paintComponent(Graphics g) {

	// Kutsutaan esi-isän metodia
	// <code>paintComponent(Graphics g)</code>.
	// Muuten komponentin tausta jää piirtämättä.
	super.paintComponent(g);

	// Kutsutaan kaksoispuskurointia varten metodia
	// <code>update(Graphics g)</code>.
	update(g);

    }


    /**
     * Metodi, joka syrjäytetään animaatiossa tarvittavan
     * kaksoispuskuroinnin takia. Kaksoispuskurointi
     * ehkäiseen näytön välkkymisen näyttöä päivitettäessä.
     */
    public void update(Graphics g) {

	// Kysytään komponentin koko.
	Dimension d = this.getSize();
	
	// Jos puskuria ei ole alustettu tai komponentin koko
	// on muuttunut, asetetaan puskurin kooksi komponentin
	// koko. Sitten luodaan komponentin kokoinen kuva ja
	// otetaan siltä grafiikkakonteksti, johon piirretään.
	if ( (puskuriGrafiikat == null)
	     || (d.width != puskuriKuvanKoko.width)
	     ||(d.height != puskuriKuvanKoko.height) ) {
	    puskuriKuvanKoko = d;
	    puskuriKuva = createImage(d.width, d.height);
	    puskuriGrafiikat = puskuriKuva.getGraphics();
	}
	
	// Pyyhitään puskurin tausta.
	puskuriGrafiikat.setColor(getBackground());
	puskuriGrafiikat.fillRect(0, 0, d.width, d.height);
	
	// Piirretään puskuriin muuttujan <code>valiaikatiedot</code>
	// arvon sisältävä merkkijono, jos sellainen on olemassa.
	if (valiaikatiedot != null) {
	    puskuriGrafiikat.setColor(Color.black);
	    puskuriGrafiikat.drawString(valiaikatiedot,130,20);
	}

	// Käydään jälleen näkyvät pommit läpi ja piirretään ne
	// puskuriin.
	synchronized(pommit) {
	    for (int i=0;i<pommit.size();i++) {
		pommi = (Pommi)pommit.elementAt(i);
		pommi.piirraPommi(puskuriGrafiikat);
	    }
	}

	// Tarkistetaan, onko laukaus ammuttu.
	if (laukaus != null && peli.annaPelinTila() == 1) {

	    // Jos on, tarkistetaan onko laukaus jo näkyvissä.
	    // Jos on, piirretään laukaus puskuriin. Muussa 
	    // tapauksessa piirretään puskuriin matkalla oleva
	    // laukaus.
	    if (laukaus.onNakyvissa()) {		
		piirraLaukaus(puskuriGrafiikat);
	    } else if (lentoAika > 0) {
		puskuriGrafiikat.setColor(Color.red);
		puskuriGrafiikat.fillOval(laukauksenX,laukauksenY,5,5);
	    }

	}

	// Piirretään puskurin sisältö kerralla näytölle.
	g.drawImage(puskuriKuva,0,0,this);

    }

    
    /**
     * Soittaa parametrina annetun äänitiedoston kerran läpi.
     *
     * @param aani soitettavan äänitiedoston nimi
     */
    public void soitaAani(String aani) {

	try {
	    
	    // Määritetään äänitiedoston "osoite". Äänitiedoston
	    // tulee sijaita Pommipaneeli.java:n sisältävän
	    // hakemiston alihakemistossa "Ääniefektit".
	    URL aanenlahde = new URL("file:" + System.getProperty("user.dir")
				     + "/Ääniefektit/" + aani);

	    // Äänitiedoston soittamiseksi sovelluksessa on kutsuttava
	    // <code>Applet</code>-luokan metodia
	    // newAudioClip(URL url).
	    aaniefekti = Applet.newAudioClip(aanenlahde);

	    // Soitetaan äänitiedosto kerran läpi.
	    aaniefekti.play();	    

	} catch (MalformedURLException murle) {
	    murle.printStackTrace();
	}

    }


    /** 
     * Luo uuden laukauksen näytölle kohtaan, johon pelaaja klikkaa
     * hiirellä.
     */
    public void kuuntele(MouseEvent e) {

	// Tarkistetaan, että edellinen laukaus on poistunut
	// näkyvistä ja peli on käynnissä.
	if (laukaus == null && peli.annaPelinTila() == 1) {

	    // Liukuluvun ampumaMatka arvoksi asetetaan etäisyys
	    // antennin nupista klikattuun kohtaan. Etäisyys
	    // lasketaan pythagoraan lauseella.
	    double ampumaMatka = Math.sqrt((Math.pow(Math.abs(getSize().width/2-e.getX()),2.0)
					    + Math.pow(getSize().height-e.getY(),2.0)));

	    // Liukuluvun muuttamiseksi kokonaisluvuksi on luotava
	    // Double-olio, jolla on käyttökelpoinen metodi
	    // <code>intValue</code>. 
	    Double ampumaMatka2 = new Double(ampumaMatka);

	    // Asetetaan tällä metodilla matkan pituudesta saatu
	    // kokonaisluku muuttujan <code>lentoAika</code> arvoksi.
	    lentoAika = ampumaMatka2.intValue();

	    // Lasketaan, kuinka pitkä matka matkalla olevan laukauksen
	    // tulee edetä yhdellä <code>run</code>-metodin suorituskerralla
	    // (ottaen huomioon, että muuttujan <code>lentoAika</code> arvoa
	    // vähennetään 30:lla jokaísen sekunnin kymmenesosan aikana). 
	    // Asetetaan muuttujien <code>laukauksensuuntaX</code>ja 
	    // <code>laukauksensuuntaY</code> arvoiksi vastaavat
	    // kokonaislukuarvot.
	    Double suuntaXdouble;
	    Double suuntaYdouble;

	    // Varmistetaan, ettei synny nollalla jakoa.
	    if (lentoAika != 0) {

		// Tuplavarmistus (muuttujan <code>lentoAika</code>
		// arvo muuttuu sekunnin kymmenyksen välein).
		double lentoAika2 = lentoAika + 0.000000000000000001;
		
		suuntaXdouble = new Double(((getSize().width/2)-e.getX())
					   / (lentoAika2 / 30));
		suuntaYdouble = new Double((getSize().height-e.getY())
					   / (lentoAika2 / 30));
		laukauksensuuntaX = suuntaXdouble.intValue();
		laukauksensuuntaY = suuntaYdouble.intValue();

		// Jos <code>lentoAika</code> on nolla, laukaus ei liiku. 
	    } else {
		laukauksensuuntaX = 0;
		laukauksensuuntaY = 0;
	    }

	    // Luodaan uusi laukaus, jonka keskipisteenä tulee
	    // olemaan pelaajan hiirellä valitsema piste
	    laukaus = new Laukaus(e.getX(),e.getY(),this);

	    // Soitetaan ääniefekti laukaisun merkiksi.
	    soitaAani("beep.au");
	}
    }

 

    /**
     * Sisäluokka, joka toteuttaa rajapinnan 
     * <code>MouseListener</code>.
     */
    public class Kuuntelija implements MouseListener {


	/** 
	 * Kuunnellaan hiiren klikkauksia ja kutsutaan
	 * sellaisen tapahtuessa pommipaneelin
	 * metodia <code>kuuntele(MouseEvent e)</code>.
	 *
	 * @param e hiiritapahtuma
	 */
	public void mouseClicked(MouseEvent e) {
	    kuuntele(e);
	}
	

	/** 
	 * Kuunnellaan hiiren napin vapauttamista.
	 * Tässä sovelluksessa metodi ei tee
	 * mitään, vaan sitä tarvitaan vain
	 * rajapinnan toteuttamiseen.
	 *
	 * @param e hiiritapahtuma
	 */
	public void mouseReleased(MouseEvent e) {
	}
	

	/** 
	 * Kuunnellaan hiiren siirtymistä
	 * komponentin ylle.
	 * Tässä sovelluksessa metodi ei tee
	 * mitään, vaan sitä tarvitaan vain
	 * rajapinnan toteuttamiseen.
	 *
	 * @param e hiiritapahtuma
	 */
	public void mouseEntered(MouseEvent e) {
	}
		

	/** 
	 * Kuunnellaan hiiren napin poistumista
	 * komponentin yltä.
	 * Tässä sovelluksessa metodi ei tee
	 * mitään, vaan sitä tarvitaan vain
	 * rajapinnan toteuttamiseen.
	 *
	 * @param e hiiritapahtuma
	 */
	public void mouseExited(MouseEvent e) {
	}
	

	/** 
	 * Kuunnellaan hiiren napin painamista
	 * pohjaan.
	 * Tässä sovelluksessa metodi ei tee
	 * mitään, vaan sitä tarvitaan vain
	 * rajapinnan toteuttamiseen.
	 *
	 * @param e hiiritapahtuma
	 */
	public void mousePressed(MouseEvent e) {
	}

    }

}


