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 eines Kuchens
 * 
 * @author Bernd Schubert
 * 
 */
public class KuchenForm extends JDialog implements ActionListener, KeyListener,
    DocumentListener, FocusListener, WindowListener, ChangeListener, ItemListener
{
  /**
   * 
   */
  private static final long              serialVersionUID = 20150305L;
  private JLabel                         jLabelDummy;
  private JTextField                     tfAnbieter, tfKurzname, tfBeschreibung, tfDatum, tfPreis;
  private JCheckBox                      ckbDiabetiker;
  private JButton                        btnOK, btnAbbrechen;
  private JSpinner                       spAnzahl;
  private JComboBox<ListItem>            cboSorte;
  private DefaultComboBoxModel<ListItem> cboSorteModel;

  private Component                      owner;
  private KeyboardFocusManager           kbFocusManager;

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

  private String                         aktDatum;
  private String                         aktAnbieter;
  private String                         aktBeschreibung;
  private String                         aktKurzname;
  private int                            aktAnzahl;
  private int                            aktSorte;
  private Double                         aktPreis;
  private boolean                        aktDiabetiker;

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

  public KuchenForm()
  {
    initializeComponent();
  }

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

  private void initializeComponent()
  {
    aktBeschreibung = "";
    aktAnbieter = "anbieter";
    aktKurzname = "name";
    aktAnzahl = 0;
    aktSorte = 0;
    aktDatum = df.format( new Date() );
    aktDiabetiker = true;
    aktPreis = 0.00;

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

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

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

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

    tfAnbieter = new JTextField();
    tfAnbieter.setBounds( 20, 90, 300, 25 );
    tfAnbieter.setMargin( new Insets( 0, 2, 0, 0 ) );
    tfAnbieter.addKeyListener( this );
    tfAnbieter.addFocusListener( this );
    tfAnbieter.getDocument().addDocumentListener( this );
    this.add( tfAnbieter );

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

    tfBeschreibung = new JTextField();
    tfBeschreibung.setBounds( 20, 145, 300, 25 );
    tfBeschreibung.setMargin( new Insets( 0, 2, 0, 0 ) );
    tfBeschreibung.addKeyListener( this );
    tfBeschreibung.addFocusListener( this );
    tfBeschreibung.getDocument().addDocumentListener( this );
    this.add( tfBeschreibung );

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

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

    ckbDiabetiker = new JCheckBox( "für Diabetiker geeignet" );
    ckbDiabetiker.setBounds( 170, 200, 200, 25 );
    ckbDiabetiker.addItemListener( this );
    this.add( ckbDiabetiker );

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

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

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

    spAnzahl = new JSpinner();
    spAnzahl.setBounds( 175, 255, 100, 25 );
    spAnzahl.addChangeListener( this );
    spAnzahl.addFocusListener( this );
    //((JSpinner.DefaultEditor) ((JSpinner) spAnzahl).getEditor()).getTextField().addKeyListener( this );
    this.add( spAnzahl );

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

    cboSorteModel = new DefaultComboBoxModel<ListItem>();
    cboSorte = new JComboBox<ListItem>( cboSorteModel );
    cboSorte.setBounds( 20, 310, 300, 25 );
    cboSorte.setBackground( Color.WHITE );
    cboSorte.addItemListener( this );
    this.add( cboSorte );

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

    btnAbbrechen = new JButton( "Abbrechen" );
    btnAbbrechen.setBounds( 210, 400, 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 noch 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 initFrame()
  {
    DecimalFormat f = new DecimalFormat( "0.00" );
    tfAnbieter.setText( aktAnbieter );
    tfBeschreibung.setText( aktBeschreibung );
    tfKurzname.setText( aktKurzname );
    spAnzahl.setModel( new SpinnerNumberModel( aktAnzahl, 0, 500, 1 ) );
    tfDatum.setText( aktDatum );
    tfPreis.setText( f.format( aktPreis ) );
    ckbDiabetiker.setSelected( aktDiabetiker );

    ArrayList<ListItem> sortenListe = SortenDB.getSortenListe();
    for ( ListItem item : sortenListe )
    {
      if ( !isVorhanden || (int)item.getValueMember() > 0 )
        cboSorteModel.addElement( item );
      if ( (int)item.getValueMember() == aktSorte )
        cboSorte.setSelectedItem( item );
    }
  }

  private void readEntry()
  {
    Kuchen kuchen = KuchenDB.getKuchen( aktID );

    if ( kuchen == null )
      return;

    aktBeschreibung = kuchen.getBeschreibung();
    aktAnbieter = kuchen.getAnbieter();
    aktKurzname = kuchen.getKurzkuchen();
    aktSorte = kuchen.getSorte();
    aktAnzahl = kuchen.getAnzahl();
    aktDatum = kuchen.getAbgabedatum();
    aktDiabetiker = kuchen.isDiabetikergeeignet();
    aktPreis = kuchen.getPreis();
  }

  private boolean updateEntry()
  {
    Kuchen kuchen = new Kuchen();
    kuchen.setID( aktID );
    kuchen.setAnbieter( aktAnbieter );
    kuchen.setBeschreibung( aktBeschreibung );
    kuchen.setKurzkuchen( aktKurzname );
    kuchen.setAbgabedatum( aktDatum );
    kuchen.setAnzahl( aktAnzahl );
    kuchen.setPreis( aktPreis );
    kuchen.setSorte( aktSorte );
    kuchen.setDiabetikergeeignet( aktDiabetiker );

    return KuchenDB.updateKuchen( aktID, kuchen );
  }

  private boolean insertEntry()
  {
    Kuchen kuchen = new Kuchen();
    kuchen.setAnbieter( aktAnbieter );
    kuchen.setBeschreibung( aktBeschreibung );
    kuchen.setKurzkuchen( aktKurzname );
    kuchen.setAbgabedatum( aktDatum );
    kuchen.setAnzahl( aktAnzahl );
    kuchen.setPreis( aktPreis );
    kuchen.setSorte( aktSorte );
    kuchen.setDiabetikergeeignet( aktDiabetiker );

    aktID = KuchenDB.insertKuchen( kuchen );

    return ( aktID > -1 );
  }

  private boolean queryExit()
  {
    pruefeSpinner();

    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 void pruefeSpinner()
  {
    //Hier muß folgende Situation abgefangen werden:
    //Im Textfeld des Spinners ist schon getippt worden, das Editor-Feld hat aber noch den Fokus.
    //Versucht man zu speichern, so ist der Spinner.Value noch nicht aktualisiert.
    //Falls es die erste oder einzige Änderung des Nutzers bleibt (kein weiteres Feld anfokussiert wird),
    //könnte das untergehen (!hasChanged)
    int spinnerValue = (int)spAnzahl.getValue();
    int spinnerText = Integer.parseInt(
        ( (JSpinner.DefaultEditor)( (JSpinner)spAnzahl ).getEditor() ).getTextField().getText() );

    //System.out.println( "Spinner-Value:" + spinnerValue );
    //System.out.println( "Spinner-Text:" + spinnerText );
    //System.out.println( "Geändert:" + hasChanged );

    if ( spinnerText != spinnerValue )
      hasChanged = true;
  }

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

    aktAnbieter = tfAnbieter.getText();
    if ( aktAnbieter.length() < 2 )
      return fehler( tfAnbieter, "Anbieter muß mind.2 Zeichen haben" );

    aktBeschreibung = tfBeschreibung.getText();

    aktKurzname = tfKurzname.getText();
    if ( aktKurzname.length() < 2 )
      return fehler( tfKurzname, "Name muß mind.2 Zeichen haben" );

    if ( DatenPlausi.okGeld( tfPreis.getText() ) )
      aktPreis = Double.parseDouble( tfPreis.getText().replace( ',', '.' ) );
    else
      return fehler( tfPreis, "Preis muß positiv mit 2 Nachkommastellen sein" );
    if ( aktPreis <= 0.00 && !isVorhanden )
      return fehler( spAnzahl, "Preis muß anfangs größer 0.00 sein" );

    //aktAnzahl = (int)spAnzahl.getValue();
    //siehe pruefenSpinnner
    aktAnzahl = Integer.parseInt(
        ( (JSpinner.DefaultEditor)( (JSpinner)spAnzahl ).getEditor() ).getTextField().getText() );

    if ( aktAnzahl < 1 && !isVorhanden )
      return fehler( spAnzahl, "Anzahl muß anfangs größer 0 sein" );

    aktDiabetiker = ckbDiabetiker.isSelected();

    aktSorte = (int)( (ListItem)cboSorte.getSelectedItem() ).getValueMember();

    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();
    }
    if ( o instanceof JSpinner )
    {
      tf = ( (JSpinner.DefaultEditor)( (JSpinner)o ).getEditor() ).getTextField();
      tf.selectAll();
      tf.requestFocusInWindow();
    }
  }

  @Override
  public void focusLost( FocusEvent e )
  {
  }

  @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;
      }
    }

    if ( o == tfPreis )
    {
      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 == tfAnbieter && kc == KeyEvent.VK_ENTER
        && tfAnbieter.getText().length() > 0 )
      kbFocusManager.focusNextComponent();
    else if ( o == tfBeschreibung && kc == KeyEvent.VK_ENTER )
      kbFocusManager.focusNextComponent();
    else if ( o == tfKurzname && kc == KeyEvent.VK_ENTER
        && tfKurzname.getText().length() > 0 )
      kbFocusManager.focusNextComponent();
    else if ( o == tfDatum && kc == KeyEvent.VK_ENTER
        && tfDatum.getText().length() == 10 )
      kbFocusManager.focusNextComponent();
    else if ( o == tfPreis && kc == KeyEvent.VK_ENTER
        && tfPreis.getText().length() > 0 )
      kbFocusManager.focusNextComponent();
  }

  @Override
  public void keyReleased( KeyEvent e )
  {
    int kc = e.getKeyCode();
    if ( kc == 27 )
    {
      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 stateChanged( ChangeEvent e )
  {
    Object o = e.getSource();

    if ( o instanceof JSpinner && o.equals( spAnzahl ) )
    {
      hasChanged = true;
    }
  }

  @Override
  public void itemStateChanged( ItemEvent e )
  {
    hasChanged = true;
  }
}
