/*
 * AddressBookImageField.java
 *
 * Copyright 2013 John W Dawson
 *
 * This code is distributed under the terms of the GNU General Public License, version 3
 *
 * This class represents a field on the data entry tabs which can be used to select and display images 
 */
 
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.imageio.*;
import java.io.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.filechooser.*;
import java.util.regex.*;
import java.util.*;
import java.util.logging.*;
import javax.naming.directory.*;
class AddressBookImageField extends JPanel implements AddressBookField, ActionListener
{

  private byte imageBuffer [];
  private Container panel;
  private EditPanel tabs;
  private String ldapAttribute;
  private String fieldLabel;
  private MessageLine messageLine;
  private ConfigurationRecord configuration;

  private class ImagePanel extends JComponent
  {

    // This is called when the component is redrawn
    public void paint (Graphics g) 
    {
     // Erase any image currently displayed
      float panelWidth = getWidth ();
      float panelHeight = getHeight ();
      g.setColor (getBackground());
      g.fillRect (0, 0, (int) panelWidth, (int) panelHeight);
      
      // Check if there is an image to display
      if (imageBuffer != null)
      {
        // Read decoded image into a buffer ready for display
        try
        {
          BufferedImage image = ImageIO.read (new ByteArrayInputStream (imageBuffer)); 
              
          // Calculate the position and dimensions for the image to appear filling as much as possible of
          // panel but with correct aspect ratio and centred
          float imageWidth = image.getWidth ();
          float imageHeight = image.getHeight ();
          if (imageWidth / imageHeight > panelWidth / panelHeight)
          {
            // Image is wider in shape than panel, so there will be a blank band at bottom
            float height = panelWidth * imageHeight / imageWidth;
            g.drawImage(image, 0, 0, (int) panelWidth, (int) height, null);
          }
          else
          {
            // Image is narrower in shape than (or identical to) panel, so there will be blank bands left & right (or image
            // will fit perfectly)
            float width = panelHeight * imageWidth / imageHeight;
            g.drawImage (image, (int) ((panelWidth - width) / 2), 0, (int) width, (int) panelHeight, null);
          }
        }
        catch (IOException e)
        {}
      }
          
    }
  }
  
  private ImagePanel imagePanel = new ImagePanel ();
  private JButton loadButton = new JButton ("Load"); 
  private JButton saveButton = new JButton ("Save");
  private JButton clearButton = new JButton ("Clear");
  private DocumentListener listener;
  
  public AddressBookImageField (ConfigurationRecord fieldConfiguration, Map<String, AddressBookField> fieldList, 
                                ConfigurationRecord configuration, MessageLine messageLine)
  {
    this.configuration = configuration;
    this.messageLine = messageLine;
    this.ldapAttribute = fieldConfiguration.getElementValue ("ldapattribute");
    this.fieldLabel = fieldConfiguration.getElementValue ("label");

    // Set size for image panel
    imagePanel.setPreferredSize 
      (new Dimension 
          ((int)fieldConfiguration.getIntegerValue ("imagewidth"),
           (int)fieldConfiguration.getIntegerValue ("imageheight")));
   
    // Create group layout
    GroupLayout layout = new GroupLayout(this);
    layout.setAutoCreateGaps(true);
    this.setLayout(layout);
    
    // Create groups for arranging image panel and buttons
    GroupLayout.ParallelGroup verticalGroup = layout.createParallelGroup(GroupLayout.Alignment.LEADING);
    layout.setVerticalGroup (verticalGroup);
    
    GroupLayout.SequentialGroup horizontalGroup = layout.createSequentialGroup();
    layout.setHorizontalGroup (horizontalGroup);
    
    horizontalGroup.addComponent (imagePanel);
    verticalGroup.addComponent (imagePanel);
    
    // Create groups to arrange buttons
    GroupLayout.SequentialGroup buttonVerticalGroup = layout.createSequentialGroup();
    verticalGroup.addGroup (buttonVerticalGroup);
    GroupLayout.ParallelGroup buttonHorizontalGroup = layout.createParallelGroup(GroupLayout.Alignment.LEADING);
    horizontalGroup.addGroup (buttonHorizontalGroup);

    // Make buttons same width and add make this object handler for clicks
    layout.linkSize (SwingConstants.HORIZONTAL,loadButton, saveButton, clearButton);
    loadButton.addActionListener (this);
    saveButton.addActionListener (this);
    clearButton.addActionListener (this);
    
    // Add buttons to layout
    buttonHorizontalGroup.addComponent (loadButton);
    buttonHorizontalGroup.addComponent (saveButton);
    buttonHorizontalGroup.addComponent (clearButton);
    
    buttonVerticalGroup.addComponent (loadButton);
    buttonVerticalGroup.addComponent (saveButton);
    buttonVerticalGroup.addComponent (clearButton);
    
    // Disable clear & save buttons initially
    clearButton.setEnabled (false);
    saveButton.setEnabled (false);
    
  }

  // This method is called when the set or clear button is pressed    
  public void actionPerformed (ActionEvent event)
  {
    // Check which button was clicked
    if (event.getSource () == loadButton)
    {
  
      // Display file selector
      JFileChooser chooser = new JFileChooser ();
      FileNameExtensionFilter filter = new FileNameExtensionFilter ("Image Files", "jpg", "jpeg");
      chooser.setFileFilter(filter);
      
      // Set default directory to that last used for load, or if not present to last directory
      // used for save
      String defaultDirectory = configuration.getElementValue ("imageloaddirectory");
      if (defaultDirectory == null)
      {
        defaultDirectory = configuration.getElementValue ("imagesavedirectory");
      }
      if (defaultDirectory != null)
      {
        chooser.setCurrentDirectory (new File (defaultDirectory));
      }
      if (chooser.showOpenDialog (this) == JFileChooser.APPROVE_OPTION)
      {
        try
        {
          // File has been selected, read file into buffer
          File imageFile = chooser.getSelectedFile ();
          int imageFileLength = (int) imageFile.length ();
          imageBuffer = new byte [imageFileLength];
          FileInputStream input = new FileInputStream (imageFile);
          input.read (imageBuffer);
          input.close ();
            
          // Save directory in configuration
          configuration.addOrReplaceElement ("imageloaddirectory", imageFile.getParent ());
          
          // Display the image on the panel
          imagePanel.repaint ();
          
          // Enable save and clear buttons
          saveButton.setEnabled (true);
          clearButton.setEnabled (true);
          
          // Notify panel that an image has been set
          listener.insertUpdate (null);
        }
        catch (FileNotFoundException e)
        {
          JOptionPane.showMessageDialog (null, "Image file doesn't exist", 
                                         "Image File Select Error", JOptionPane.ERROR_MESSAGE);
        }
        catch (IOException e)
        {
          JOptionPane.showMessageDialog (null, "Error reading image file", 
                                         "Image File Select Error", JOptionPane.ERROR_MESSAGE);
        }
          
      }
    }
    
    else if (event.getSource () == saveButton)
    {
      // Display file selector
      JFileChooser chooser = new JFileChooser ();
      FileNameExtensionFilter filter = new FileNameExtensionFilter ("Image Files", "jpg", "jpeg");
      chooser.setFileFilter(filter);
      
      // Set default directory to that last used for save, or if not present to lasr directory
      // used for load
      String defaultDirectory = configuration.getElementValue ("imagesavedirectory");
      if (defaultDirectory == null)
      {
        defaultDirectory = configuration.getElementValue ("imageloaddirectory");
      }
      if (defaultDirectory != null)
      {
        chooser.setCurrentDirectory (new File (defaultDirectory));
      }
      
      if (chooser.showSaveDialog (this) == JFileChooser.APPROVE_OPTION)
      {
        try
        {
          // Check if file extension was specified, if not add .jpg
          File imageFile = chooser.getSelectedFile ();
          if (!Pattern.matches (".*\\.\\w+", imageFile.getPath()))
          {
            imageFile = new File (imageFile.getPath() + ".jpg");
          }
          
          // Confirm overwrite  if file already exists
          if (!imageFile.exists () 
                || JOptionPane.showConfirmDialog 
                      (null, "Do you want to replace the existing file?", 
                       "Confirm Replace", JOptionPane.YES_NO_OPTION)
                                                      == JOptionPane.YES_OPTION)
          {
            // Write image buffer contents to selected output file
            FileOutputStream output = new FileOutputStream (imageFile);
            output.write (imageBuffer);
            output.close ();
            messageLine.setTransientText ("Image saved");
            
            // Save directory in configuration
            configuration.addOrReplaceElement ("imagesavedirectory", imageFile.getParent ());
          }
        }
        catch (IOException e)
        {
          JOptionPane.showMessageDialog (null, "Error saving image file", 
                                         "Image File Save Error", JOptionPane.ERROR_MESSAGE);
        }
      }
    }
    else if (event.getSource () == clearButton)
    {
      // Get confirmation from user
      if (JOptionPane.showConfirmDialog 
            (null, "Do you really want to remove this image?", 
            "Confirm Clear", JOptionPane.YES_NO_OPTION)
                                      == JOptionPane.YES_OPTION)
      { 
        clear ();
        
      // Notify panel that an image has been cleared
      listener.removeUpdate (null);
      }
    }
  }
  
  public void setDnField (Boolean terminal)
  {}
  
  public String getText()
  {
    return null;
  }
  
  public byte [] getBinary()
  {
    return imageBuffer;
  }
  
  public void setText(String t)
  {}
  
  public void initialise ()
  {
    // Buttons are initially disabled
    loadButton.setEnabled (false);
    saveButton.setEnabled (false);
    clearButton.setEnabled (false);
    
    this.panel = getParent();
    this.tabs = (EditPanel) panel.getParent();
    
  }
  
  public void displayValue (SearchResult entry)
  {
  
    try
    {
      // Check if attribute is present in entry
      Attribute attribute = entry.getAttributes().get(ldapAttribute);
      if (attribute != null)
      {
        // Display image
        imageBuffer = (byte[]) attribute.get();
        imagePanel.repaint ();
    
        // Enable clear & save buttons
        clearButton.setEnabled (true);
        saveButton.setEnabled (true);
      }  
      else
      {
        // Otherwise clear any previous image
        clear ();
      }  
    }
    catch (Exception e)
    {
      System.out.println (e.getMessage());
      e.printStackTrace ();
      imageBuffer = null;
    }
  }
  
  public void clear ()
  {
    // Set buffer to null and clear image on panel
    imageBuffer = null;
    imagePanel.repaint ();
     
    // Disable clear & save buttons
    clearButton.setEnabled (false);
    saveButton.setEnabled (false);
    
  }  
  
  public Boolean requiredEmpty ()
  {
    // Images are never required
    return false;
  }
  
  public String getLdapAttribute ()
  {
    return ldapAttribute;
  }
  
  public String getValue (SearchResult entry)
  {
    // Get attribute value from attribute entries
    try
    {   
      return String.valueOf (entry.getAttributes().get(ldapAttribute).get());
    }
    catch (Exception e)
    {
      return null;
    }
  }
  
  public String getLabel ()
  {
    return fieldLabel;
  } 
  
  public void setFocus ()
  { 
    // Return focus to set button (reset tab if necessary)
    tabs.setSelectedComponent (panel);
    loadButton.requestFocusInWindow ();
  }

  public Boolean validateText ()
  {
    return true;
  }  

  public Boolean isDnField ()
  {
    return false;
  }
  
  public Boolean isTerminal ()
  {
    return false;
  }
  
  public void addDocumentListener (DocumentListener listener)
  {
    // Save ref to the listener, so we can manually notify changes to image
    this.listener = listener;
  }

  public void setEnabled (boolean enabled)
  { 
    loadButton.setEnabled (enabled);
    
    // Clear & save buttons are not enabled if no image set
    clearButton.setEnabled (enabled && imageBuffer != null);
    saveButton.setEnabled (enabled && imageBuffer != null);
  }

  public CallButton getCallButton ()
  {
    return null;
  }    
  public void enableCallButton () {};
  public void disableCallButton () {};
      
}