package kuchenBasar;

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

import javax.swing.*;
import javax.swing.event.*;

import util.*;

/**
 * Frame zur Verwaltung der Felder einer Bestellung
 * 
 * @author Bernd Schubert
 * 
 */
public class BestellungenForm extends JDialog implements ActionListener, KeyListener,
    DocumentListener, FocusListener, WindowListener, ItemListener, ChangeListener
{
  /**
   * 
   */
  private static final long              serialVersionUID = 20150911L;
  private JLabel                         jLabelDummy;
  private JTextField                     tfDatum, tfAktuell, tfEinrichtung,
                                         tfPreis, tfMenge, tfBetrag;
  private JCheckBox                      ckbBezahlt, ckbAbgeholt;
  private JButton                        btnOK, btnAbbrechen;
  private JSlider                        slMenge;
  private JComboBox<ListItem>            cboGast, cboKuchen;
  private DefaultComboBoxModel<ListItem> cboGastModel, cboKuchenModel;

  private Component                      owner;
  private KeyboardFocusManager           kbFocusManager;

  private int                            aktID            = -1;
  private boolean                        hasChanged       = false;
  private boolean                        isVorhanden      = false;

  private String                         aktDatum;
  private int                            aktGast;
  private int                            aktKuchen;
  private int                            altKuchen;
  private String                         aktAktuell;
  private String                         aktEinrichtung;
  private Double                         aktPreis;
  private int                            aktMenge;
  private int                            aktMaximal;
  private Double                         aktBetrag;
  private boolean                        aktBezahlt;
  private boolean                        aktAbgeholt;

  private DateFormat                     df               = new SimpleDateFormat( "dd.MM.yyyy" );

  public BestellungenForm()
  {
    initializeComponent();
  }

  /**
   * 
   * @param ID
   */
  public BestellungenForm( int ID )
  {
    this();
    this.aktID = ID;
  }

  private void initializeComponent()
  {
    aktDatum = df.format( new Date() );
    aktGast = 0;
    aktKuchen = 0;
    altKuchen = -1;
    aktAktuell = "";
    aktEinrichtung = "";
    aktPreis = 0.0;
    aktMenge = 0;
    aktMaximal = 100;
    aktBezahlt = false;
    aktAbgeholt = false;
    aktBetrag = 0.00;

    this.setTitle( "Bestellung" );
    this.setLayout( null );
    this.setSize( new Dimension( 480, 430 ) );
    this.setDefaultCloseOperation( JDialog.DO_NOTHING_ON_CLOSE );
    this.setResizable( false );
    this.addWindowListener( this );

    jLabelDummy = new JLabel( "Bestelldatum" );
    jLabelDummy.setBounds( 20, 10, 100, 25 );
    this.add( jLabelDummy );

    tfDatum = new JTextField();
    tfDatum.setBounds( 20, 35, 100, 25 );
    tfDatum.setMargin( new Insets( 0, 2, 0, 0 ) );
    tfDatum.addKeyListener( this );
    tfDatum.addFocusListener( this );
    tfDatum.getDocument().addDocumentListener( this );
    this.add( tfDatum );

    jLabelDummy = new JLabel( "Aktuell" );
    jLabelDummy.setBounds( 250, 10, 100, 25 );
    this.add( jLabelDummy );

    tfAktuell = new JTextField();
    tfAktuell.setBounds( 250, 35, 200, 25 );
    tfAktuell.setBorder( null );
    tfAktuell.setFocusable( false );
    tfAktuell.setMargin( new Insets( 0, 2, 0, 0 ) );
    this.add( tfAktuell );

    jLabelDummy = new JLabel( "Gast" );
    jLabelDummy.setBounds( 20, 65, 100, 25 );
    this.add( jLabelDummy );

    cboGastModel = new DefaultComboBoxModel<ListItem>();
    cboGast = new JComboBox<ListItem>( cboGastModel );
    cboGast.setBounds( 20, 90, 200, 25 );
    cboGast.setBackground( Color.WHITE );
    cboGast.addItemListener( this );
    this.add( cboGast );

    jLabelDummy = new JLabel( "Einrichtung" );
    jLabelDummy.setBounds( 250, 65, 100, 25 );
    this.add( jLabelDummy );

    tfEinrichtung = new JTextField();
    tfEinrichtung.setBounds( 250, 90, 200, 25 );
    tfEinrichtung.setBorder( null );
    tfEinrichtung.setFocusable( false );
    tfEinrichtung.setMargin( new Insets( 0, 2, 0, 0 ) );
    this.add( tfEinrichtung );

    jLabelDummy = new JLabel( "Kuchen" );
    jLabelDummy.setBounds( 20, 120, 100, 25 );
    this.add( jLabelDummy );

    cboKuchenModel = new DefaultComboBoxModel<ListItem>();
    cboKuchen = new JComboBox<ListItem>( cboKuchenModel );
    cboKuchen.setBounds( 20, 145, 200, 25 );
    cboKuchen.setBackground( Color.WHITE );
    cboKuchen.addItemListener( this );
    this.add( cboKuchen );

    jLabelDummy = new JLabel( "Preis" );
    jLabelDummy.setBounds( 250, 120, 100, 25 );
    this.add( jLabelDummy );

    tfPreis = new JTextField();
    tfPreis.setBounds( 250, 145, 200, 25 );
    tfPreis.setBorder( null );
    tfPreis.setFocusable( false );
    tfPreis.setMargin( new Insets( 0, 2, 0, 0 ) );
    this.add( tfPreis );

    jLabelDummy = new JLabel( "Menge" );
    jLabelDummy.setBounds( 20, 175, 100, 25 );
    this.add( jLabelDummy );

    tfMenge = new JTextField();
    tfMenge.setBounds( 20, 200, 100, 25 );
    tfMenge.setMargin( new Insets( 0, 2, 0, 0 ) );
    tfMenge.addKeyListener( this );
    tfMenge.addFocusListener( this );
    tfMenge.getDocument().addDocumentListener( this );
    this.add( tfMenge );

    slMenge = new JSlider( JSlider.HORIZONTAL, 0, aktMaximal, aktMenge );
    slMenge.setBounds( 150, 195, 250, 40 );
    slMenge.addChangeListener( this );
    this.add( slMenge );

    jLabelDummy = new JLabel( "Betrag/Spende" );
    jLabelDummy.setBounds( 20, 230, 120, 25 );
    this.add( jLabelDummy );

    tfBetrag = new JTextField();
    tfBetrag.setBounds( 20, 255, 100, 25 );
    tfBetrag.setMargin( new Insets( 0, 2, 0, 0 ) );
    tfBetrag.addKeyListener( this );
    tfBetrag.addFocusListener( this );
    tfBetrag.getDocument().addDocumentListener( this );
    this.add( tfBetrag );

    ckbBezahlt = new JCheckBox( "bezahlt" );
    ckbBezahlt.setBounds( 250, 260, 95, 25 );
    ckbBezahlt.addItemListener( this );
    this.add( ckbBezahlt );

    ckbAbgeholt = new JCheckBox( "abgeholt" );
    ckbAbgeholt.setBounds( 350, 260, 100, 25 );
    ckbAbgeholt.addItemListener( this );
    this.add( ckbAbgeholt );

    btnOK = new JButton( "OK" );
    btnOK.setBounds( 100, 350, 120, 25 );
    btnOK.addActionListener( this );
    btnOK.setFocusable( false );
    this.add( btnOK );

    btnAbbrechen = new JButton( "Abbrechen" );
    btnAbbrechen.setBounds( 280, 350, 120, 25 );
    btnAbbrechen.addActionListener( this );
    btnAbbrechen.setFocusable( false );
    this.add( btnAbbrechen );
  }

  /**
   * 
   * @param owner
   * @return ID des bearbeiteten (neuen/vorhandenen) Kuchens
   */
  public int showDialog( Component owner )
  {
    this.owner = owner;
    return showDialog();
  }

  /**
   * 
   * @param owner
   * @return ID des bearbeiteten (neuen/vorhandenen) Kuchens
   */
  public int showDialog()
  {
    initDialog();
    this.setVisible( true );

    //weil der Dialog modal ist, kann hier der Rückgabewert bestimmt werden:
    //welche ID wurde upgedatet bzw. keine (-1)
    if ( hasChanged )
      return aktID;
    else
      return -1;
  }

  private void initDialog()
  {
    this.setModal( true );
    this.setLocationRelativeTo( owner );

    kbFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();

    isVorhanden = ( aktID > -1 );
    if ( isVorhanden )
      readEntry();

    initFrame();

    hasChanged = !isVorhanden;
  }

  private void initMenge()
  {
    tfMenge.setText( String.valueOf( aktMenge ) );

    Hashtable<Integer, JLabel> sliderLabelTable = new Hashtable<Integer, JLabel>();
    sliderLabelTable.put( 0, new JLabel( String.valueOf( 0 ) ) );
    if ( aktMaximal >= 12 )
    {
      sliderLabelTable.put( aktMaximal / 4, new JLabel( String.valueOf( aktMaximal / 4 ) ) );
      sliderLabelTable.put( aktMaximal * 3 / 4, new JLabel( String.valueOf( aktMaximal * 3 / 4 ) ) );
    }
    if ( aktMaximal >= 6 )
      sliderLabelTable.put( aktMaximal / 2, new JLabel( String.valueOf( aktMaximal / 2 ) ) );
    sliderLabelTable.put( aktMaximal, new JLabel( String.valueOf( aktMaximal ) ) );

    slMenge.setMinimum( 0 );
    slMenge.setMaximum( aktMaximal );
    slMenge.setValue( aktMenge );
    slMenge.setMinorTickSpacing( 1 );
    slMenge.setMajorTickSpacing( 5 );
    slMenge.setPaintTicks( true );
    slMenge.setPaintLabels( true );
    slMenge.setPaintTrack( false );
    slMenge.setLabelTable( sliderLabelTable );
  }

  private void anpassenGast()
  {
    tfEinrichtung.setText( GaesteDB.getEinrichtung( aktGast ) );
  }

  private void anpassenKuchen()
  {
    int tempMenge;
    int tempMaximal;

    DecimalFormat f = new DecimalFormat( "0.00" );
    aktPreis = KuchenDB.getPreis( aktKuchen );
    tfPreis.setText( f.format( aktPreis ) );

    tempMaximal = KuchenDB.getAnzahl( aktKuchen );
    if ( altKuchen == aktKuchen )
      tempMaximal += aktMenge;

    //Die Menge muß hier direkt abgegriffen werden, 
    //da der Slider die aktMenge nicht automatisch verändern darf 
    tempMenge = slMenge.getValue();

    if ( tempMenge > tempMaximal )
      tempMenge = tempMaximal;

    aktMaximal = tempMaximal;
    aktMenge = tempMenge;

    initMenge();

    anpassenBetrag();
  }

  private void anpassenBetrag()
  {
    //Die Menge muß hier direkt abgegriffen werden, 
    //da der Slider die aktMenge nicht automatisch verändern darf 
    int tempMenge = slMenge.getValue();

    DecimalFormat f = new DecimalFormat( "0.00" );
    aktBetrag = tempMenge * aktPreis;
    tfBetrag.setText( f.format( aktBetrag ) );
  }

  private void initFrame()
  {
    initMenge();

    DecimalFormat f = new DecimalFormat( "0.00" );
    tfDatum.setText( aktDatum );
    tfAktuell.setText( " " + aktAktuell );

    tfEinrichtung.setText( " " + aktEinrichtung );
    tfPreis.setText( " " + f.format( aktPreis ) );

    tfBetrag.setText( f.format( aktBetrag ) );
    ckbBezahlt.setSelected( aktBezahlt );
    ckbAbgeholt.setSelected( aktAbgeholt );

    cboGast.removeItemListener( this );
    ArrayList<ListItem> gaesteListe = GaesteDB.getGaesteListe();
    for ( ListItem item : gaesteListe )
    {
      if ( !isVorhanden || (int)item.getValueMember() > 0 )
        cboGastModel.addElement( item );
      if ( (int)item.getValueMember() == aktGast )
        cboGast.setSelectedItem( item );
    }
    cboGast.addItemListener( this );

    cboKuchen.removeItemListener( this );
    ArrayList<ListItem> kuchenListe = KuchenDB.getKuchenListe();
    for ( ListItem item : kuchenListe )
    {
      if ( !isVorhanden || (int)item.getValueMember() > 0 )
        cboKuchenModel.addElement( item );
      if ( (int)item.getValueMember() == aktKuchen )
        cboKuchen.setSelectedItem( item );
    }
    cboKuchen.addItemListener( this );
  }

  private void readEntry()
  {
    Bestellung bestellung = BestellungenDB.getBestellung( aktID );

    if ( bestellung == null )
      return;

    aktDatum = bestellung.getBestelldatum();
    aktAktuell = DatenPlausi.ts2df( bestellung.getLetzterzugriff() );
    aktGast = bestellung.getGast();
    aktKuchen = bestellung.getKuchen();
    altKuchen = aktKuchen;
    aktMenge = bestellung.getMenge();
    aktEinrichtung = GaesteDB.getEinrichtung( aktGast );
    aktMaximal = KuchenDB.getAnzahl( aktKuchen ) + aktMenge;
    aktPreis = KuchenDB.getPreis( aktKuchen );
    aktBezahlt = bestellung.isBezahlt();
    aktAbgeholt = bestellung.isAbgeholt();
    aktBetrag = bestellung.getGesamtpreis();
  }

  private boolean updateEntry()
  {
    //Hier kann kein einfaches Update stattfinden, 
    //da sonst die Kuchen durch einfaches Bestellungsändern "verschwinden" 
    //oder "vermehrt" werden könnten.
    //Es müssen die Kuchen der "alten Bestellung" zurückgebucht werden,
    //Dann kann der neue Kuchen bestellt werden. 
    //Eine Mengenänderung des gleichen Kuchens ist somit ein "erlaubter Sonderfall"
    //dieses komplexen Umbuchens.
    //Beide Updates müssen in der gleichen Transaktion stattfinden, daher steuert
    //die Klasse BestellungenDB das komplett.

    Bestellung bestellung = new Bestellung();
    bestellung.setID( aktID );
    bestellung.setBestelldatum( aktDatum );
    bestellung.setGast( aktGast );
    bestellung.setKuchen( aktKuchen );
    bestellung.setMenge( aktMenge );
    bestellung.setGesamtpreis( aktBetrag );
    bestellung.setBezahlt( aktBezahlt );
    bestellung.setAbgeholt( aktAbgeholt );

    return BestellungenDB.komplexesUpdateBestellung( aktID, bestellung );
  }

  private boolean insertEntry()
  {
    Bestellung bestellung = new Bestellung();
    bestellung.setBestelldatum( aktDatum );
    bestellung.setGast( aktGast );
    bestellung.setKuchen( aktKuchen );
    bestellung.setMenge( aktMenge );
    bestellung.setGesamtpreis( aktBetrag );
    bestellung.setBezahlt( aktBezahlt );
    bestellung.setAbgeholt( aktAbgeholt );

    aktID = BestellungenDB.insertBestellung( bestellung );

    return ( aktID > -1 );
  }

  private boolean queryExit()
  {
    if ( !hasChanged )
      return true;

    if ( !plausi() )
      return false;

    Object[] options =
    { "Ja", "Nein", "Abbrechen" };

    int retValue = JOptionPane.showOptionDialog(
        this,
        "Daten wurden geändert\nÄnderungen speichern?",
        "Frage",
        JOptionPane.YES_NO_CANCEL_OPTION,
        JOptionPane.WARNING_MESSAGE,
        null,
        options,
        options[2]
        );

    switch ( retValue )
    {
    // Nein - nicht speichern und Form schließen
      case JOptionPane.NO_OPTION:
        return true;

        // Abbruch - nicht speichern und Form geöffnet lassen
      case JOptionPane.CANCEL_OPTION:
        return false;

        // Ja - Speichern ( Insert / Update )
      case JOptionPane.YES_OPTION:
        return saveEntry();

      default:
        break;

    }
    return false;
  }

  private boolean saveEntry()
  {
    if ( isVorhanden )
      return updateEntry();
    else
      return insertEntry();
  }

  private boolean plausi()
  {
    if ( DatenPlausi.okDatum( tfDatum.getText() ) )
      aktDatum = tfDatum.getText();
    else
      return fehler( tfDatum, "Datum DD.MM.YYYY" );

    aktGast = (int)( (ListItem)cboGast.getSelectedItem() ).getValueMember();
    if ( aktGast < 1 && !isVorhanden )
      return fehler( cboGast, "Gast muß bestimmt sein" );

    aktKuchen = (int)( (ListItem)cboKuchen.getSelectedItem() ).getValueMember();
    if ( aktKuchen < 1 && !isVorhanden )
      return fehler( cboKuchen, "Kuchen muß bestimmt sein" );

    aktMenge = (int)slMenge.getValue();
    if ( aktMenge < 1 && !isVorhanden )
      return fehler( tfMenge, "Menge muß anfangs größer 0 sein" );

    if ( DatenPlausi.okGeld( tfBetrag.getText() ) )
      aktBetrag = Double.parseDouble( tfBetrag.getText().replace( ',', '.' ) );
    else
      return fehler( tfBetrag, "Betrag muß positiv mit 2 Nachkommastellen sein" );

    aktBezahlt = ckbBezahlt.isSelected();
    aktAbgeholt = ckbAbgeholt.isSelected();

    return true;
  }

  private boolean fehler( Component c, String ursache )
  {
    JOptionPane.showMessageDialog(
        this,
        "Fehler, " + ursache,
        "Plausi Prüfung",
        JOptionPane.ERROR_MESSAGE );
    c.requestFocusInWindow();

    return false;
  }

  @Override
  public void windowOpened( WindowEvent e )
  {
  }

  @Override
  public void windowClosing( WindowEvent e )
  {
    if ( queryExit() )
      this.dispose();
  }

  @Override
  public void windowClosed( WindowEvent e )
  {
  }

  @Override
  public void windowIconified( WindowEvent e )
  {
  }

  @Override
  public void windowDeiconified( WindowEvent e )
  {
  }

  @Override
  public void windowActivated( WindowEvent e )
  {
  }

  @Override
  public void windowDeactivated( WindowEvent e )
  {
  }

  @Override
  public void focusGained( FocusEvent e )
  {
    Object o = e.getSource();
    JTextField tf;
    if ( o instanceof JTextField )
    {
      tf = (JTextField)o;
      tf.selectAll();
    }
  }

  @Override
  public void focusLost( FocusEvent e )
  {
    JTextField tf;
    if ( !( e.getSource() instanceof JTextField ) )
      return;
    tf = (JTextField)e.getSource();
    if ( tf.equals( tfMenge ) )
    {
      if ( DatenPlausi.okIntMinMax( tfMenge.getText(), 0, aktMaximal ) )
      {
        aktMenge = Integer.parseInt( tfMenge.getText() );
        slMenge.setValue( aktMenge );
      }
      else
        tfMenge.setText( String.valueOf( aktMenge ) );
    }
  }

  @Override
  public void insertUpdate( DocumentEvent e )
  {
    hasChanged = true;
  }

  @Override
  public void removeUpdate( DocumentEvent e )
  {
    hasChanged = true;
  }

  @Override
  public void changedUpdate( DocumentEvent e )
  {
  }

  @Override
  public void keyTyped( KeyEvent e )
  {
    int kc = e.getKeyChar();
    Object o = e.getSource();
    JTextField tf = (JTextField)o;

    if ( o == tfDatum )
    {
      if ( !( Character.isDigit( kc ) || kc == '.' ) )
      {
        e.consume();
        return;
      }

      tf.replaceSelection( "" );
      if ( tf.getText().length() >= 10 )
      {
        e.consume();
        return;
      }
    }

    else if ( o == tfMenge )
    {
      if ( !Character.isDigit( kc ) )
      {
        e.consume();
        return;
      }
    }

    else if ( o == tfBetrag )
    {
      if ( !( Character.isDigit( kc ) || kc == '.' || kc == ',' ) )
      {
        e.consume();
        return;
      }
    }
  }

  @Override
  public void keyPressed( KeyEvent e )
  {
    int kc = e.getKeyCode();
    Object o = e.getSource();

    if ( o == tfDatum && kc == KeyEvent.VK_ENTER
        && tfDatum.getText().length() == 10 )
      kbFocusManager.focusNextComponent();
    else if ( o == tfBetrag && kc == KeyEvent.VK_ENTER
        && tfBetrag.getText().length() > 0 )
      kbFocusManager.focusNextComponent();
    else if ( o == tfMenge && kc == KeyEvent.VK_ENTER
        && tfMenge.getText().length() > 0 )
      kbFocusManager.focusNextComponent();
  }

  @Override
  public void keyReleased( KeyEvent e )
  {
    int kc = e.getKeyCode();
    if ( kc == 27 ) //ESC
    {
      windowClosing( new WindowEvent( this, WindowEvent.WINDOW_CLOSING ) );
    }
  }

  @Override
  public void actionPerformed( ActionEvent e )
  {
    Object o = e.getSource();

    if ( o == btnOK )
      windowClosing( new WindowEvent( this, WindowEvent.WINDOW_CLOSING ) );
    if ( o == btnAbbrechen )
    {
      hasChanged = false;
      windowClosing( new WindowEvent( this, WindowEvent.WINDOW_CLOSING ) );
    }
  }

  @Override
  public void itemStateChanged( ItemEvent e )
  {
    if ( !( e.getSource() instanceof JComboBox ) )
      return;
    if ( !( e.getStateChange() == ItemEvent.SELECTED ) )
      return;

    @SuppressWarnings("unchecked")
    JComboBox<ListItem> cbo = (JComboBox<ListItem>)e.getSource();
    if ( cbo.equals( cboGast ) )
    {
      aktGast = (int)( (ListItem)cboGast.getSelectedItem() ).getValueMember();

      anpassenGast();
    }
    else if ( cbo.equals( cboKuchen ) )
    {
      aktKuchen = (int)( (ListItem)cboKuchen.getSelectedItem() ).getValueMember();

      anpassenKuchen();
    }

    hasChanged = true;
  }

  @Override
  public void stateChanged( ChangeEvent e )
  {
    if ( !( e.getSource() instanceof JSlider ) )
      return;

    JSlider sl = (JSlider)e.getSource();
    if ( sl.equals( slMenge ) )
    {
      //hier wird nur das Textfeld angepaßt, die aktMenge bleibt unverändert
      tfMenge.setText( String.valueOf( slMenge.getValue() ) );

      anpassenBetrag();
    }

    hasChanged = true;
  }
}
