import java.applet.*;
import java.awt.*;
import java.util.*;
import java.net.*;
import java.io.*;

public class napp extends Applet
{
	public void start()
	{
		nShopPanel.initValues(this);
	}
	
	public void prod(String iD, String it, String pr, String sh)
	{
		nShopPanel.prod(iD,it,pr,sh);
	}
}

public class nShopFrame extends Frame
{
	void formPanel()
	{
		add(nShopPanel.clearCart);
		add(nShopPanel.help);
		add(nShopPanel.up);
		add(nShopPanel.down);
		add(nShopPanel.addItem);
		add(nShopPanel.addTenItems);
		add(nShopPanel.removeItem);
		add(nShopPanel.removeTenItems);
		add(nShopPanel.finalizeOrder);
		add(nShopPanel.reset);
		add(nShopPanel.listOfProducts);
		add(nShopPanel.itemChosen);
		add(nShopPanel.subLabel);
		add(nShopPanel.subTotal);
		setBackground(nShopPanel.backColor);	//also set twice in nShopPanel
	}
	
	public boolean handleEvent(Event event)
	{
		if(event.id == Event.WINDOW_DESTROY)
		{
			nShopPanel.closeWindow();
			return true;
		}
		else if(event.id ==Event.LIST_SELECT)
		{
			nShopPanel.updateHeading();
			return true;
		}
		return super.handleEvent(event);
	}
	
	public boolean action(Event event, Object what)
	{
		if(event.target == nShopPanel.clearCart)
		{
			nShopPanel.clearCart();
			//setVisible(false);
			hide();
			return true;
		}
		else if(event.target == nShopPanel.up)
		{
			nShopPanel.up();
			return true;
		}
		else if(event.target == nShopPanel.down)
		{
			nShopPanel.down();
			return true;
		}
		else if(event.target == nShopPanel.addItem)
		{
			nShopPanel.addAnItem(new Integer(1));
			return true;
		}
		else if(event.target == nShopPanel.addTenItems)
		{
			nShopPanel.addAnItem(new Integer(10));
			return true;
		}
		else if(event.target == nShopPanel.removeItem)
		{
			nShopPanel.removeAnItem(new Integer(1));
			return true;
		}
		else if(event.target == nShopPanel.removeTenItems)
		{
			nShopPanel.removeAnItem(new Integer(10));
			return true;
		}
		else if(event.target == nShopPanel.finalizeOrder)
		{
			nShopPanel.orderJump();
			return true;
		}
		else if(event.target == nShopPanel.help)
		{
			nShopPanel.helpJump();
			return true;
		}
		else if(event.target == nShopPanel.reset)
		{
			nShopPanel.resetOrder();
			return true;
		}
		else
			return true;
	}
}

abstract class nShopPanel
{
	static Vector products = new Vector(100);
	static String newPrices = "";
	static String ID = "";
	static String item = "";
	static Double price = new Double(0.0);
	static double subtotal = 0.0;
	static int limit = 0;
	static int numInList = 0;
	static String substring = "";
	static String shipping = "";
	static String[] resetID;	//to store reset values; a Vector class messes up
	static Integer[] resetNum;
	static String[] resetItem;
	static Double[] resetPrice;
	static String[] resetShipping;
	static int resetNumItems;
	static nShopFrame nshopFrame = new nShopFrame();
	static String frameTitle;
	static Boolean firstTime = new Boolean(true);
	static Boolean dataNotRead = new Boolean(true);
	static String temp = "";
	static Integer currentNumber = new Integer(0);
	static String codebase;
	static StringBuffer buffer;
	static Color backColor = new Color(255,50,50);
	static napp parent;
	static TextField itemChosen = new TextField();
	static List listOfProducts = new List();
	static TextField subTotal = new TextField("$ 0.0");
	static Label subLabel = new Label(" Subtotal . . .");
	static Button clearCart = new Button("Clear Cart");
	static Button help = new Button("Help");
	static Button addItem = new Button("More");
	static Button addTenItems = new Button("Ten More");;
	static Button removeItem = new Button("Less");
	static Button removeTenItems = new Button("Ten Less");
	static Button finalizeOrder = new Button("Checkout ...");
	static Button reset = new Button("Reset");
	static Button up = new Button("Up");
	static Button down = new Button("Down");
	static Font small = new Font("Courier",Font.PLAIN,10);
	static Font medium = new Font("TimesRoman",Font.PLAIN,12);
	static Font big = new Font("Dialog",Font.BOLD,20);
	static Font mediumBold = new Font("TimesRoman",Font.BOLD,13);
	static Font moneyBold = new Font("Courier",Font.BOLD,13);
	static long startTime;
	static AppletContext ac;
	
	static void formPanelElements()
	{
		nshopFrame.reshape(154,110,343,182);	//was 154,110,343,177 - also resized elsewhere
		nshopFrame.setLayout(null);	//if you use layout manager, you can't get background color
		
		clearCart.setFont(medium);
		clearCart.setBackground(Color.yellow);
		clearCart.reshape(220,64,66,15);

		help.setFont(medium);
		help.setBackground(Color.yellow);
		help.reshape(290,64,41,15);
		
		up.setFont(medium);
		up.reshape(225,82,38,15);
		up.setBackground(Color.yellow);
		
		down.setFont(medium);
		down.setBackground(Color.yellow);
		down.reshape(258,82,67,15);
		
		addItem.setFont(medium);
		addItem.setBackground(Color.yellow);
		addItem.reshape(225,99,38,15);
		
		addTenItems.setFont(medium);
		addTenItems.setBackground(Color.yellow);
		addTenItems.reshape(258,99,67,15);
		
		removeItem.setFont(medium);
		removeItem.setBackground(Color.yellow);
		removeItem.reshape(225,116,38,15);	//was 220,90,38,15
		
		removeTenItems.setBackground(Color.yellow);
		removeTenItems.setFont(medium);
		removeTenItems.reshape(258,116,67,15);	//was 253,90,67,15
		
		finalizeOrder.setFont(mediumBold);
		finalizeOrder.setBackground(Color.yellow);
		finalizeOrder.reshape(218,134,75,16);	//was 220,131,106,16
		
		reset.setFont(small);
		reset.setBackground(Color.yellow);
		reset.reshape(297,134,35,16);
		reset.disable();
		
		listOfProducts.setFont(small);
		listOfProducts.setBackground(Color.white);
		listOfProducts.reshape(10,58,200,74);//(10,58,200,89);	//was 5,35
		
		subTotal.setFont(moneyBold);
		subTotal.setBackground(Color.white);
		subTotal.setEditable(false);
		subTotal.reshape(90,132,120,20);
		
		subLabel.setFont(mediumBold);
		subLabel.setBackground(new Color(160,160,215));
		subLabel.reshape(12,132,78,20);
		
		itemChosen.setFont(big);
		itemChosen.setBackground(Color.white);
		itemChosen.setEditable(false);
		itemChosen.reshape(10,28,320,30);

		nshopFrame.setFont(small);
		nshopFrame.setBackground(new Color(255,50,50));	//also set in formAWTFrame and nshopFrame
	}
	
	static void init()
	{
		if(dataNotRead.booleanValue())
		{
			buffer = new StringBuffer();
			priceUpdate();	//reads in the files if not already done
		}
		if(products.size() > 2)
			upFrame();
	}
	
	static void initValues(Applet p)
	{
		parent = (napp)p;
		codebase = parent.getCodeBase().toString();
		frameTitle = new String(codebase.substring(7,codebase.length()));
		nshopFrame.setTitle("From: " + frameTitle);
		ac = parent.getAppletContext();
		init();
		if(firstTime.booleanValue())
		{
			formPanelElements();	//Part 1 - simulates a constructor for nShopPanel; note nshopFrame is initialized in nShopPanel declaration
			nshopFrame.formPanel();	//Part 2 - simulates a constructor for nShopPanel
			firstTime = new Boolean(false);
		}
	}
	
	static void upFrame()
	{
		nshopFrame.requestFocus();
		nshopFrame.toFront();
		nshopFrame.show();
	}

	static void prod(String iD, String it, String pr, String sh)
	{
		if(dataNotRead.booleanValue())
			return;   //prevents null pointers if selection made while database still being read on slow connection.
		upFrame();
		if(pr.length() > 0)	//OK if price empty string, but then there MUST be an entry in the database. If this is missing, there will be an error.
		{
			if(pr.charAt(0) == '$')
				pr = new String(pr.substring(1));	//strip $ from the price
			try	//Trap HTML price coding errors. These could otherwise crash the cart!
			{
				Double.valueOf(pr);
			}
			catch (NumberFormatException e)
			{
				itemChosen.setText(" Oops! HTML price not a number");
				return;
			}
		}
		ID = new String(iD);
		String temp = new String(checkPrice(ID,new String(pr)));	//adjusts to database
		if(temp.equals(""))	//trap if no price in HTML AND ALSO no entry in database. This could really mess up the cart!
		{
			itemChosen.setText(" Oops! No price in our database!");
			return;
		}
		price = Double.valueOf(temp);	//we know now that price is OK, so proceed
		prod(ID, it, price, sh, new Integer(1));
		nshopFrame.resize(343,182);	//Netscape needs it repeated here, was 343,172
	}
	
	static void prod(String ID, String it, Double price, String sh, Integer purchaseAmount)
	{
		temp = it;
		if( (temp.length() > 21) && (temp.charAt(21) != '.') )
			item = temp.substring(0,20) + " ...";	//second condition prevents double ...s at reset
		else
			item = temp;
		shipping = new String(sh);
		reset.disable();
		if(products.size() > 0)
		{
			for(int i = 0; i < products.size()/5; i++)
			{
				if(products.elementAt(5*i).equals(ID))
				{
					products.setElementAt(new Integer(((Integer)products.elementAt(5*i + 1)).intValue() + purchaseAmount.intValue()), 5*i + 1);
					updateTheList(i,false);
					updateSubtotal();
					return;
				}
			}
		}
		products.addElement((String)ID);	//new purchase
		products.addElement(purchaseAmount);
		products.addElement((String)item);
		products.addElement((Double)price);
		products.addElement((String)shipping);
		numInList++;	//count in class List doesn't work
		updateTheList(products.size()/5 - 1,true);
		updateSubtotal();
	}
	
	static void updateTheList(int row, boolean newItem)
	{
		String temp1 = products.elementAt(5*row+1).toString() + " ";
		temp1 += (String)products.elementAt(5*row+2) + "  ";
		String price = "$" + currency((Double)products.elementAt(5*row+3));
		if(temp1.length() > (28 - price.length()))
			temp1 = temp1.substring(0,(25 - price.length())) + "...";
		int spaces = 29 - temp1.length() - price.length();
		for (int j = 0; j < spaces; j++)
		{
			temp1 += " ";
		}
		temp1 += price;
		if(newItem)
		{
			listOfProducts.addItem(temp1);
			row = numInList - 1;
		}
		else
			listOfProducts.replaceItem(temp1,row);
		listOfProducts.select(row);
		listOfProducts.makeVisible(row);
		updateHeading();	//the previous two commands don't trigger an event, so need this
	}
	
	 static String currency(Double money)
	{
		String temp = money.toString();
		if((temp.indexOf(".") > 0) && (temp.indexOf(".") == (temp.length() - 2)))
		{
			temp = temp + "0";
		}
		else if((temp.indexOf(".") > 0) && (temp.indexOf(".") < (temp.length() - 2)))
		{
			temp = temp.substring(0,temp.indexOf(".") + 3);
		}
		return temp;
	}
	
	static void clearCart()
	{
		if(products.size() == 0)
			return;
		products.removeAllElements();
		listOfProducts.select(numInList - 1);	//doesn't delete first item in IE if there are three items, and first item happens to be selected, so select another
		for(int i = 0; i < numInList ; i++)
		{
			listOfProducts.delItem(0);
		}
		itemChosen.setText("");
		numInList = 0;	//count in class List doesn't work
	}
	
	static void updateHeading()
	{
		if(listOfProducts.getSelectedItem() != null)
		{
			itemChosen.setText(listOfProducts.getSelectedItem().substring(0,listOfProducts.getSelectedItem().indexOf('$') + 1) + String.valueOf(  currency( new Double( ((Integer)products.elementAt(listOfProducts.getSelectedIndex()*5 + 1)).intValue() * ((Double)products.elementAt(listOfProducts.getSelectedIndex()*5 + 3)).doubleValue() ) ) ));
		}
		else
			itemChosen.setText("");
	}
	
	static void updateSubtotal()
	{
		subtotal = 0.0;
		for(int i = 0; i < products.size()/5; i++)
		{
			subtotal += ((Double)products.elementAt(5*i + 3)).doubleValue() * ((Integer)products.elementAt(5*i + 1)).intValue();
		}
		substring = "$" + currency(new Double(subtotal));
		limit = 13 - substring.length();
		for(int i = 0; i < limit; i++)	
			substring = " " + substring;
		subTotal.setText(substring);
	}
	
	static void up()
	{
		if(listOfProducts.getSelectedIndex() > 0)
		{
			listOfProducts.select(listOfProducts.getSelectedIndex() - 1);
			updateHeading();
		}
	}
	
	static void down()
	{
		if( (listOfProducts.getSelectedIndex() >= 0) && (listOfProducts.getSelectedIndex() < numInList) )
		{
			listOfProducts.select(listOfProducts.getSelectedIndex() + 1);
			updateHeading();
		}
	}
	
	static void addAnItem(Integer m)
	{
		if(listOfProducts.getSelectedIndex() >= 0)
		{
			products.setElementAt(new Integer(((Integer)products.elementAt(listOfProducts.getSelectedIndex()*5 + 1)).intValue() + m.intValue()),listOfProducts.getSelectedIndex()*5 + 1);
			updateTheList(listOfProducts.getSelectedIndex(),false);
			updateSubtotal();
		}
	}
	
	static void removeAnItem(Integer m)
	{
		limit = listOfProducts.getSelectedIndex();
		if(limit >= 0)
		{
			if( (((Integer)products.elementAt(5*limit + 1)).intValue()-m.intValue()) > 0)	//decrement
			{
				products.setElementAt(new Integer(((Integer)products.elementAt(5*limit + 1)).intValue() - m.intValue()),5*limit + 1);
				updateTheList(listOfProducts.getSelectedIndex(),false);
			}
			else	//remove completely
			{
				products.removeElementAt(5*limit + 4);	//be sure to remove elements from the top first, or else renumbering messes you up
				products.removeElementAt(5*limit + 3);
				products.removeElementAt(5*limit + 2);
				products.removeElementAt(5*limit + 1);
				products.removeElementAt(5*limit);
				if(limit > 0)
					listOfProducts.select(limit - 1);	//Netscape won't transfer focus properly, so help it
				else if(numInList > 1)
					listOfProducts.select(1);
				listOfProducts.remove(limit);
				numInList--;
				itemChosen.setText("");
				updateHeading();	//another item will be selected, but it needs to be echoed into heading
			}
			updateSubtotal();
		}
	}
	
	static String orderString()
	{
		int size = products.size();	//to prepare for reset
		resetID = new String[size];	//dimension may be too large, but not by much; Vector class messes up
		resetNum = new Integer[size];
		resetItem = new String[size];
		resetPrice = new Double[size];
		resetShipping = new String[size];
				
		String temp = "";
		double total = 0;
		int currentNum = currentNumber.intValue();
		temp = "?VENDOR=" + eliminateSpaces(codebase) + "&";;
		temp += "CURRNUM=" + currentNum + "&";
		int i = 0;
		while(products.size()/5 > 0)
		{
			String tryMore = "";
			tryMore += "I" + i + "=" + eliminateSpaces((String)products.elementAt(0)) + "&";
			tryMore += "Q" + i + "=" + products.elementAt(1) + "&";
			tryMore += "D" + i + "=" + eliminateSpaces((String)products.elementAt(2)) + "&";
			tryMore += "P" + i + "=" + currency((Double)products.elementAt(3)) + "&";
			tryMore += "S" + i + "=" + eliminateSpaces((String)products.elementAt(4)) + "&";
			i++;
			if(temp.concat(tryMore).length() < 900)	//900 seems OK for Explorer
			{
				currentNum++;
				temp += tryMore;
				total = total + ((Integer)products.elementAt(1)).intValue()*((Double)products.elementAt(3)).doubleValue();
				
				resetID[i - 1] = (String)products.elementAt(0);	//prepare for possible reset
				resetNum[i - 1] = (Integer)products.elementAt(1);
				resetItem[i - 1] = (String)products.elementAt(2);
				resetPrice[i - 1] = (Double)products.elementAt(3);
				resetShipping[i - 1] = (String)products.elementAt(4);
				resetNumItems = i - 1;	//end prepare for possible reset
				
				listOfProducts.select(0);
				removeAnItem(new Integer(30000));	//make sure to get them all
			}
			else
			{
				break;					
			}
			currentNumber = new Integer(currentNum);
		}
		if(products.size()/5 > 0)
			temp += "More=true&";
		else
		{
			temp += "More=false&";
			itemChosen.setText(" Reset restores your order");
		}
		temp += "NUM=" + i + "&";
		temp += "TOT=" + currency(new Double(total)) + "&";
		reset.enable();
		return temp;
	}

	static String eliminateSpaces(String s)
	{
		String temp = s;
		while(temp.indexOf(' ') > 0)
		{
			temp = temp.substring(0,temp.indexOf(' ')) + "%20" + temp.substring(temp.indexOf(' ') + 1);
		}
		return temp;
	}
	
	static void priceUpdate()	//reads database
	{
		//note that boolean dataNotRead is not reset from default if page jump is made during read. This allows a second try on the next page. It IS reset if the file is not present, so an attempt to read is then only made once.
		//format of data file matches Access data base written to text file. It can be edited with a text editor.
		//database is present in a text file called update.txt, in same directory as applet code.
		try
		{
			URL u = new URL(codebase + "update.txt");
			URLConnection conn = null;
			DataInputStream data = null;
			String line;
			conn = u.openConnection();
			conn.connect();
			data = new DataInputStream(new BufferedInputStream(conn.getInputStream(),10240));	//set buffer size of 10K; result is placed in Stringbuffer.
			while((line = data.readLine()) != null)
			{
				buffer.append(line);
			}
		}
		catch(IOException ex)
		{
			nShopPanel.dataNotRead = new Boolean(false);
			return;
		}
		catch(Exception e)
		{
		}
		nShopPanel.newPrices = buffer.toString();	//this string is now the data base. Search is made with string functions; this is very fast.
		nShopPanel.dataNotRead = new Boolean(false);
	}
	
	static String checkPrice(String ID, String price)	//compare to database
	{
		if(newPrices.equals(""))	//no database
		   return price;
		if(ID.equals(newPrices.substring(1,newPrices.indexOf(',') - 1)))	//is first item in database; adjusted end points allow for quote marks from Access
		{
			String temp = newPrices.substring(newPrices.indexOf(',') + 2);	//strip away $ sign from Access
			String temp2 = temp.substring(0,temp.indexOf("\""));
			return temp2;	//carriage return and line feed are ignored by Java, goes directly to quote in Access
		}
		int index = newPrices.indexOf("\"" + ID + "\"");	//if surrounded by quotes as in Access
		if((index > 0) && (index < newPrices.length()))	//later in database
		{
			String temp = newPrices.substring(index + ID.length() + 4);	//as in Access; 2 extra quotes, and a $ sign
			if(temp.indexOf(",") > 0)
			{
				String temp2 = temp.substring(0,temp.indexOf("\""));
				return temp.substring(0,temp.indexOf("\""));	//not last item, or last but followed properly by comma; Access has extra dollar sign, then CR and LF followed by quote
			}
			else
			{
				return temp;	//JAVA appears to ignore any CR or LF at end of file, so no need to change things;
			}
		}
		else return price;
	}
	
	static void resetOrder()
	{
		reset.disable();
		for(int i = 0; i < resetNumItems + 1; i++)
		{
			prod(resetID[i],resetItem[i],resetPrice[i],resetShipping[i],resetNum[i]);
		}
	}
	
	static void pause(int time)
	{
		startTime = System.currentTimeMillis();
		while(System.currentTimeMillis() < startTime + time)
		{
			//infinite loop: create delay to bring up nShopFrame AFTER orderFrame is displayed
		}
	}
	
	static void closeWindow()
	{
		itemChosen.setText(" 'Clear Cart' closes window");
		pause(4000);
		updateHeading();
	}
	
	static void orderJump()
	{
		if(parent.isActive())
		{
			try
			{
				ac.showDocument(new URL("http://209.87.142.42/order.asp" + orderString()),"_blank");
				//Naming the window _order creates a new window if it doesn't exist, or uses the existing one if it does. Very nice. However, the order window can then get lost in the bottom or the back, and cannot then be brought back easily. Too bad.
			}
			catch (MalformedURLException me) {}
		}
		else
		{
			itemChosen.setText(" Finalize at merchant's site");
			pause(4000);
			updateHeading();
		}
	}

	static void helpJump()
	{
		if(parent.isActive())
		{
			try
			{
				ac.showDocument(new URL("http://209.87.142.42/help.htm"));
			}
			catch (MalformedURLException me) {}
		}
		else
		{
			itemChosen.setText(" Available at merchant's site");
			pause(4000);
			updateHeading();
		}
	}
}