 
/*
 * Copyright (c) 1996 Mika Saastamoinen. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and
 * without fee is hereby granted. 
 * 
 * I MAKE NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. I SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 * 
 * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
 * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
 * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
 * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
 * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
 * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
 * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES").  YOUR COMPANY
 * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
 * HIGH RISK ACTIVITIES.
 *
 * :)
 */
 
 /* 
  * Nuin ikkään, kun tuo pikku disclaimeri on poissa tieltä, niin voidaankin 
  * mennä itse asiaan... O:)
  */
  
import java.applet.Applet;
import java.awt.*;
import java.lang.*;
import java.util.Random;
import java.net.*;

/*
korttiluokka, joka pitää sisällään yhden kortin kamat...
*/
class kortti {
	private int numero; // 0-12
	private int maa; // 0-3 = hertta, ruutu, pata, risti
	private int kx,ky; // kortin x- ja y-koordinaatit
	private static int kw = 30, kh = 50, rr = 4; // kortin leveys, korkeus ja kulman pyristyssäde
	private boolean lukittu = false; // onko kortti lukittu, eli oikealla paikallaan
	
	public void lukitse(){
		lukittu = true;
	}
	public void vapauta(){
		lukittu = false;
	}
	public boolean onko_lukittu(){
		return(lukittu);
	}
	public void aseta_x(int sarake){
		kx = 10+sarake*35;
	}
	public void aseta_y(int rivi){
		ky = 10+rivi*55;
	}
	public void aseta_paikka(int rivi,int sarake){
		aseta_x(sarake);
		aseta_y(rivi);
	}
	kortti(int maa, int nro){ // konstruktori
		this.maa = maa;
		this.numero = nro;
		aseta_paikka(maa,nro);
	}
	public int kerro_maa(){
		return(maa);
	}
	public int kerro_nro(){
		return(numero);
	}
	public void poista_vanha(Graphics g){
		g.setColor(new Color(0,128,0));
		g.fillRect(kx,ky,kw+2,kh+2);
	}

	public void piirra(Graphics g, Montana m){ // piirretään kortti...
		Color col = new Color(0);
		Integer intg = new Integer(numero+1);

		if(numero != 0){ // joku muu kortti kuin ässä
	
			// tehdään kortin tausta, mustalla varjolla
			g.setColor(col.black);
			g.fillRoundRect(kx+2,ky+2,kw,kh,rr,rr);
			g.setColor(col.white);
			g.fillRoundRect(kx,ky,kw,kh,rr,rr);

			// vaihdetaan tulostusväri kortin maan mukaan	
			if(maa <2)
				g.setColor(col.red); // punainen
			else
				g.setColor(col.black); // musta
		
			// tulostetaan kortin numero
			switch(numero) {
				case 9:
					g.drawString("10",kx+2,ky+10);
					g.drawString("10",kx+kw-12,ky+kh-2);
					break;
				case 10: // jätkä
					g.drawString("J",kx+2,ky+10);
					g.drawString("J",kx+kw-9,ky+kh-2);
					break;
				case 11: // eukko
					g.drawString("Q",kx+2,ky+10);
					g.drawString("Q",kx+kw-9,ky+kh-2);
					break;
				case 12: // kunkku
					g.drawString("K",kx+2,ky+10);
					g.drawString("K",kx+kw-9,ky+kh-2);
					break;
				default: // muuten numero 1-9				
					g.drawString(intg.toString(),kx+2,ky+10);
					g.drawString(intg.toString(),kx+kw-9,ky+kh-2);
			}
		
			// tulostetaan kortin maa
			g.drawImage(m.imagot[maa],kx+10,ky+18,m);

		}else{ // kortti on ässä, piirretään vain placeholder
			poista_vanha(g);
			g.setColor(new Color(0,90,0));
			g.fillRoundRect(kx,ky,kw,kh,rr,rr);
		}
	}

}

/*
korttipytäluokka, joka pitää sisällään pelikorttien järjestyksen pydällä
*/
class korttipoyta {
	private int poyta[][] = new int[4][13];
	private static int tx=10,ty=250;
	private Graphics g;
	private Montana monty;
	private boolean valmis = false;
	private boolean undo_ok = false;
	private int u_sar1,u_riv1,u_sar2,u_riv2;
	
	korttipoyta(kortti kortit[]) { // konstruktori
		alusta(kortit);
	}
	public void aseta_undo(boolean ok){
		undo_ok = ok;
	}
	public boolean voiko_undo(){
		return(undo_ok);
	}
	public void alusta(kortti kortit[]){
		int maa,nro;
		
		// sijoitetaan kortit pytään järjestyksessä
		for(maa=0;maa<4;maa++)
			for(nro=0;nro<13;nro++){
				poyta[maa][nro] = maa*13+nro;
				kortit[poyta[maa][nro]].vapauta(); // poistetaan korttien lukitukset...
			}
	}
	
	public void valmis(boolean aseta){
		valmis = aseta;
	}
	
	public boolean onko_valmis(){
		return(valmis);
	}
	public void piirra(Graphics gfx,Montana m, kortti kortit[],boolean tyhjennys){ // piirretään pelipytä
		int rivi,sar;
		
		// alustetaan paikalliset hanskat grafiikka- ja Montana-objekteihin
		g = gfx; monty = m; 
		
		// tyhjennetään poytä
//		g.clearRect(10,10,455,230);

		//... kortti kerrallaan
		for(rivi=0;rivi<4;rivi++){
			for(sar=0;sar<13;sar++){
				if(tyhjennys){
					if(kortit[poyta[rivi][sar]].onko_lukittu()){
						kortit[poyta[rivi][sar]].piirra(g,monty);
					}else
						kortit[poyta[rivi][sar]].poista_vanha(g);
				}else{
					kortit[poyta[rivi][sar]].piirra(g,monty);
				}
			}
		}
				
		// poytä valmiustilaan		if(!onko_valmis()) valmis(true);
	}
	public void piirra(Graphics g,Montana m,kortti kortit[]){
		piirra(g,m,kortit,false);	
	}

	public void tyhjenna_vaarista(Graphics g,Montana m, kortti kortit[]){
		piirra(g,m,kortit,true);
	}

	public void sekoita(kortti kortit[]) {
		int i,krt1,krt2,rivi1, sar1,rivi2,sar2,lkm,vaarat;
		boolean luk1,luk2;
		Random rnd = new Random();
		
		if((lkm=laske_oikeat(kortit))!=48){
			vaarat = 48-lkm;
			
			// riittääk 200 sekoitusiteraatiota?
			for(i=0;i<vaarat*4;){

				// valitaan kaksi satunnaista korttia
				rivi1 = Math.abs(rnd.nextInt()%4);
				sar1 = Math.abs(rnd.nextInt()%13);
				rivi2 = Math.abs(rnd.nextInt()%4);
				sar2 = Math.abs(rnd.nextInt()%13);
					
				krt1 = poyta[rivi1][sar1];
				krt2 = poyta[rivi2][sar2];
				luk1 = kortit[krt1].onko_lukittu(); // onko kortit lukittu?
				luk2 = kortit[krt2].onko_lukittu();
						
				if(krt1 != krt2 && !luk1 && !luk2){ // jos ovat eri kortit, eikä kumpikaan ole
											// lukossa, niin vaihdetaan ne keskenään
					poyta[rivi2][sar2] = krt1;
					kortit[krt1].aseta_paikka(rivi2,sar2); // kortin koordinaatit samalla kondikseen
					poyta[rivi1][sar1] = krt2;
					kortit[krt2].aseta_paikka(rivi1,sar1); // kortin koordinaatit samalla kondikseen
					i++;
				}
			}
		}
	}

	public void vaihda_kortit(kortti kortit[], int rivi1,int sar1,int rivi2,int sar2){
		int k1,k2;
		// laitetaan siirto "undo-puskuriin"
		u_riv1=rivi1;u_sar1=sar1;u_riv2=rivi2;u_sar2=sar2;
		if(!voiko_undo()) aseta_undo(true); // undo mahdolliseksi
		
		// vaihdetaan annettujen korttien paikat
		k1 = poyta[rivi1][sar1];
		k2 = poyta[rivi2][sar2];
		poyta[rivi1][sar1] = k2;
		poyta[rivi2][sar2] = k1;
		// päivitetään näyttä näiden korttien osalta
		kortit[k2].poista_vanha(g);
		kortit[k1].poista_vanha(g);
		kortit[k2].aseta_paikka(rivi1,sar1);
		kortit[k1].aseta_paikka(rivi2,sar2);
		kortit[k2].piirra(g,monty);
		kortit[k1].piirra(g,monty);
	}
	public void undo(kortti kortit[]){
		if(voiko_undo()){
			vaihda_kortit(kortit,u_riv1,u_sar1,u_riv2,u_sar2);
			viesti(0); // ok
		}else viesti(5); // undo ei mahdollinen
	}
	public void poista_viesti(){
		g.setColor(new Color(0,128,0)); // taustan väri
		g.fillRect(10,241,455,13); // peitetään vanhat tekstit
	}
	public void viesti(int vst,String teksti){
		poista_viesti(); // poistetaan vanha viesti

		g.setColor(new Color(255,255,255)); // teksti valkoisella värillä
		switch(vst){
			case 0: // voi siirtää
				g.drawString("OK!",tx,ty);
				break;
			case 1: // ei voi siirtää - ei paikkaa
				g.drawString("You can't move that card! Try something else!",tx,ty);
				break;
			case 2: // ei voi siirtää - lukittu
				g.drawString("You can't move that card! It's locked in its correct place!",tx,ty);
				break;
			case 3: // tyhjä ruutu, ei tapahdu mitään
				break;
			case 4: // vain kaksi sekoitusta sallitaan
				g.drawString("Only two re-shuffles are allowed!",tx,ty);
				break;
			case 5: // undo ei ole mahdollinen
				g.drawString("Undo is not possible, no cards have been moved!",tx,ty);
				break;
			case 99:
				g.drawString(teksti,tx,ty);
				break;			
			default:
				g.drawString("Unknown error! User will be terminated! :)",tx,ty);
		}
	}
	
	public void viesti(String teksti){
		viesti(99,teksti);
	}
	public void viesti(int vst){
		viesti(vst,"");
	}
	public void tarkista_klikkaus(kortti kortit[],int rivi, int sarake){
		int i,j,k=0,rivi2,sarake2,knro,kmaa;
		int viesti = 3;

		if(!kortit[poyta[rivi][sarake]].onko_lukittu()){	

			knro = kortit[poyta[rivi][sarake]].kerro_nro();
			kmaa = kortit[poyta[rivi][sarake]].kerro_maa();
			// käydään korttipytä läpi ja katsotaan, voitaisiinko klikattu kortti sijoittaa
			// johonkin tyhjään koloon. jos voi, niin tehdään niin. jos ei, niin mollataan
			// käyttäjää
		
			switch(knro){
				case 0: // ässä eli tyhjä paikka, ei voi siirtää tietenkään
					viesti = 3;
					break;
				case 1: // 2 eli sen voi sijoittaa vain ekaan sarakkeeseen
					for(i=0,k=0;i<4;i++){
						if(kortit[poyta[i][0]].kerro_nro() == 0){ // tyhjä paikka
							vaihda_kortit(kortit,i,0,rivi,sarake); // vaihdetaan korttien paikat
							viesti = 0;
							i = 4; // että päästään loopista ulos
						}else k++;
					}
					if(k>3) viesti = 1; // ei tyhjiä paikkoja ekassa sarakkeessa
					break;
				default: // jokin muu kortti, voi sijoittaa muihin kuin ekaan sarakkeeseen
					for(i=0,k=0;i<4;i++){
						for(j=1;j<13;j++){ // toisesta sarakkeesta etiäppäin
							if(kortit[poyta[i][j]].kerro_nro() == 0){ // tyhjä paikka
								// tarkistetaan tyhjän paikan vasemmalta puolelta,
								// onko ko. kortti samaa maata kuin tämä ja numeroa
								// pienempi
								if(kortit[poyta[i][j-1]].kerro_nro() == knro-1 && kortit[poyta[i][j-1]].kerro_maa() == kmaa){
									// on, eli voidaan sijoittaa kortti tyhjään paikkaan
									vaihda_kortit(kortit,i,j,rivi,sarake);
									viesti = 0;
									j = 13; i = 4; // että päästään loopista ulos
								}else k++;
							}
						}
					}
					if(k>3) viesti = 1; // yksikään tyhjä paikka ei ole sopiva
					break;
			} // switch
			
		} else{ // lukittu vai ei?
			viesti = 2;
		}

		//kerrotaan käyttäjälle toimenpiteen onnistumisesta
		viesti(viesti);
	}
	
	public void lukitse_oikeat(kortti kortit[]){
		int r,s;
		
		for(r=0;r<4;r++){
			// lukitaan eka kortti, jos se on 2
			if(kortit[poyta[r][0]].kerro_nro() == 1) kortit[poyta[r][0]].lukitse();
			for(s=1;s<13;s++){
				// jos tämä kortti on samaa maata ja numeroa suurempi kuin rivin edellinen,
				// niin lukitaan se paikalleen
				if(kortit[poyta[r][s]].kerro_nro() == kortit[poyta[r][s-1]].kerro_nro()+1 &&
				   kortit[poyta[r][s]].kerro_maa() == kortit[poyta[r][s-1]].kerro_maa() &&
				   kortit[poyta[r][s-1]].onko_lukittu()){
					kortit[poyta[r][s]].lukitse(); // lukitaan kortti paikalleen
				}
			}
		}
	}
	public int laske_oikeat(kortti kortit[]){ // palauttaa oikein saatujen korttien lkm:n
		int r,s,lkm=0;
		
		for(r=0;r<4;r++)
			for(s=0;s<13;s++)
				if(kortit[poyta[r][s]].onko_lukittu())
					lkm++;
		return(lkm);
	}
}

/*
Montanaluokka, joka pitää sisällään appletin pääkilkkeet...
*/
public class Montana extends Applet{
	// muuttujat
	
	MediaTracker mtracker = new MediaTracker(this);
	boolean gfx_ladattu = false;
	private kortti kortit[] = new kortti[52];	
	private korttipoyta pelipoyta;
	private int sekoitus_lkm = 2,H=0,Hs,Hr;
	private Graphics gfxbase;
	private Button cb_me = new Button ("Montana by -MiS-");
	private Button cb_shuf = new Button("Shuffle");
	private Button cb_undo = new Button("Undo");
	private Button cb_newg = new Button("New Game");
	private Panel paneeli = new Panel();
	public Image imagot[] = new Image[4];
	
	public void init() { // alustus
		int maa,nro;
		Dimension d = this.size();

		// alustetaan korttigrafiikat
		imagot[0] = getImage(getCodeBase(), "javaimg/hertta.gif");
		mtracker.addImage(imagot[0],0);
		imagot[1] = getImage(getCodeBase(), "javaimg/ruutu.gif");
		mtracker.addImage(imagot[1],1);
		imagot[2] = getImage(getCodeBase(), "javaimg/pata.gif");
		mtracker.addImage(imagot[2],2);
		imagot[3] = getImage(getCodeBase(), "javaimg/risti.gif");
		mtracker.addImage(imagot[3],3);
		
		// taustaväri vihreäksi
		setBackground(new Color(0,128,0));
		
		// asetetaan vakiofontti
		setFont(new Font("Times",0,12));
		
		// tehdään painikkeet
// PositionLayout-luokan (ei Javan vakioluokka, vaan kolmannen osapuolen) kautta pystyttaisiin 
// automatiikallakin sijoittelemaan kontrolleja mieluisiin paikkoihin, mutta emme nyt sita 
// kuitenkaan kayta
//		setLayout(new PositionLayout());
//		paneeli.setLayout(new PositionLayout(475,30));
//		paneeli.add("355 0",cb_me); // nappien sijainti paneelissa
//		paneeli.add("160 0",cb_shuf);
//		paneeli.add("85 0",cb_undo);
//		paneeli.add("10 0",cb_newg);
//		add("0 265",paneeli); // paneelin sijainti?

		// huolehditaan layouttaamisesta itse, eli automatiikka pois
		setLayout(null);
	   
		// asetellaan namikat paikalleen
		add(cb_newg);
		Dimension prefSize = cb_newg.preferredSize();
		cb_newg.resize(prefSize.width, prefSize.height);
		cb_newg.move(10, (d.height - (prefSize.height) - 10));

		add(cb_undo);
		cb_undo.resize(prefSize.width, prefSize.height);
		cb_undo.move(20 + prefSize.width, (d.height - (prefSize.height) - 10) );
        
		add(cb_shuf);
		cb_shuf.resize(prefSize.width, prefSize.height);
		cb_shuf.move(30 + (prefSize.width * 2), (d.height - (prefSize.height) - 10) );

		add(cb_me);
		prefSize = cb_me.preferredSize();
		cb_me.resize(prefSize.width, prefSize.height);
		cb_me.move(d.width - 10 - prefSize.width, (d.height - (prefSize.height) - 10) );
 
		// tehdään kortit		
		for(maa=0;maa<4;maa++)
			for(nro=0;nro<13;nro++)
				kortit[maa*13+nro] = new kortti(maa,nro);

		// tehdään pelipoyta
		pelipoyta = new korttipoyta(kortit);
		
		cb_me.enable(pelipoyta.onko_valmis()); // napit pois paalta
		cb_shuf.enable(pelipoyta.onko_valmis()); 
		cb_undo.enable(pelipoyta.onko_valmis());
		cb_newg.enable(pelipoyta.onko_valmis()); // mys tämä siihen asti, että grafiikat saadaan ladattua...
	}

	public void paint( Graphics g) { // ruudun piirto
		// otetaan grafiikkakahva talteen
		gfxbase = g.create();
		
		if(!gfx_ladattu){ // ladataan grafiikat
			gfxbase.setColor(new Color(255,255,255));
			gfxbase.drawString("Please wait - loading graphics...",10,250);		

			try{
				mtracker.waitForAll();
			}catch(InterruptedException e){
				System.out.println("mediatrackerin interruptedexception iski!");
			}
			// kuvat ladattu
			gfx_ladattu = true;

			cb_me.enable(true); // napit päälle
			cb_newg.enable(true); 

			gfxbase.drawString("Please wait - loading graphics... DONE! Click 'New Game' to start playing. ENJOY! :)",10,250);		
			
			paint(gfxbase);
		}else
			if(pelipoyta.onko_valmis()) pelipoyta.piirra(gfxbase,this,kortit);

	}

	public void aloita_peli(){
		// tehdään pelipoytä
		pelipoyta.alusta(kortit);
		// sekoitetaan poytä
		pelipoyta.sekoita(kortit);
		// piirretään korttipoydän kortit
		pelipoyta.piirra(gfxbase,this,kortit);
		pelipoyta.valmis(true); // peli voi alkaa
		pelipoyta.poista_viesti();	// poistetaan tekstiviesti
		cb_shuf.enable(pelipoyta.onko_valmis());
		cb_undo.enable(pelipoyta.onko_valmis());
	}

	public void lopeta_peli(){
		int oikein = 0;
		
		pelipoyta.lukitse_oikeat(kortit);
		oikein = pelipoyta.laske_oikeat(kortit);
		pelipoyta.tyhjenna_vaarista(gfxbase,this,kortit);
		pelipoyta.valmis(false); // peli ei ole käynnissä enää
		if(oikein!=48)
			pelipoyta.viesti("You managed to arrange "+oikein+" (of 48) cards into the correct places! Well done!"+((oikein<24)?" (NOT!! :)":" Way cool!!! :)"));
		else 
			pelipoyta.viesti("You got them all!! Yeah, hip hip hurray! Amazing! Fantastic! You're the best!");
		cb_shuf.enable(pelipoyta.onko_valmis());
		cb_undo.enable(pelipoyta.onko_valmis());
	}

	
	public boolean mouseUp(Event evt, int mx, int my){
		int rivi = (my-10)/55;
		int sarake = (mx-10)/35;

		if(pelipoyta.onko_valmis()){ // reagoidaan klikkaukseen vain, jos a) peli on käynnissä...
		
			pelipoyta.poista_viesti();	// poistetaan tekstiviesti
		
			// ...tai b), jos klikkaus on pelikorttien alueella
			if(rivi >=0 && rivi <=3 && sarake >= 0 && sarake <= 12 && sekoitus_lkm >= 0){
				switch(H){case 0:pelipoyta.tarkista_klikkaus(kortit,rivi,sarake);break;case 1:Hr=rivi;Hs=sarake;H++;break;case 2:pelipoyta.vaihda_kortit(kortit,Hr,Hs,rivi,sarake);H=0;break;}
			}	
		}
		return(true);	
	}

	public boolean action(Event evt, Object arg) {

		// mitä tehdään sekoituspainikkeen takana...
		if ("Shuffle".equals(arg)) {
			if(sekoitus_lkm > 0){
				pelipoyta.lukitse_oikeat(kortit);
				pelipoyta.tyhjenna_vaarista(gfxbase,this,kortit);
				pelipoyta.sekoita(kortit);
				pelipoyta.piirra(gfxbase,this,kortit);
				sekoitus_lkm --;
				 // jos sallitut sekoitukset on tehty, joten sekoitusnappi pois päältä
				if(sekoitus_lkm == 0) cb_shuf.disable();
				
				pelipoyta.aseta_undo(false); // undota ei voi käyttää sekoituksen jälkeen
			}

			
			return true;
		}

		// mitä tehdään undo-painikkeen takana... nappi on kyllä harmaana toistaiseksi...
		if ("Undo".equals(arg)) {
			pelipoyta.undo(kortit);
			return true;
		}

		// mitä tehdään uusi peli -painikkeen takana...
		if ("New Game".equals(arg)) {
			aloita_peli();
			cb_newg.setLabel("End Game"); // newgame endgameksi
			sekoitus_lkm = 2;
			return(true);
		}

		// mitä tehdään lopeta peli -painikkeen takana...
		if ("End Game".equals(arg)) {
			lopeta_peli();
			cb_newg.setLabel("New Game"); // endgame takaisin newgameksi
			return(true);
		}

		// mitä tehdään about-namiskan takana
		if("Montana by -MiS-".equals(arg)){
			try{
				URL u;
				u = new URL("HTTP://www.utu.fi/~mikasaas");
				getAppletContext().showDocument(u);
			}catch(MalformedURLException e){
				// mitä tehdään, jos URL ei skulaa? tässä tapauksessa ei mitään...
			}
			return(true);
		}
		return false;
    }

	public boolean keyUp(Event evt,int key){
		if(pelipoyta.onko_valmis()){ 
			switch(key){case 'H':H=1;break;}
		}//if pelipoyta
		return(true);
	}

    public String getAppletInfo() {
		return "Montana v0.5 by Mika Saastamoinen";
    }
}
