package AppletInterface;

import amber.awt.event.ComponentWindowEvent;
import amber.awt.event.ComponentWindowListener;
import amber.server.application.ApplicationInterface;
import amber.server.component.*;
import amber.server.exception.ComponentHandlerException;
import amber.server.panel.BaseFrame;
import amber.type.server.XYConstraints;
import amber.utility.*;

import java.awt.*;
import java.awt.event.*;
import java.util.*;

/**
  * This class handles the requirements for the specific manipulation of panels within
  * specific applications. In a lot of ways this class functions very similarly to the
  * ApplicationInterface class.
  * This is the overall intelligence for this particular panel.
  *
  * @author Insert your name here
  * @version 1.0.0
  * @see amber.server.panel.BaseFrame
*/
import DataClasses.*;
import Validators.*;
import DatabaseCommunication.*;
import XmlCommunication.*;


/**
 * @author student
 *
 * This class is being used for updating user information
 * This starts with validation of user data, followed by sending
 * admin update to TMS and update user details in the application 
 * database.
 * 
 */
public class UpdateFrame
	extends BaseFrame
	implements ActionListener, ComponentWindowListener {
	public final int FRAMEWIDTH = 800;
	public final int FRAMEHEIGHT = 700;

	private User_Data userdata = getStockTradingApplication().getUser();
	private UserAddress_Data addressdata =
		getStockTradingApplication().getAddress();
	private Stock_Data[] listed = getStockTradingApplication().getStocks();
	private Stock_Data[] interested = userdata.getStocksInterested();

	private UserManager umanager = new UserManager();

	private ServletConnection servletconn = new ServletConnection();
	private XmlGenerator xmlgenerator = new XmlGenerator();
	private XmlUnmarshal xmlunmarshal = new XmlUnmarshal();

	private MainFrame caller;

	private FloatButtonHandler btnMain =
		new FloatButtonHandler(getParentApplication());
	private ImageHandler imgBanner = new ImageHandler(getParentApplication());
	private FloatButtonHandler btnUpdate =
		new FloatButtonHandler(getParentApplication());
	private FloatButtonHandler btnLogout =
		new FloatButtonHandler(getParentApplication());
	private LabelHandler lblInfo = new LabelHandler(getParentApplication());
	private LabelHandler lblPersonOrganisation =
		new LabelHandler(getParentApplication());
	private LabelHandler lblFirstName =
		new LabelHandler(getParentApplication());
	private TextFieldHandler txtFirstName =
		new TextFieldHandler(getParentApplication());
	private LabelHandler lblLastName = new LabelHandler(getParentApplication());
	private TextFieldHandler txtLastName =
		new TextFieldHandler(getParentApplication());
	private LabelHandler lblOrganisation =
		new LabelHandler(getParentApplication());
	private TextFieldHandler txtOrganisation =
		new TextFieldHandler(getParentApplication());
	private LabelHandler lblPhoneNumber =
		new LabelHandler(getParentApplication());
	private TextFieldHandler txtPhone =
		new TextFieldHandler(getParentApplication());
	private LabelHandler lblEmail = new LabelHandler(getParentApplication());
	private TextFieldHandler txtEmail =
		new TextFieldHandler(getParentApplication());
	private LabelHandler lblContactAddress =
		new LabelHandler(getParentApplication());
	private LabelHandler lblStreet = new LabelHandler(getParentApplication());
	private TextFieldHandler txtStreet =
		new TextFieldHandler(getParentApplication());
	private LabelHandler lblSuburb = new LabelHandler(getParentApplication());
	private TextFieldHandler txtSuburb =
		new TextFieldHandler(getParentApplication());
	private LabelHandler lblCity = new LabelHandler(getParentApplication());
	private TextFieldHandler txtCity =
		new TextFieldHandler(getParentApplication());
	private LabelHandler lblCountry = new LabelHandler(getParentApplication());
	private TextFieldHandler txtCountry =
		new TextFieldHandler(getParentApplication());
	private LabelHandler lblStockPreferences =
		new LabelHandler(getParentApplication());
	private ListHandler lstStocks = new ListHandler(getParentApplication());
	private ButtonHandler btnAdd = new ButtonHandler(getParentApplication());
	private ChoiceHandler chInterested =
		new ChoiceHandler(getParentApplication());
	private LabelHandler lblInterestedStocks =
		new LabelHandler(getParentApplication());
	private FloatButtonHandler btnUpdateLocal =
		new FloatButtonHandler(getParentApplication());

	private Font level1 = new Font("Arial", Font.BOLD, 14);
	private Font level2 = new Font("Arial", Font.ITALIC, 14);
	private LabelHandler lblPreBillingAmount =
		new LabelHandler(getParentApplication());
	private TextFieldHandler txtPreBillingAmount =
		new TextFieldHandler(getParentApplication());
	private LabelHandler lblAccountDetails =
		new LabelHandler(getParentApplication());
	private ButtonHandler btnRemove = new ButtonHandler(getParentApplication());

	/**
	 * This is the constructor for UpdateFrame.java
	 */
	public UpdateFrame() {
		super();
		defineComponents();
	}

	/**
	  * The initialising constructor.
	  * @param appHandler The handle to the main ApplicationInterface which
	  * handles the functions of the overall application.
	*/
	public UpdateFrame(ApplicationInterface appHandler) {
		super(appHandler);
		defineComponents();
	}
	/**
	  * The initialising constructor.
	  * @param appHandler The handle to the main ApplicationInterface which
	  * handles the functions of the overall application.
	*/
	public UpdateFrame(ApplicationInterface appHandler, MainFrame mf) {
		super(appHandler);
		this.caller = mf;
		defineComponents();
	}
	/**
	  * The initialising constructor.
	  * @param id The int containing the id of the corresponding remote component
	  * residing on the browser.
	  * @param appHandler The handle to the main ApplicationInterface which
	  * handles the functions of the overall application.
	*/
	public UpdateFrame(int id, ApplicationInterface appHandler) {
		super(id, appHandler);
		defineComponents();
	}

	/**
		* This function would normally never need to be called, but is required
		* to be defined as it is called internally.
		* Its specific purpose is to set the controls to a known state once
		* they are created.
	*/
	public void fillControls() {
	}

	/**
	  * This function determines if the panel can be closed.
	  * The derived panels must determine if this panel can close. If this is
	  * not possible the function should return false.
	  * @return boolean false if it is not possible to close this panel.
	*/
	public boolean canClose() {
		return true;
	}

	/**
	  * This function is called to save any required information in the panel.
	  * This function is called externally when another panel wishes to take
	  * over the base panel or when closing the panel.
	  * This function need not actually do something.
	*/
	public void saveData() {
	}

	/**
	  * This function is called to define the components which are a part of this
	  * panel.
	  * This function is called by the constructor to set up the normal static
	  * components and their locations.
	  * This method is required.
	*/
	protected void defineComponents() {
		try {
			setProperties();
			addComponents();
			addListeners();
			preFillDataFields();

		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
	/**
	 * This method is being used for setting the properties of components being
	 * used in this frame.
	 * @return void
	 */
	private void setProperties() {
		try {
			this.setTitle("Stock Trading Update");
			//this.setBackground(Color.LIGHT_GRAY);
			this.setBackground(Color.WHITE);
			imgBanner.setImage("Banner1.gif");

			lblInfo.setFont(level1);

			btnMain.setBackground(Color.LIGHT_GRAY);
			btnUpdate.setBackground(Color.LIGHT_GRAY);
			btnLogout.setBackground(Color.LIGHT_GRAY);
			btnUpdateLocal.setBackground(Color.LIGHT_GRAY);

			lblPersonOrganisation.setFont(level1);
			lblContactAddress.setFont(level1);
			lblStockPreferences.setFont(level1);
			lblInterestedStocks.setFont(level1);

			btnMain.setLabel("MAIN");
			btnUpdate.setLabel("UPDATE");
			btnLogout.setLabel("LOGOUT");
			lblInfo.setText("");
			lblFirstName.setText("First Name :");
			txtFirstName.setText("");
			lblPersonOrganisation.setText("Person/Organisation Details :");
			lblLastName.setText("Last Name :");
			txtLastName.setText("");
			lblOrganisation.setText("Organisation :");
			txtOrganisation.setText("");
			lblPhoneNumber.setText("Phone Number :");
			txtPhone.setText("");
			lblEmail.setText("Email Address :");
			txtEmail.setText("");
			lblContactAddress.setText("Contact Address :");
			lblStreet.setText("Street :");
			txtStreet.setText("");
			lblSuburb.setText("Suburb :");
			txtSuburb.setText("");
			lblCity.setText("City :");
			txtCity.setText("");
			lblCountry.setText("Country :");
			txtCountry.setText("");
			lblStockPreferences.setText("Listed Stock Options :");
			btnAdd.setLabel(">>");
			lblInterestedStocks.setText("Stocks interested in :");
			btnUpdateLocal.setLabel("UPDATE");
			lblSuburb.setText("Suburb :");

			btnUpdate.setEnabled(false);

			lblPreBillingAmount.setText("Add to account :");
			txtPreBillingAmount.setText("");
			lblAccountDetails.setText("Account Details :");
			btnRemove.setLabel("<<");
		} catch (Exception ex) {
			System.err.println(ex.toString());
		}
	}
	
	/**
	 * This method is being used for adding components to the 3 panels.
	 * @return void
	 */
	private void addComponents() {
		try {
			add(btnMain, new XYConstraints(164, 100, 154, 20));
			add(imgBanner, new XYConstraints(0, 0, 800, 100));
			add(btnUpdate, new XYConstraints(323, 100, 154, 20));
			add(btnLogout, new XYConstraints(482, 100, 154, 20));
			add(lblInfo, new XYConstraints(92, 540, 600, 20));
			add(lblPersonOrganisation, new XYConstraints(4, 124, 200, 20));
			add(lblFirstName, new XYConstraints(5, 150, 100, 20));
			add(txtFirstName, new XYConstraints(105, 150, 200, 20));
			add(lblLastName, new XYConstraints(5, 175, 100, 20));
			add(txtLastName, new XYConstraints(105, 175, 200, 20));
			add(lblOrganisation, new XYConstraints(5, 200, 100, 20));
			add(txtOrganisation, new XYConstraints(105, 200, 200, 20));
			add(lblPhoneNumber, new XYConstraints(5, 225, 100, 20));
			add(txtPhone, new XYConstraints(105, 225, 200, 20));
			add(lblEmail, new XYConstraints(5, 250, 100, 20));
			add(txtEmail, new XYConstraints(105, 250, 200, 20));
			add(lblContactAddress, new XYConstraints(400, 125, 200, 20));
			add(lblStreet, new XYConstraints(400, 150, 100, 20));
			add(txtStreet, new XYConstraints(500, 150, 200, 20));
			add(lblSuburb, new XYConstraints(400, 175, 100, 20));
			add(txtSuburb, new XYConstraints(500, 175, 200, 20));
			add(lblCity, new XYConstraints(400, 200, 100, 20));
			add(txtCity, new XYConstraints(500, 200, 200, 20));
			add(lblCountry, new XYConstraints(400, 225, 100, 20));
			add(txtCountry, new XYConstraints(500, 225, 200, 20));
			add(lblAccountDetails, new XYConstraints(400, 275, 150, 20));
			add(lblPreBillingAmount, new XYConstraints(400, 300, 100, 20));
			add(txtPreBillingAmount, new XYConstraints(500, 300, 200, 20));
			add(lblStockPreferences, new XYConstraints(8, 352, 200, 20));
			add(lstStocks, new XYConstraints(10, 376, 168, 120));
			add(btnAdd, new XYConstraints(192, 376, 50, 54));
			add(chInterested, new XYConstraints(264, 424, 145, 21));
			add(btnRemove, new XYConstraints(192, 436, 50, 54));
			add(lblInterestedStocks, new XYConstraints(276, 356, 128, 20));
			add(btnUpdateLocal, new XYConstraints(500, 424, 154, 20));
		} catch (Exception ex) {
			System.err.println(ex.toString());
		}
	}
	/**
	 * This method is being used for adding listeners to the components that
	 * are being used in this frame.
	 * @return void
	 */
	private void addListeners() {
		try {
			addWindowListener(this);
			addActionListener(this);

			btnUpdateLocal.addActionListener(this);
			btnMain.addActionListener(this);
			btnLogout.addActionListener(this);
			btnAdd.addActionListener(this);
			btnRemove.addActionListener(this);
		} catch (Exception ex) {
			System.err.println(ex.toString());
		}
	}
	
	/**
	 * This method is being used for pre-setting the components using the 
	 * data classes.
	 * @return void
	 */
	private void preFillDataFields() {
		try {
			//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
			//	PRE-FILLING OF THE DATA FIELDS..
			//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
			if (userdata.getType().equals("Person")) {
				txtFirstName.setText(userdata.getFirstName());
				txtLastName.setText(userdata.getLastName());
				txtOrganisation.setEnabled(false);
				txtOrganisation.setBackground(Color.LIGHT_GRAY);
			} else {
				txtOrganisation.setText(userdata.getOrganisation());
				txtFirstName.setEnabled(false);
				txtLastName.setEnabled(false);
				txtFirstName.setBackground(Color.LIGHT_GRAY);
				txtLastName.setBackground(Color.LIGHT_GRAY);
			}
			//			txtPassword.setText(userdata.getPassword());

			txtStreet.setText(addressdata.getStreet());
			txtSuburb.setText(addressdata.getSuburb());
			txtCity.setText(addressdata.getCity());
			txtCountry.setText(addressdata.getCountry());
			txtEmail.setText(addressdata.getEmail());
			txtPhone.setText(addressdata.getPhone());

			this.txtPreBillingAmount.setText("0");

			//fill the list with stocks not listed as interested..
			Stock_Data[] display =
				new Stock_Data[(listed.length - interested.length)];
			Vector stocksdisplay = new Vector();

			for (int i = 0; i < listed.length; i++)
				stocksdisplay.addElement((Stock_Data) listed[i]);

			for (int j = 0; j < interested.length; j++) {
				String interest = ((Stock_Data) interested[j]).toString();
				for (int k = 0; k < listed.length; k++) {
					String list = ((Stock_Data) listed[k]).toString();
					if (list.equals(interest)) {
						//dont need to display it..
						stocksdisplay.remove(listed[k]);
					}
				}
			}
			for (int l = 0; l < display.length; l++) {
				display[l] = (Stock_Data) stocksdisplay.elementAt(l);
			}

			for (int m = 0; m < display.length; m++)
				this.lstStocks.add(((Stock_Data) display[m]).toString());

			for (int n = 0; n < interested.length; n++)
				this.chInterested.add(((Stock_Data) interested[n]).toString());

		} catch (Exception e) {
			System.err.println(e.toString());
		}
	}
	/* (non-Javadoc)
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	public void actionPerformed(ActionEvent e) {
		try {
			if (e.getSource() == btnUpdateLocal) {
				//update Customer details..
				//++++++++++++++++++++++++++++++++++++++++++++++++++++
				// DO VALIDATION OF THE FIELDS (AGAIN).
				//++++++++++++++++++++++++++++++++++++++++++++++++++++

				//user validation..
				// user validation..+++++++++++++++++++++++++++++

				String type = userdata.getType();
				String firstName = txtFirstName.getText();
				String lastName = txtLastName.getText();
				String organisation = txtOrganisation.getText();
				//			String password = txtPassword.getText();
				//			String verifier = txtVerifyPassword.getText();
				String pre = this.txtPreBillingAmount.getText();
				String acc = String.valueOf(userdata.getAccount());

				if (type.equals("Person")) {
					if (firstName.equals("") || lastName.equals(""))
						throw new ValidationException("Required User detail fields are empty.");
				} else {
					if (organisation.equals(""))
						throw new ValidationException("Required User detail fields are empty.");
				}
				if (pre.equals(""))
					throw new ValidationException("Prebilling amount field is empty.");
				if (!isNumber(pre))
					throw new ValidationException("PreBilling amount field must contain a number (without a $). ");

				String[] interests = chInterested.getItems();
				Stock_Data[] interested = getStocks(interests);

				userdata.setFirstName(firstName);
				userdata.setLastName(lastName);
				userdata.setOrganisation(organisation);
				userdata.setPreBillingAmount(pre);
				userdata.setStocksInterested(interested);

				AccountValidator av = new AccountValidator();
				boolean accValid = av.validate(userdata);

				if (!accValid) {
					//revert to the old version of user..
					userdata = getStockTradingApplication().getUser();
					throw new ValidationException(av.getMessage());
				}

				//update user account..
				double account = userdata.getAccount();
				double increment = (Double.valueOf(pre)).doubleValue();

				double newacc = account + increment;
				userdata.setAccount(newacc);

				accValid = av.validate(userdata);

				if (!accValid
					&& (Integer.parseInt(txtPreBillingAmount.getText()) > 0)) {
					//revert to the old version of user..
					userdata = getStockTradingApplication().getUser();
					throw new ValidationException(av.getMessage());
				}

				UserValidator uv = new UserValidator();
				boolean userValid = uv.validate(userdata);

				if (!userValid) {
					//revert to the old version of user..
					userdata = getStockTradingApplication().getUser();
					throw new ValidationException(uv.getMessage());
				}

				//address validation..+++++++++++++++++
				String email = txtEmail.getText();
				String street = txtStreet.getText();
				String suburb = txtSuburb.getText();
				String city = txtCity.getText();
				String country = txtCountry.getText();
				String phone = txtPhone.getText();

				if (email.equals("")
					|| street.equals("")
					|| suburb.equals("")
					|| city.equals("")
					|| country.equals("")
					|| phone.equals(""))
					throw new ValidationException("Required Address detail fields are empty.");

				addressdata.setCity(city);
				addressdata.setCountry(country);
				addressdata.setEmail(email);
				addressdata.setPhone(phone);
				addressdata.setStreet(street);
				addressdata.setSuburb(suburb);

				UserAddressValidator uav = new UserAddressValidator();
				boolean addressValid = uav.validate(addressdata);

				if (!addressValid) {
					//revert back to the old version..
					addressdata = getStockTradingApplication().getAddress();
					throw new ValidationException(uav.getMessage());
				}
				//stocks interested validation..++++++++++++++++++++++++++++++++++

				if (chInterested.getItemCount() <= 0)
					throw new ValidationException("You have to choose at least one stock as interested.");

				//End of validation++++++++++++++++++++++++++++++++++++++++++++++++

				//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
				//UPDATE USER IN TMS
				//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

				String xmlstr =
					xmlgenerator.adminUpdateXml(userdata, addressdata);

				//send and get xml messages to/from TMS.
				String out = servletconn.sendXml(xmlstr);
				System.out.println("ADMIN UPDATE XML: " + xmlstr);

				xmlunmarshal.readResponse(out);
				System.out.println("RESPONSE: " + out);

				//End of update in TMS+++++++++++++++++++++++++++++++++++++++++++++

				//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
				//UPDATE USER IN LOCAL DATABASE
				//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

				//now add user to the local database..
				boolean done = umanager.updateUser(userdata);

				System.out.println(
					"Updated user in local database successfully: " + done);

				if (done) {
					lblInfo.setText(
						"Your details have been updated in TMS. Your account balance is : $"
							+ userdata.getAccount());
				}

				//set the master copy of user and address to reflect changes..
				getStockTradingApplication().setUser(userdata);
				getStockTradingApplication().setAddress(addressdata);

				//refresh the mainframe to reflect the changes..
				caller.refresh();
			} else if (e.getSource() == btnMain) {
				caller.setVisible(true);
				this.setVisible(false);
				//closeWindow();
			} else if (e.getSource() == btnLogout) {
				try {
					setVisible(false);
					closeWindow();
					caller.shutDown();
					// Shut down the program
					getParentApplication().shutDownClient();
					getParentApplication().shutDown();
				} catch (Exception ex) {
					getParentApplication().getLog().logException(
						Log.LoggingLow,
						"Error closing application",
						ex);
				}
			} else if (e.getSource() == btnAdd) {
				//add the stock as interested.
				if (lstStocks.getSelectedIndex() != -1) {
					String transfer = lstStocks.getSelectedItem();
					int index = lstStocks.getSelectedIndex();
					lstStocks.remove(index);
					chInterested.add(transfer);
				}
			} else if (e.getSource() == btnRemove) {
				//remove the stock from interested list.
				if (chInterested.getSelectedIndex() != -1) {
					String transfer = chInterested.getSelectedItem();
					int index = chInterested.getSelectedIndex();
					chInterested.remove(index);
					lstStocks.add(transfer);
				}
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			displayMessageBox("Error", ex.getMessage());
		}
	}
	/**
	 * This method is being used for getting all the stocks specified by the
	 * names.
	 * @param names The names of all the stocks to be retrieved.
	 * @return Stock_Data[] The stocks which retrieved.
	 */
	private Stock_Data[] getStocks(String[] names) {
		Stock_Data[] interests = new Stock_Data[names.length];
		int count = 0;
		for (int f = 0; f < names.length; f++) {
			for (int g = 0; g < listed.length; g++) {
				Stock_Data temp = (Stock_Data) listed[g];
				if (((String) names[f]).equals(temp.toString())) {
					interests[count] = temp;
					System.out.println(temp.toString());
					count++;
					break;
				}
			}
		}
		return interests;
	}
	
	/**
	 * This utility method is being used for checking if the given string
	 * is a number
	 * @param s The string to be checked.
	 * @return boolean Specifies whether the given string is a number.
	 */
	private boolean isNumber(String s) {
		try {
			double d = Double.parseDouble(s);
		} catch (Exception e) {
			return false;
		}
		return true;
	}
	/* (non-Javadoc)
	 * @see amber.awt.event.ComponentWindowListener#windowOpened(amber.awt.event.ComponentWindowEvent)
	 */
	public void windowOpened(ComponentWindowEvent arg0) {
	}
	/* (non-Javadoc)
	 * @see amber.awt.event.ComponentWindowListener#windowClosing(amber.awt.event.ComponentWindowEvent)
	 */
	public void windowClosing(ComponentWindowEvent arg0) {
		setVisible(false);
	}
	/* (non-Javadoc)
	 * @see amber.awt.event.ComponentWindowListener#windowClosed(amber.awt.event.ComponentWindowEvent)
	 */
	public void windowClosed(ComponentWindowEvent arg0) {
	}
	/* (non-Javadoc)
	 * @see amber.awt.event.ComponentWindowListener#windowIconified(amber.awt.event.ComponentWindowEvent)
	 */
	public void windowIconified(ComponentWindowEvent arg0) {
	}
	/* (non-Javadoc)
	 * @see amber.awt.event.ComponentWindowListener#windowDeiconified(amber.awt.event.ComponentWindowEvent)
	 */
	public void windowDeiconified(ComponentWindowEvent arg0) {
	}
	/* (non-Javadoc)
	 * @see amber.awt.event.ComponentWindowListener#windowActivated(amber.awt.event.ComponentWindowEvent)
	 */
	public void windowActivated(ComponentWindowEvent arg0) {
	}
	/* (non-Javadoc)
	 * @see amber.awt.event.ComponentWindowListener#windowDeactivated(amber.awt.event.ComponentWindowEvent)
	 */
	public void windowDeactivated(ComponentWindowEvent arg0) {
	}

	/**
	 * This method is being used for preparing for the closure of the window.
	 * @return void
	 */
	private void closeWindow() {
		try {
			removeWindowListener(this);
			caller.remove(this);
			//caller.setVisible(true) ;
		} catch (ComponentHandlerException che) {
			che.printStackTrace();
		}
	}
	/**
	 * This method is being used for getting the parent application.
	 * @return StockTradingApplication The parent application.
	 */
	protected StockTradingApplication getStockTradingApplication() {
		return (StockTradingApplication) getParentApplication();
	}

}