import sale.*;
import sale.stdforms.*;
import data.*;
import data.ooimpl.*;
import data.stdforms.*;
import users.*;

import java.util.*;
import java.text.*;
import java.lang.*;

import javax.swing.JTextField;
import javax.swing.JPanel;
import javax.swing.JOptionPane;
import javax.swing.BoxLayout;
import javax.swing.JLabel;



/**
 * Verkaufs- bzw. Verleihprozess, der von einem registrierten Kunden
 * am Automaten durchgeführt werden kann.
 */
public class RentProcess extends SaleProcess
{

  //// attributes ////////////////////////////////////////////////////////////

  // Gates
  protected UIGate capabilityGate;
  protected UIGate selectionGate;
  protected UIGate rentGate;
  protected Gate   decisionGate;
  protected UIGate getChangeGate;

  // Transitions
  protected Transition toSelectionTransition;
  protected Transition toPayingTransition;
  protected Transition toDecisionTransition;
  protected Transition toGetChangeTransition;

  // verwendete Waehrung
  protected data.Currency myCurrency;

  // zu zahlender Betrag
  private IntegerValue toPayValue;

  // gezahlter Betrag
  private IntegerValue paidValue;

  // Bewertung der Relation zwischen zu zahlendem und gezahltem Betrag
  private int payAssessment;

  // Kunde, der ausleihen moechte
  private Customer customer;


  //// constructor ///////////////////////////////////////////////////////////

  /**
   * Erzeugt ein neues Objekt der Klasse RentProcess.
   */
  public RentProcess()
  {
    super ("RentProcess");
  }

  //// protected methods /////////////////////////////////////////////////////

  /**
   * Baut die Oberfläche für den Verleihvorgang auf.
   */
  protected void setupMachine()
  {
    // verwendete Waehrung ermitteln
    myCurrency = (Currency)Shop.getTheShop().getCatalog("DM");

    // Gate zur Kundennummer-Abfrage anlegen
    capabilityGate = new UIGate(null, null);

    // Formsheet fuer Eingabe der Kundennummer
    final TextInputForm tif = new TextInputForm("Customer-ID",
						"Customer-ID",
						"");

    // Auswahl-Gate anlegen
    selectionGate = new UIGate(null, null);

    // Gate zum Ausleihen/Bezahlen anlegen
    rentGate = new UIGate(null, null);

    // Gate zum Ausgeben des Wechselgeldes anlegen
    getChangeGate = new UIGate(null, null);

    // zu verwendenden Datenkorb ermitteln
    final DataBasket db = getBasket();

    // Transition zum Selection Gate
    toSelectionTransition = new Transition()
    {
      public Gate perform(SaleProcess pOwner, User usr)
      {
	// zu verwendenden Bestand ermitteln
	CountingStockImpl cs =
	  (CountingStockImpl)Shop.getTheShop().getStock("Video-Countingstock");

	// am Gate darzustellendes FormSheet
	TwoTableFormSheet ttfs = TwoTableFormSheet.create(
          "Make your selection", // Titel des FormSheets
	  cs,                    // Quell-CountingStock
	  db,                    // Ziel-DataBasket
	  selectionGate,         // Gate, an dem das FormSheet darzustellen ist
	  null,                  // Comparator fuer Quelltabelle
	  null,                  // Comparator furr Ziel-Tabelle
	  false,        // Zeilen mit Anzahl == 0 in der Quelltabelle anzeigen?
	  new OfferTED(true),    // TableEntryDescriptor furr Quelltabelle
	  null,                  // TableEntryDescriptor furr Zieltabelle
	  null                   // Transferstrategie
	);

	// FormSheetContentCreator am Auswahl-Gate anmelden    
	ttfs.addContentCreator(new FormSheetContentCreator()
	{
	  protected void createFormSheetContent(FormSheet fs)
	  {
	    // alle vorhandenen Buttons entfernen
	    fs.removeAllButtons();

	    // neuen "OK"-Button einbauen
	    fs.addButton ("Ok", 100, new sale.Action()
	    {
	      public void doAction (SaleProcess p, SalesPoint sp)
	      {
		// Transition zum Bezahlen als naechste Transition setzen
		selectionGate.setNextTransition(toPayingTransition);
	      }
	    });

	    // "Cancel"-Button einbauen
	    fs.addButton ("Cancel", 101, new sale.Action()
	    {
	      public void doAction (SaleProcess p, SalesPoint sp)
	      {
		// Transition zum Rollback-Gate als naechste Transition setzen
		selectionGate.setNextTransition(
		  GateChangeTransition.CHANGE_TO_ROLLBACK_GATE);
	      }
	    });
	  }
	});

	// erstelltes FormSheet am zu betretenden Gate setzen
	selectionGate.setFormSheet(ttfs);

	// als naechstes zu betretendes Gate zurueckgeben
	return selectionGate;
      }
    };

    // Transition zum Wechselgeld-Gate
    toGetChangeTransition = new Transition()
    {
      public Gate perform(SaleProcess pOwner, User usr)
      {
	// am Gate darzustellendes FormSheet
	MsgForm mf = new MsgForm(
	  "Get Change",
	  "You get " +
	  myCurrency.toString((IntegerValue)paidValue.subtract(toPayValue)) +
	  " Change.");

	// ContentCreator zur Neubelegung des "OK"-Buttons hinzufuegen 
	mf.addContentCreator(new FormSheetContentCreator()
        {
	  public void createFormSheetContent(FormSheet fs)
	  {
	    // neue Aktion setzen
	    fs.getButton(FormSheet.BTNID_OK).setAction(new Action()
	    {
	      public void doAction(SaleProcess p, SalesPoint sp)
	      {
		// zum Commit-Gate fuehrende Transition als
		// naechste Transition setzen
		getChangeGate.setNextTransition(
	          GateChangeTransition.CHANGE_TO_COMMIT_GATE);
	      }
	    });
	  }
	});

	// erstelltes FormSheet am zu betretenden Gate setzen
	getChangeGate.setFormSheet(mf);

	// als naechstes zu betretendes Gate zurueckgeben
	return getChangeGate;
      }
    };

    // Gate anlegen, das entscheidet, ob Wechselgeld gegeben, zum Bezahlen
    // oder zum Commit-Gate gegangen wird
    decisionGate = new Gate()
    {
      public Transition getNextTransition(SaleProcess pOwner, User usr)
        throws InterruptedException
      {
	switch(payAssessment) {
	  // zuwenig bezahlt - Message anzeigen und zurueck zum Bezahlen
	  case -1:
	    FormSheet mf = new MsgForm("Error",
				       "You have to pay more!",
				       false);
	    pOwner.getContext().popUpFormSheet(pOwner, mf);
	    return new GateChangeTransition(rentGate);

	  // genau richtig bezahlt - zum Commit-Gate
	  case  0:
	    return GateChangeTransition.CHANGE_TO_COMMIT_GATE;

	  // zuviel bezahlt - zur Wechselgeldausgabe
	  case  1:
	    return toGetChangeTransition;

	  // falscher Wert in payAssessment - Fehlermeldung und Abbruch
	  default:
	    FormSheet mf2 = new MsgForm("Error",
					"Internal error at Decision Gate. " +
					"Will quit process.",
					false);
	    pOwner.getContext().popUpFormSheet (pOwner, mf2);
	    return GateChangeTransition.CHANGE_TO_QUIT_GATE;
        }
      }
    };

    // Transition zum Entscheidungsgate
    toDecisionTransition = new Transition()
    {
      public Gate perform (SaleProcess pOwner, User usr)
      {
	// ausreichend Geld gegeben?
	if (paidValue.compareTo (toPayValue) >= 0) {
	  // zu zahlenden Betrag im Safe des Restaurants ablegen, wenn
	  // ueberhaupt etwas zu bezahlen ist
	  if (toPayValue.getValue().intValue() > 0)
	    ((CountingStock)Shop.getTheShop().getStock(
	      "coin slot")).add("1-Pfennig-Stueck",
				toPayValue.getValue().intValue(), 
				pOwner.getBasket());

	  // Genau richtig gezahlt? - payAssessment entsprechend belegen
	  if (paidValue.compareTo(toPayValue) == 0)
	    payAssessment = 0;
	  else
	    payAssessment = 1;
	}

	// nein, zuwenig bezahlt - payAssessment entsprechend belegen
	else
	  payAssessment = -1;

	// Entscheidungsgate als naechstes zu betretendes Gate zurueckgeben
	return decisionGate;
      }
    };

    // Transition zum Gate, an dem zu bezahlen ist
    toPayingTransition = new Transition()
    {
      public Gate perform(SaleProcess pOwner, User usr)
      {
	// Parameter zum Aufsummieren des Datenkorbes festlegen
	final DataBasketCondition dbc =
	  DataBasketConditionImpl.ALL_STOCK_ITEMS;
	BasketEntryValue bev = BasketEntryValues.ONLY_STOCK_ITEMS;
	QuoteValue qvSum =
	  new QuoteValue(new IntegerValue(0), new IntegerValue(0));

	// ...und Datenkorb damit aufsummieren
	pOwner.getBasket().sumBasket (dbc, bev, qvSum);

	// zu zahlenden Betrag ermitteln
	toPayValue = (IntegerValue)qvSum.getBid();

	// FormSheet für das naechste Gate erstellen
	FormSheet tif =
	  new TextInputForm("Paying",
			    "You have to pay " +
			    myCurrency.toString (toPayValue) + ".",
			    myCurrency.toString (toPayValue)
			    );

	// FormSheetContentCreator zum Einbauen der Buttons anmelden
	tif.addContentCreator(new FormSheetContentCreator()
	{
	  protected void createFormSheetContent(FormSheet fs)
	  {
	    // als 'final' markierte Version des FormSheets anlegen
	    final TextInputForm tifFinal = (TextInputForm)fs;

	    // alle bisher vorhandenen Buttons entfernen
	    fs.removeAllButtons();

	    // neuen "OK"-Button einbauen
	    fs.addButton("Ok", 100, new sale.Action()
	    {
	      public void doAction (SaleProcess p, SalesPoint sp)
	      {
		try {
		  // eingegebenen Text verarbeiten
		  paidValue =
		    (IntegerValue)myCurrency.parse(tifFinal.getText());

		  // Videos in den Bestand des Kunden uebernehmen
		  Object date = Shop.getTheShop().getTimer().getTime();
		  Iterator i = db.iterator(dbc);
		  while (i.hasNext()) {
		    CountingStockItemDBEntry cassetteItem =
		      (CountingStockItemDBEntry)i.next();
		    int number = cassetteItem.count();
		    for (; number > 0; number --)
		      customer.addVideoCassette(new CassetteStoringStockItem(
		        cassetteItem.getSecondaryKey(),	date));
		  }

		  // ...und naechste Transition setzen
		  rentGate.setNextTransition(toDecisionTransition);
		}
		catch(ParseException pexc) {
		  // eingegebener Text konnte nicht verarbeitet werden
		  // Meldung erzeugen und ausgeben
		  MsgForm mf = new MsgForm ("Error",
					    "The specified amount does " +
					    "not have an appropriate format.");

		  try {
		    p.getContext().popUpFormSheet (p, mf);
		  }
		  catch(InterruptedException iexc) {
		  }
		}
	      }
	    });

	    // "Back"-Button einbauen
	    fs.addButton ("Back", 101, new sale.Action()
	    {
	      public void doAction (SaleProcess p, SalesPoint sp)
	      {
		// naechste Transition setzen, fuehrt ohne weitere Aktionen
		// zum Auswahl-Gate
		rentGate.setNextTransition(
		  new GateChangeTransition(selectionGate));
	      }
	    });

	    // "Cancel"-Button einbauen
	    fs.addButton ("Cancel", 102, new sale.Action()
	    {
	      public void doAction (SaleProcess p, SalesPoint sp)
	      {
		// naechste Transition setzen, fuehrt zum Rollback-Gate
		rentGate.setNextTransition(
		  GateChangeTransition.CHANGE_TO_ROLLBACK_GATE);
	      }
	    });

	  }
	});

	// FormSheet am entsprechenden Gate setzen
	rentGate.setFormSheet(tif);

	// ...und naechstes zu betretendes Gate zurueckgeben
	return rentGate;
      }
    };

    // FormSheetContentCreator am Kundennummer-Eingabe-Gate anmelden    
    tif.addContentCreator(new FormSheetContentCreator()
    {
      protected void createFormSheetContent(FormSheet fs)
      {
	// alle vorhandenen Buttons entfernen
	fs.removeAllButtons();

	// neuen "OK"-Button einbauen
	fs.addButton ("Ok", 100, new sale.Action()
	{
	  public void doAction (SaleProcess p, SalesPoint sp)
	  {
	    // Eingabe ueberpruefen
	    String customerID = tif.getText();
	    boolean isNotAnInteger = true;

	    try {
	      new Integer(customerID);
	      isNotAnInteger = false;
	    }
	    catch(NumberFormatException nfe) {
	      isNotAnInteger = true;
	    }

	    // Eingabe korrekt -> selectionGate
	    if (!isNotAnInteger && (new Integer(customerID)).intValue() > 0) {

	      // existiert der Kunde?
	      try {
		customer = new Customer(customerID);

		// nein, dann neu anlegen und der Liste reg. Kunden hinzufuegen
		VideoMachine.addCustomer(customer);
	      }

	      // ja, dann diesen holen
	      catch (DuplicateKeyException dke) {
	        customer = VideoMachine.getCustomerByID(customerID);
	      }

	      capabilityGate.setNextTransition(toSelectionTransition);
	    }

	    // Eingabe falsch -> Kunden informieren und
	    // Eingabe wiederholen
	    else {
	      JOptionPane.showMessageDialog(null,
	        "CustomerID must be a positive Number!");
	      capabilityGate.setNextTransition(
	        new GateChangeTransition(capabilityGate));
	    }

	  }
	});

	// "Cancel"-Button einbauen
	fs.addButton ("Cancel", 101, new sale.Action()
	{
	  public void doAction (SaleProcess p, SalesPoint sp)
	  {
	    // Transition zum Rollback-Gate als naechste Transition setzen
	    capabilityGate.setNextTransition(
	      GateChangeTransition.CHANGE_TO_ROLLBACK_GATE);
	  }
	});
      }
    });

    // FormSheet am entsprechenden Gate setzen
    capabilityGate.setFormSheet(tif);

  }

  //// public methods ////////////////////////////////////////////////////////

  /**
   * Gibt das Startgate des Prozesses zurück.
   */
  public Gate getInitialGate()
  {  
    // Automat aufbauen
    setupMachine();
 
    // ...und Capability-Gate als Startgate zurueckgeben
    return capabilityGate;
  }
}