//
// Wabburami.java
//

import waba.ui.*;
import waba.fx.*;
import waba.sys.*;

import extra.ui.*;

import wabadma.ui.*;
import wabadma.util.*;

/**
 *  The classic game of Hammurabi for the JavaOne 1999 KVM.
 *<P>
 *  Play the part of the ruler of ancient Babylon with this
 *  great-great-great-granddaddy of empire building games.   
 *<P> 
 *  The foundation of this Spotlet is Sun's Rumor game that was 
 *  distributed at the 1999 JavaOne developer conference; all the event
 *  and text handling comes directly from that code.  Everything else
 *  was converted from the C version of the game (see below).  I won't
 *  claim that the coding is incredibly clean or well-implemented, but this
 *  is more a demonstration that an ordinary Java engineer can write a 
 *  functional KVM application than anything else (although there's no
 *  data access of any kind).  
 *<P>
 *  Based on C code from the Open License Software Supplement (Skunkware)
 *  that I found at ftp.sco.com/skunkware/ and seemed to me to be close enough
 *  to the original version (in BASIC?) to use as the basis for this code.  : )
 *<P>
 *  TODO:
 *<LI> Add saving of high scores and perhaps game state in a .pdb file.
 *<P>
 *
 *  @author Jim Lesko  --  Jim@MartianWind.com *  @version 0.9
 *
 *  version 0.9w:  07 Nov 2000
 *	modified into waba by dmarcher@pobox.com 
 *
 */


public class Wabburami extends ExtraMainWindow {
	static String TITLE="Wabburami";
	static String VERSION="0.9w(b10)";
	static String VERSIONDATE="2000 Nov 07";
   
	static DLWRandom r;
	// UI objects:
	MenuBar mb;
	//static Button newButton;	// unused
   	static Button exitButton;	// exit the game
   	static Button doneButton;	// done with turn
	static Button buyButton;	// buy acres
	static Button sellButton;	// sell acres
	static Button plantMaxButton;	// plant Max. acres
	static Button feedNeededButton;	// feed Min. Needed bushels
	static Button plantRbButton;	// plant using Remaining bushels
	static Button feedRbButton;	// feed Remaining bushels	
	static Button bsPlusButton;	// increase buy/sell #
	static Button bsMinusButton;	// decrease buy/sell #
	static Button feedAcresButton;	// feed min. bushels to tend acres
	static Button plantAvgButton;	// plant min. so avg. yields feeds all
	static NumericEdit buysellEdit;	// # of acres to buy or sell
	static NumericEdit feedEdit;	// # of bushels to feed people
	static NumericEdit plantEdit;	// # of acres to plant
	static OutputBox out;
	static Label statusLabel;

   	// UI state:
   	static int lineAscent;
	static int lineHeight;
	FontMetrics fm;
   
   	// Game variables:
   	static boolean playing;
	static boolean hard;	// hard difficulty (otherwise it's easy)
   
	static int year;	// Turn number
	static int acres; 	// Acres owned
	static int bushels; 	// Bushels in storage
	static int pop;		// Population of city
	static int immigrants;	// How many folks immigrated
	static int starved;	// How many died of starvation
	static int rats;	// How many bushels rats destroyed 
	static int harvest;	// Size of last year's harvest
	static int acreCost;	// Cost of an acre in bushels
   
	static boolean plague; 	// Plagues struck this turn.
	static int plagueCount; // How many died of plague


	// Game finals: (some of these will probably change to vars. when
	//	easy/hard difficulty is added.)
	private final static int PLANT_MAX_PER_PERSON = 10;
	private final static int PLANT_ACRES_PER_BUSHEL = 2;
	private final static int BUSHELS_NEEDED_PER_PERSON = 20;
	private final static int MAX_RAT_PCT = 60;
	private final static int PLAGUE_PCT = 10;
	private final static int MAX_IMMIGRANTS = 50;
	private final static int AVG_YIELD = 3;

	// finals locating ui objects:
	private final static int W=160;	// Width
	private final static int H=160; // Height
	private final static int L=0; 	// Left edge
	private final static int R=159; // Right edge
	private final static int T=0; 	// Top edge
	private final static int B=159; // Bottom edge
	private final static int G_H=15;// generic height used by many objects
	private final static int YEAR_X=120;
	private final static int YEAR_Y=2;
	private final static int YEAR_W=W-YEAR_X+1;
	
	private final static int OUT_H=78;
	
	private final static int[] LINES_A = {94,95};

	private final static int OUT_Y=16;  
	private final static int STATUS_Y=98;
	private final static int BUYSELL_Y=112;
	private final static int FEED_Y=128;
	private final static int PLANT_Y=144;
	private final static int EXIT_Y=128;
	private final static int DONE_Y=144;

	private final static int[][] LABEL_LOCS = 
		{
			{0, BUYSELL_Y, 70, G_H},
			{0, FEED_Y, 55, G_H},
			{0, PLANT_Y, 55, G_H}
		};

	private final static String[] LABEL_TXTS =
		{
			"Trade Acres:",
			"Feed Bushels:",
			"Plant Acres:"
		};

	private final static int E_X=55;	// x of *Edit
	private final static int E_W=25;	// w of *Edit
	private final static int ED_B_W=29;	// w of Exit & Done Buttons
	private final static int BS_B_W=22;	// w of Buy & Sell Buttons

	/*
	 * X Layouts:	(button widths: Buy&Sell 24, Exit&Done 32, others 15)
	 *
 	 * Buy/Sell Acres:  
	 *   [Label 0..69] [Edit 55..79] [- 83] [+ 99] [Buy 115] [Sell 138]
	 *
	 * Feed:
	 *   [Label 0..54] [Edit 55..79] [ 83] [R 99] [! 115] [Exit 131]
	 * 
	 * Plant:
	 *   [Label 0..54] [Edit 55..79] [ 83] [R 99] [> 115] [Done 131]
	 *
	 */

	private final static int BS_M_X=83;	// x of Minus Button
	private final static int BS_P_X=99;	// x of Plus Button
	private final static int BUY_X=115;	// x of Buy Button
	private final static int SELL_X=138;	// x of Sell Button

	private final static int PF_A_X=83;	// x of A Buttons
	private final static int RB_X=99;	// x of *RbButton

	private final static int PLANT_MAX_X=115;  // x of Plant Max Button
	private final static int FEED_NEEDED_X=115;// x of Feed Needed Button
	private final static int ED_B_X=131;	// x of Edit & Done Buttons


	private final static String aboutText =

		TITLE + "\n" + VERSION + "\n" + VERSIONDATE;

	//"Greetings, Wabburami.";

	private final static String helpText1 = 

	"You have"+
	" inherited control of Babylon and are charged with"+
	" growing it into a great nation.  You must utilize careful"+
	" land management, distribute food to your subjects, and permit"+
	" those subjects to tend to your crops.  Rats, starvation, and"+
	" plague are the enemies which you must fight.";

	//"against every step of the way.";
        
	private final static String helpText2 =

	"Each year you must decide whether to buy or sell land, how much grain"+
	" to feed the people, and how many acres to plant.  Once you've"+
	" allocated your resources by entering the appropriate amounts and"+
	" tapping the 'Done' button, you will be told of the"+
	" results of your decisions.";
	
	//"The game ends only when you sell all"+
	//" your land or tap the 'Exit' button.";
            
	//"Go now -- Babylon awaits your enlightened rule.";

	private final static String historyText =	

	"Wabburami is a Waba version of the classic Hammurabi game.  Its"+
	" implementation is based on the following versions:\n"+ 

	"Focal version by ? DEC programmer\n"+
	"Basic version by David H. Ahl\n"+
	"C version by ? SCO contributer\n"+
	"Java KVM version by Jim Lesko\n"+  //JavaOne 1999
	"Waba (this) version by Dave Archer";
         

	private final static String keyText1 = 

	"To save time writing digits in the 3 numeric fields, you may also"+
	" use the following buttons as shortcuts.\n"+
	"\nTrading Buttons:\n"+
	" [-] decreases acres to trade\n"+
	" [+] increases acres to trade\n"+
	" [Buy] buys # of acres displayed\n"+
	" [Sell] sells # of acres displayed";

	private final static String keyText2 =
	"Feed Buttons:\n"+
	" [A] feed enough people to tend crops\n"+
	" [R] feed with remaining bushels\n"+
	" [!] feed enough for all people\n"+
	"Plant Buttons:\n"+
	" [A] plant enough for average yield\n"+
	" [R] plant with remaining bushels\n"+
	" [>] plant as many acres as possible";
	

	/** 
	 *  App starts here.  Need to check for saved game and/or prompt
	 *  for new game.  
	 *
	 *  Currently just starts a new game.
	 */ 
	public Wabburami() {
		super(TITLE); // adding ,true double-buffers but is slow
		r=new DLWRandom();
		fm=getFontMetrics(defaultFont);
		lineAscent=fm.getAscent();
		lineHeight=fm.getHeight();

		String[] mls1={"File","New","Quit"};
		String[] mls2={"Options","Help1","Help2","Key1","Key2","History","About"};
		mb = new MenuBar();
		mb.add(new Menu(mls1));
		mb.add(new Menu(mls2));
		setMenuBar(mb);

		InitNewGame(false); // init easy game
		StartGame();
	}

		
	/**
	 *  Initialize variables for a new game.
	 *
	 */
	private void InitNewGame(boolean hard) {
		this.hard = hard;
	  	year 	= 1;   
		acres 	= 1000;
		bushels = 2800; 
		pop 	= 100;  
		starved	= 0;
		rats	= 200;
		harvest = 3000; 
		acreCost= 0; 
		immigrants = 5;
		plague 	= false;
		plagueCount=0;
	}


	/**
	 *  Game (new or saved) starts here.  Create UI structures, 
	 *  display beginning info, and begin allowing UI events.
	 */
	private void StartGame() {
		CreateUI();
		// show first (or last)  turn
		showReport();

		playing = true;
		this.repaint();
	}

	
	/**
	 *  Generic Button setup.
	 *
	 */
	private Button addNewButton(String s, int x, int y, int w, int h) {
		Button b = new Button(s);
		b.setRect(x,y,w,h);
		add(b);
		return b;
	}

		
	/**
	 *  Create UI structures.
	 *
	 */
	private void CreateUI() {
		// add buttons
		exitButton = addNewButton("Exit", ED_B_X, EXIT_Y, ED_B_W, G_H);
		doneButton = addNewButton("Done", ED_B_X, DONE_Y, ED_B_W, G_H);
		buyButton = addNewButton("Buy", BUY_X, BUYSELL_Y, BS_B_W, G_H);
		sellButton = addNewButton("Sell", SELL_X, BUYSELL_Y, BS_B_W, G_H);
		feedNeededButton = addNewButton("!", FEED_NEEDED_X, FEED_Y, G_H, G_H);
		plantMaxButton = addNewButton(">", PLANT_MAX_X, PLANT_Y, G_H, G_H);
		feedRbButton = addNewButton("R", RB_X, FEED_Y, G_H, G_H);
		plantRbButton = addNewButton("R", RB_X, PLANT_Y, G_H, G_H);
		bsMinusButton = addNewButton("-", BS_M_X, BUYSELL_Y, G_H, G_H);
		bsPlusButton = addNewButton("+", BS_P_X, BUYSELL_Y, G_H, G_H);
		feedAcresButton = addNewButton("A", PF_A_X, FEED_Y, G_H, G_H);
		plantAvgButton = addNewButton("A", PF_A_X, PLANT_Y, G_H, G_H);



		// create a place to output text
		out = new OutputBox(0,OUT_Y,W,OUT_H);
		add(out);

		// add static labels
		for(int i=0;i<LABEL_TXTS.length;i++) {
			//System.err.println("Label "+LABEL_TXTS[i]);
			Label l = new Label(LABEL_TXTS[i]);
			l.setRect(LABEL_LOCS[i][0],LABEL_LOCS[i][1],LABEL_LOCS[i][2],LABEL_LOCS[i][3]);
			add(l);
		}

		// add status label
		statusLabel = new Label("");
		statusLabel.setRect(0, STATUS_Y, W, lineHeight);
		add(statusLabel);


		// add edit controls
		buysellEdit = new NumericEdit();
		buysellEdit.setRect(E_X, BUYSELL_Y, E_W, G_H);	
		buysellEdit.setInt(5);
		add(buysellEdit);
		
		feedEdit = new NumericEdit();
		feedEdit.setRect(E_X,FEED_Y,E_W,G_H);
		feedEdit.setInt(2000);
		add(feedEdit);

                plantEdit = new NumericEdit();
                plantEdit.setRect(E_X,PLANT_Y,E_W,G_H);
                plantEdit.setInt(1000);
                add(plantEdit);
	}


	public void clearRect(Graphics g, int x, int y, int w, int h) {
		g.setColor(255,255,255);
		g.fillRect(x,y,w,h);
		g.setColor(0,0,0);
	}


	public void onPaint(Graphics g) {
		//System.err.println("onPaint");

		// Year display is next to Title, not in out box
		clearRect(g,YEAR_X,YEAR_Y,YEAR_W,lineAscent);
		g.drawText("Year "+year,YEAR_X,YEAR_Y);

		// Draw lines as per LINES_A
		for(int i=0;i<LINES_A.length;i++)
			g.drawLine(T,LINES_A[i],R,LINES_A[i]);
	 }


	/**
	 * Update info. in status line.
	 */
	private void updateStatus() {
		int surplus = calcSurplus();
		statusLabel.setText("B: "+bushels+"  P: "+pop+"  A: "+acres+"  Surplus: "+surplus);
		statusLabel.repaint();
	}


	/**
	 *  Refresh screen with this turn's info. 
	 */
	private void showReport() {

		// draw the border between the top text and the input area
		//g.drawLine(0,14,159,14);

		out.clear();

		// Tell user how this turn went
		if (plague) {
			plagueCount = pop;
			pop /= 2;
			plagueCount -= pop;
			out.println("Plague! "+plagueCount+ ((plagueCount == 1) ?
            		" person" : " people") +" died, "+starved+" starved,");
      		} else
         		out.println("Last year "+starved+((starved == 1) ?
            		" person" : " people") + " starved");
      
    		out.println("and "+immigrants+((immigrants == 1) ?
     			    " person" : " people") + " entered the city.");
      
  		out.println("The population is now "+pop+".");
      
	      	out.println("We harvested "+harvest+" bushels at "+
         		acreCost+" BPA.");
      
      		out.println("Rats ate "+rats+" bushels, leaving "+
         		bushels+".");
      
      		out.println("The city owns "+acres+" acres of land.");
      
		acreCost = r.random(6) + 22;
      
	      	out.println("Land is worth "+acreCost+" an acre.");
      
		updateStatus();
		this.repaint();
	}

	/**
	 *  Events only checked while playing.
	 *  (Presumably none of these events will get hit when not 
	 *  playing because the objects won't exist, but this also
	 *  makes onEvent look a bit neater.)
	 *
	 */
	private void playingEvent(Event e) {
      		if (e.type == ControlEvent.PRESSED) {
			if (e.target instanceof Button) {	
				// pressed buttons
				if (e.target == exitButton)
       			  		endGame();
				else if (e.target == doneButton)
      		  			processTurn();
				else if (e.target == buyButton)
					buyAcres();
				else if (e.target == sellButton)
					sellAcres();
				else if (e.target == plantMaxButton)
					plantMax();
				else if (e.target == feedNeededButton)
					feedNeeded();
				else if (e.target == plantRbButton)
					plantRb();
				else if (e.target == feedRbButton)
					feedRb();
				else if (e.target == bsMinusButton)
					modifyBs(-1);
				else if (e.target == bsPlusButton)
					modifyBs(1);
				else if (e.target == feedAcresButton)
					feedAcres();
				else if (e.target == plantAvgButton)
					plantAvg();

			}
		}
	}
	
	/**  
    	 *  Checks for events.  Responsible for updating state of the
    	 *  game, exiting, acknowledging errors, etc.
     	 */
	public void onEvent(Event e) {
		if (playing)
			playingEvent(e);  // check playing events

      		if (e.type == ControlEvent.PRESSED) {
			// check menu items being selected
			if (e.target instanceof Menu) {
				String sel=((Menu)e.target).getSelected();
				if (sel.equals("Quit"))	{
					endGame();
				} else if (sel.equals("About")) {
					Dialog id = new Dialog("About",0,80,160,80);
					id.out.printlnCentered(aboutText);
					add(id);
					return;
				} else if (sel.equals("History")) {
					Dialog h = new Dialog("History",0,16,160,height-16,historyText);
					add(h);
					return;
				} else if (sel.equals("Help1")) {
					Dialog h = new Dialog("Help1",0,16,160,height-16,helpText1);
					add(h);
					return;
				} else if (sel.equals("Help2")) {
					Dialog h = new Dialog("Help2",0,16,160,height-16,helpText2);
					add(h);
					return;
				} else if (sel.equals("Key1")) 
					add(new Dialog("Key1",0,16,160,height-16,keyText1));
				else if (sel.equals("Key2"))
					add(new Dialog("Key2",0,16,160,height-16,keyText2));
				
			}
		}				
  	 }


	/**
	 * modify buy/sell acres by i
	 *
	 */
	private void modifyBs(int i) {
		int v = buysellEdit.getInt();

		v += i;
		if (v < 0)
			v = 0;

		buysellEdit.setInt(v);
	}

	
	/**
	 * buy some acres
	 *
	 */
	private void buyAcres() {
		int value = buysellEdit.getInt();
		if ((value*acreCost) > bushels) 
			showError();
		else {
			bushels -= value*acreCost;
			acres += value;
		}
		updateStatus();
	}


	/**
	 * sell some acres
	 *
	 */
	private void sellAcres() {
		int value = buysellEdit.getInt();
		if (value > acres)
			showError();
		else {
			bushels += value*acreCost;
			acres -= value;
		}
		// be nice and reduce acres being planted if needed

		if (acres < plantEdit.getInt())
			plantEdit.setInt(acres);
		updateStatus();
	}
		

	/**
	 * plant maximum possible acres
	 *
	 */
	private void plantMax() {
		int m;

		m=pop * PLANT_MAX_PER_PERSON;

		if (m>acres)
			m=acres;

		plantEdit.setInt(m);
		plantEdit.repaint();
		updateStatus();
	}
		

	/**
	 * try to feed everybody just enough
	 *
	 */
	private void feedNeeded() {
		int m;

		m = pop * BUSHELS_NEEDED_PER_PERSON;

		if (m > bushels)
			m = bushels;

		feedEdit.setInt(m);
		feedEdit.repaint();
		updateStatus();
	}


	/**
	 * plant remaining bushels (after bushels used feeding people)
	 *
	 */
	private void plantRb() {
		int m = ( bushels-feedEdit.getInt() ) * PLANT_ACRES_PER_BUSHEL;
		if (m < 0 )
			m = 0;
		plantEdit.setInt(m);
		plantEdit.repaint();
		updateStatus();
	}

		
	/**
	 * feed remaining bushels (after bushels used for planting)
	 *
	 */
	private void feedRb() {
		int m = bushels-(plantEdit.getInt()/PLANT_ACRES_PER_BUSHEL);

		if (m < 0 )
			m = 0;
		feedEdit.setInt(m);
		feedEdit.repaint();
		updateStatus();
	}

	
	/**
	 * feed just enough people to tend all fields
	 *
	 */
	private void feedAcres() {
		feedEdit.setInt(acres/PLANT_MAX_PER_PERSON*BUSHELS_NEEDED_PER_PERSON);
		feedEdit.repaint();
		updateStatus();
	}


	/**
	 * plant just enough to feed everyone with an avg. yield
	 *
	 */
	private void plantAvg() {
		plantEdit.setInt((pop*BUSHELS_NEEDED_PER_PERSON)/AVG_YIELD);
		plantEdit.repaint();
		updateStatus();
	}


	private int calcSurplus() {
		int feed = feedEdit.getInt();
		int plant = plantEdit.getInt();

		return bushels-feed-(plant/PLANT_ACRES_PER_BUSHEL);
	}

	private void processTurn() {
		int feed = feedEdit.getInt();
		int plant = plantEdit.getInt();

            	if (feed > bushels) {
			// trying to feed more bushels than we have
			showError();
			return;
            	}

		if (plant > acres   || 
                plant / PLANT_ACRES_PER_BUSHEL > (bushels-feed) ||
                plant     > pop * PLANT_MAX_PER_PERSON) {
			// trying to plant more acres than we have, or
			// trying to plant more bushels than we have, or
			// trying to plant more than the population can
			showError();
			return;
		}
			
		
		bushels -= feed;
		starved = pop - feed / BUSHELS_NEEDED_PER_PERSON;
		immigrants = 0;
             
		if (starved < 0) {
			immigrants = -1 * (starved / 2);
			starved = 0;
		}

               	// Essentially, the turn's over; update state &
               	// figure out random events (plague): 
	        bushels -= plant / PLANT_ACRES_PER_BUSHEL;
       		acreCost = r.random(5) + 1;
	        harvest = acreCost * plant;
		int p = r.random(MAX_RAT_PCT*10);
               	//ratPop = r.random(600); 
               	//rats = (ratPop * (bushels + harvest)) / 1000; 
		if (hard)
			rats = p * (bushels + harvest) / 1000;
		else
			rats = p * bushels / 1000;
        	bushels = bushels - rats + harvest;
               
		//plague = (r.random(10) == 0);
		plague = (r.random(100) < PLAGUE_PCT);
               	immigrants += (5 - acreCost) * bushels / 600 + 1;
               	immigrants = (immigrants > MAX_IMMIGRANTS) ? MAX_IMMIGRANTS : immigrants;
               	immigrants = (immigrants < 0)  ? 0  : immigrants;
               	pop += immigrants - starved;
      
		year ++;
		showReport();
		this.repaint();
   	}
       

	/**
	 *  Game has ended either by user request or naturally.
	 *
	 *  This is a good place to add final score, saving stats,
	 *  that sort of thing.  (But not a saved game.)
	 */
	public void endGame () {
		playing = false;
		exit(0);
	}

	/**
	 *  Exit caught by VM.  This is called after exit(0) in endGame,
	 *  so when we get here, we should check the value of playing to
	 *  see if we need to save the game.
	 *
	 */
	public void onExit() {
		if (playing)
			; // save game
		else
			; // remove old saved game
	}

   /**
    *  Let the player know that something isn't right.  
    */
   private void showError() {
	add(new Dialog("Wabburami, Think Again",0,80,160,80,
		"You do not have enough bushels, acres, or people to do "+
		"as you request."));
   }
}

