package org.openswing.swing.client;

import java.beans.*;
import java.util.*;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

import org.openswing.swing.form.model.client.*;
import org.openswing.swing.logger.client.*;
import org.openswing.swing.util.client.*;
import com.toedter.calendar.*;
import javax.swing.text.JTextComponent;


/**
 * <p>Title: OpenSwing Framework</p>
 * <p>Description: Class that could be used as bease class for an input control.</p>
 * <p>Copyright: Copyright (C) 2006 Mauro Carniel</p>
 *
 * <p> This file is part of OpenSwing Framework.
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the (LGPL) Lesser General Public
 * License as published by the Free Software Foundation;
 *
 *                GNU LESSER GENERAL PUBLIC LICENSE
 *                 Version 2.1, February 1999
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *       The author may be contacted at:
 *           maurocarniel@tin.it</p>
 *
 * @author Mauro Carniel
 * @version 1.0
 */
public class BaseInputControl extends JPanel implements InputControl {

  /** attribute name that identifies the input control */
  private String attributeName = null;

  /** mandatory property of the input control */
  private boolean required = false;

  /** define if the input control is enabled on INSERT mode */
  private boolean enabledOnInsert = true;

  /** define if the input control is enabled on EDIT mode */
  private boolean enabledOnEdit = true;

  /** define if the input control value is duplicated when user has clicked on COPY button */
  private boolean canCopy = true;

  /** define if the input control value is changed */
  private boolean changed = false;

  /** label used when showing error messages related to the input control */
  private LabelControl linkedLabel = null;

  /** mandatory symbol */
  private JLabel requiredIcon = new JLabel("*");

  /** old input control value */
  protected Object oldValue;

  /** value changed listener list */
  private ArrayList valueChangedListeners = new ArrayList();

  /** default background color of the binding component */
  protected Color defaultBackgroundColor = null;

  /** tooltip text */
  private String toolTipText = null;

  /** column text horizontal alignment */
  private int textAlignment = SwingConstants.LEFT;

  /** popup menu */
  private InputControlPopupMenu inputControlPopupMenu;


  /**
   * Constructor.
   */
  public BaseInputControl() {
    this.setOpaque(false);
    addFocusListener(new FocusListener() {
      public void focusGained(FocusEvent e) {
        if (!getBindingComponent().hasFocus())
          // set focus to the component inside this...
          getBindingComponent().requestFocus();
      }
      public void focusLost(FocusEvent e) {
      }
    });

    if (Beans.isDesignTime()) {
      requiredIcon.setForeground(Color.red);
      requiredIcon.setFont(new Font(requiredIcon.getFont().getFontName(),Font.PLAIN,20));
      requiredIcon.setPreferredSize(new Dimension(15, 15));
      requiredIcon.setFocusable(false);
    }
    else {
      requiredIcon.setText("");
      requiredIcon.setIcon(new ImageIcon(ClientUtils.getImage("obl.gif",getClass())));
    }

  }


  /**
   * Method called by the sub-class to initialize focus listener used to push input control value in the VOModel.
   * @param component component inside this
   */
  protected void initListeners() {
    defaultBackgroundColor = getBindingComponent().getBackground();

    getBindingComponent().addFocusListener(new FocusListener() {

      public void focusGained(FocusEvent e) {
        controlFocusGained(e);
      }

      public void focusLost(FocusEvent e) {
        controlFocusLost(e);
      }

    });

    if (getBindingComponent() instanceof ItemSelectable) {
      ((ItemSelectable)getBindingComponent()).addItemListener(new ItemListener() {
        public void itemStateChanged(ItemEvent e) {
          if (e.getStateChange()==e.SELECTED && attributeName!=null) {
            maybeFireValueChangedEvent();
          }
        }
      });
    }

    inputControlPopupMenu = new InputControlPopupMenu(this);

    ClientUtils.addTabListener(getBindingComponent());
  }


  /**
   * @return component inside this whose contains the value
   */
  public JComponent getBindingComponent() {
    Logger.error(this.getClass().getName(), "getBindingComponent", "This method must be overridden in '"+this.getClass().getName()+"'",null);
    return null;
  }


  /**
   * Add a value changed listener to this input control.
   * @param listener value changed listener to add
   */
  public final void addValueChangedListener(ValueChangeListener listener) {
    this.valueChangedListeners.add(listener);
  }


  /**
   * Remove a value changed listener from this input control.
   * @param listener value changed listener to remove
   */
  public final void removeValueChangedListener(ValueChangeListener listener) {
    this.valueChangedListeners.remove(listener);
  }


  /**
   * Method called by the sub-class to fire a value changed event.
   */
  protected void maybeFireValueChangedEvent() {
    // retrieve current value...
    Object newValue = getValue();

    if (isEnabled() &&
        ((this.oldValue==null && newValue!=null) ||
         (this.oldValue!=null && newValue==null) ||
         (this.oldValue!=null && newValue!=null && ! this.oldValue.equals(newValue)))) {

      // mark the input control as changed
      setChanged(true);

      // fire value changed events...
      ValueChangeEvent e = new ValueChangeEvent(this, attributeName, this.oldValue, newValue);
      for (int i = 0; i < this.valueChangedListeners.size(); i++)
        ((ValueChangeListener)this.valueChangedListeners.get(i)).valueChanged(e);

      // update old value...
      this.oldValue = newValue;
    }

  }


  /**
   * This method is called when the input control receives focus.
   */
  private void controlFocusGained(FocusEvent e) {
    if (this instanceof DateControl)
      maybeFireValueChangedEvent();
    oldValue = getValue();
    if (ClientSettings.VIEW_BACKGROUND_SEL_COLOR && isEnabled()) {
      getBindingComponent().setBackground(ClientSettings.BACKGROUND_SEL_COLOR);
    }

    if (ClientSettings.SELECT_DATA_IN_EDITABLE_FORM &&
        isEnabled() &&
        getBindingComponent() instanceof JTextComponent)
      ((JTextComponent)getBindingComponent()).select(0,((JTextComponent)getBindingComponent()).getText().length());

  }


  /**
   * This method is called when the input control losts focus.
   */
  private void controlFocusLost(FocusEvent e) {
    if (ClientSettings.VIEW_BACKGROUND_SEL_COLOR && isEnabled()) {
      getBindingComponent().setBackground(defaultBackgroundColor);
    }
    maybeFireValueChangedEvent();
  }


  /**
   * Link the input control label to the specified label.
   * @param linkedLabel label used when showing error messages related to the input control
   */
  public final void setLinkLabel(LabelControl linkedLabel) {
    this.linkedLabel = linkedLabel;
  }


  /**
   * @return label used when showing error messages related to the input control
   */
  public final LabelControl getLinkLabel() {
    return linkedLabel;
  }


  /**
   * @return mandatory property of the input control
   */
  public final boolean isRequired() {
    return required;
  }


  /**
   * @return current Font setting
   */
  public final Font getFont() {
    return getBindingComponent()==null?null:getBindingComponent().getFont();
  }


  /**
   * Set the specified font.
   * @param font Font to set
   */
  public final void setFont(Font font) {
    if (getBindingComponent()!=null)
      getBindingComponent().setFont(font);
  }


  /**
   * Set a tooltip text. This text will be translated according to the internationalization settings.
   * @param toolTipText tool tip text entry in the dictionary
   */
  public final void setToolTipText(String toolTipText) {
    this.toolTipText = toolTipText;
    if (!Beans.isDesignTime())
      getBindingComponent().setToolTipText(ClientSettings.getInstance().getResources().getResource(toolTipText));
  }


  /**
   * @return tool tip text entry in the dictionary
   */
  public final String getToolTipText() {
    return toolTipText;
  }


  /**
   * Define mandatory property of the input control.
   * @param require mandatory property of the input control
   */
  public final void setRequired(boolean required) {
    if (!this.required && required && ClientSettings.VIEW_MANDATORY_SYMBOL) {
      if (this.getLayout() instanceof FlowLayout)
        this.add(requiredIcon);
      else if (this.getLayout() instanceof BorderLayout)
        this.add(requiredIcon,BorderLayout.EAST);
      else if (this.getLayout() instanceof GridBagLayout)
        this.add(requiredIcon, new GridBagConstraints(4, 0, 1, 1, 0.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 5, 0));

      this.revalidate();
      this.repaint();
    } else if (this.required && !required && ClientSettings.VIEW_MANDATORY_SYMBOL) {
      this.remove(requiredIcon);
    }
    this.required = required;
  }


  /**
   * @return <code>true</code> if the input control is enabled on INSERT mode, <code>false</code> otherwise
   */
  public final boolean isEnabledOnInsert() {
    return enabledOnInsert;
  }


  /**
   * Define if the input control is enabled on INSERT mode.
   * @param enabled <code>true</code> if the input control is enabled on INSERT mode, <code>false</code> otherwise
   */
  public final void setEnabledOnInsert(boolean enabled) {
    this.enabledOnInsert = enabled;
  }


  /**
   * @return <code>true</code> if the input control is enabled on EDIT mode, <code>false</code> otherwise
   */
  public final boolean isEnabledOnEdit() {
    return enabledOnEdit;
  }


  /**
   * Define if the input control is enabled on EDIT mode, <code>false</code> otherwise
   * @param enabled mandatory property of the input control
   */
  public final void setEnabledOnEdit(boolean enabled) {
    this.enabledOnEdit = enabled;
  }


  /**
   * @return define if the input control value is duplicated when user has clicked on COPY button
   */
  public final boolean isCanCopy() {
   return canCopy;
  }


  /**
   * Define if the input control value is duplicated when user has clicked on COPY button.
   * @param canCopy define if the input control value is duplicated when user has clicked on COPY button
   */
  public final void setCanCopy(boolean canCopy) {
    this.canCopy = canCopy;
  }


  /**
   * Link the input control to the form which contains it and with the specified the attribute.
   * @param attributeName attribute name to which link the input control
   */
  public final void setAttributeName(String attributeName) {
    this.attributeName = attributeName;
//    AttributeNameEditor.setColumnType(this.getClass());
  }


  /**
   * Link the input control to the form which contains it and with the specified the attribute.
   * @param attributeName attribute name to which link the input control
   */
  public final String getAttributeName() {
    return attributeName;
  }


  /**
   * Set current input control abilitation.
   */
  public void setEnabled(boolean enabled) {
    Logger.error(this.getClass().getName(), "setEnabled", "This method must be overridden in '"+this.getClass().getName()+"'",null);
  }


  /**
   * @return current input control abilitation
   */
  public boolean isEnabled() {
    try {
      Logger.error(this.getClass().getName(), "isEnabled",
                   "This method must be overridden in '" +
                   this.getClass().getName() + "'", null);
      return false;
    }
    catch (Exception ex) {
      return false;
    }
  }


  /**
   * @return value related to the input control
   */
  public Object getValue() {
    Logger.error(this.getClass().getName(), "getValue", "This method must be overridden in '"+this.getClass().getName()+"'",null);
    return null;
  }


  /**
   * Set value to the input control.
   * @param value value to set into the input control
   */
  public void setValue(Object value) {
    Logger.error(this.getClass().getName(), "setValue", "This method must be overridden in '"+this.getClass().getName()+"'",null);
  }


  /**
   * @return <code>true</code> if the input control value is changed, <code>false</code> otherwise
   */
  public final boolean isChanged() {
    return changed;
  }


  /**
   * Define if the input control value is changed.
   * @param changed <code>true</code> if the input control value is changed, <code>false</code> otherwise
   */
  public final void setChanged(boolean changed) {
    this.changed = changed;
  }


  /**
   * @return value changed listeners list
   */
  public final ValueChangeListener[] getValueChangeListeners() {
    return (ValueChangeListener[])valueChangedListeners.toArray(new ValueChangeListener[valueChangedListeners.size()]);
  }


  public void requestFocus() {
    getBindingComponent().requestFocus();
  }


  /**
   * @return column text horizontal alignment
   */
  public final int getTextAlignment() {
    return this.textAlignment;
  }


  /**
   * Set column text horizontal alignement.
   * @param alignment column text horizontal alignement; possible values: "SwingConstants.LEFT", "SwingConstants.RIGHT", "SwingConstants.CENTER".
   */
  public final void setTextAlignment(int alignment) {
    this.textAlignment = alignment;
    if (!Beans.isDesignTime() && getBindingComponent()!=null && getBindingComponent() instanceof JTextField)
      ((JTextField)getBindingComponent()).setHorizontalAlignment(alignment);
  }



}
