/*******************************************************************************
 * This file is part of GECAMed.
 * 
 * GECAMed is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License (L-GPL) as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GECAMed 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License (L-GPL)
 * along with GECAMed.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * GECAMed is Copyrighted by the Centre de Recherche Public Henri Tudor (http://www.tudor.lu)
 * (c) CRP Henri Tudor, Luxembourg, 2008
 *******************************************************************************/
package lu.tudor.santec.gecamed.agenda.gui.widgets;

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.List;
import java.util.Locale;

import javax.swing.ImageIcon;
import javax.swing.JComponent;

import lu.tudor.santec.gecamed.agenda.ejb.entity.beans.Appointment;
import lu.tudor.santec.gecamed.core.gui.GECAMedColors;
import lu.tudor.santec.gecamed.core.gui.utils.GECAMedGuiUtils;
import lu.tudor.santec.i18n.Translatrix;

/**
 * FramArea like component that paints a rounded rectangle with appointment information.
 * Color is the color of a color. 
 * 
 * @author martin.heinemann@tudor.lu
 * 18.07.2007
 * 10:06:58
 *
 *
 * @version
 * <br>$Log: AppointmentProposalArea.java,v $
 * <br>Revision 1.10  2012-07-17 14:36:01  troth
 * <br>Ticket #873 - Constraints are now static and the dialog is redesigned, add a today and 6 week button.
 * <br>
 * <br>Revision 1.9  2011-04-05 08:43:15  troth
 * <br>fix some warnings
 * <br>
 * <br>Revision 1.8  2008-09-25 09:42:27  heinemann
 * <br>fixed copyrights
 * <br>
 * <br>Revision 1.7  2008-08-19 10:25:08  heinemann
 * <br>cleanup
 * <br>
 * <br>Revision 1.6  2008-02-11 16:46:24  heinemann
 * <br>moved computeColor to GECAMedColors
 * <br>
 * <br>Revision 1.5  2008-01-18 16:09:05  heinemann
 * <br>code cleanup and java doc
 * <br>
 * <br>Revision 1.4  2007/08/28 11:34:36  heinemann
 * <br>*** empty log message ***
 * <br>
 *   
 */
public class AppointmentProposalArea extends JComponent implements ComponentListener {
	private static final long serialVersionUID = 1L;

	private String itsHeadLine;

	private String itsDescription;

	private Color fontColor;

//	private Shape itsShape;

	private List<Listener> listeners = new ArrayList<Listener>();

	private boolean border;

	private boolean roundedRectangle;

	private boolean selected;

	private ImageIcon icon;

	// private Color selectionColor = new Color(196, 0, 0);
	private Color selectionColor = Color.BLACK;

//	private boolean isMoving = false;

	private Color bgColor;

	public double xPosition = 0.0;

	public double yPosition = 54.0;

//	private double angle = 0.0;

//	private Color alphaFontColor;

//	private Font alphaFont;

	private Font normalFont;

	private float ALPHA_DEFAULT = 0.6f;

	private float SELECT_OFFSET = 0.2f;


	private float alphaValue = ALPHA_DEFAULT;

	
	private int lineDistance = 4;
	
	private static int HEADER_HEIGHT = 20;
	
	private boolean showHeader = true;

	private int lineWrap = -1;

	private Appointment appointment;

	private boolean flatterband = false;
	
	public final DateFormat timeFormat = new SimpleDateFormat("HH:mm",
			Locale.getDefault());
	public final DateFormat dayFormat = new SimpleDateFormat("EEEE",
			Locale.getDefault());
	public final DateFormat dateFormat = GECAMedGuiUtils.getDateFormat(false);
	
	
	private Font dayFont  = new Font("Arial", Font.BOLD, 18);
	private Font rangeFont = new Font("Arial", Font.BOLD, 16);
	private Font flatterFont = new Font("Arial", Font.BOLD, 12);
	

	/**
	 * @throws Exception
	 */
	public AppointmentProposalArea() {
		/* ================================================== */
		this.normalFont = new Font("Verdana", Font.PLAIN, 10);
		this.setFont(normalFont);
		// change color for drag
		this.setBackground(new Color(100, 100, 245));
		this.fontColor = Color.WHITE;
//		this.alphaFontColor = fontColor;

		this.border = true;
		this.roundedRectangle = true;
		this.selected = false;
		
		this.addComponentListener(this);
		/* ================================================== */
	}

	/**
	 * Set the Event object for the FrameArea
	 * @param event
	 */
	public void setAppointment(Appointment app, Color color) {
		/* ================================================== */
		this.appointment = app;
		// compute colors
		this.bgColor = color;

		
		/* ------------------------------------------------------- */
			this.fontColor = GECAMedColors.computeForeground(this.bgColor);
		/* ------------------------------------------------------- */
		/* ------------------------------------------------------- */
//		this.alphaFontColor = new Color(fontColor.getRed(), fontColor.getGreen(), fontColor.getBlue(), 220);
		
		
		/* ================================================== */
	}


	/**
	 * @param b
	 */
	public void enableFlatterband(boolean b) {
		/* ================================================== */
		this.flatterband = b;
		/* ================================================== */
	}
	
	/**
	 * @param aValue
	 */
//	@Deprecated
	public void setAlphaValue(float aValue) {
		if (aValue > 1.0f)
			aValue = 1.0f;
//		this.alphaValue = aValue - SELECT_OFFSET;
	}

	/**
	 * @return
	 */
	public float getAlphaValue() {
		return this.alphaValue;
	}

	// The toolkit will invoke this method when it's time to paint
	public void paint(Graphics g) {
		/* ================================================== */
		try {
			setBackground(this.bgColor);
		} catch (Exception e) {
		}
		/* ------------------------------------------------------- */
		Graphics2D g2 = (Graphics2D) g;
		// makes the graphics smoother
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		/* ------------------------------------------------------- */
		int width = (int) getWidth();
		int height = (int) getHeight();
		BufferedImage buffImg = null;
		try {
			/* ------------------------------------------------------- */
			buffImg = new BufferedImage(width, height,
						  				BufferedImage.TYPE_INT_ARGB);
			/* ------------------------------------------------------- */
		} catch (Exception e) {
//			System.out.println("W: " + width + " H: " + height);
		}
		/* ------------------------------------------------------- */
		Graphics2D gbi = buffImg.createGraphics();
		gbi.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
									RenderingHints.VALUE_ANTIALIAS_ON);
		gbi.setStroke(new BasicStroke(1.0f));
		
		
		// ===========================================================
		// draw round rectangle
		//
		// ===========================================================
		if (this.roundedRectangle) {
			/* ------------------------------------------------------- */
			AlphaComposite ac = AlphaComposite.getInstance(
					AlphaComposite.DST_OVER, alphaValue);
			gbi.setComposite(ac);
			gbi.setPaint(this.getBackground());
			/* ------------------------------------------------------- */
			gbi.fill(new RoundRectangle2D.Double(0, 0, width, height, 20, 20));
			/* ------------------------------------------------------- */
			if (this.border) {
				gbi.setPaint(Color.black);
				gbi.draw(new RoundRectangle2D.Double(1, 1, width - 2,
						height - 2, 17, 17));
			}
			/* ------------------------------------------------------- */
		}
		// ============================================================
		// draw non-round rectangle
		// ============================================================
		else {
			/* ------------------------------------------------------- */
			AlphaComposite ac = null;
			ac = AlphaComposite.getInstance(AlphaComposite.DST_OVER, alphaValue);
			gbi.setComposite(ac);
			gbi.setPaint(this.getBackground());
			/* ------------------------------------------------------- */
			gbi.fill(new Rectangle2D.Double(0, 0, width, height));
			/* ------------------------------------------------------- */
			if (this.border) {
				gbi.setPaint(Color.black);
				gbi.draw(new Rectangle2D.Double(1, 1, width - 2, height - 2));
			}
			/* ------------------------------------------------------- */
		}

		g2.drawImage(buffImg, null, 0, 0);
		/* ------------------------------------------------------- */
		// ==============================================================
		// creation of the darker header starts here
		//
		// background events do not have a darker header
		// ==============================================================
		Graphics2D gbiHeader = null;

		if (showHeader) {
			/* ------------------------------------------------------- */
			// create darker header
			BufferedImage buffImgHeader = new BufferedImage(width, HEADER_HEIGHT,
					BufferedImage.TYPE_INT_ARGB);
			gbiHeader = buffImgHeader.createGraphics();
			/* ------------------------------------------------------- */
			gbiHeader.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
					RenderingHints.VALUE_ANTIALIAS_ON);
			gbiHeader.setStroke(new BasicStroke(1.0f));
			/* ------------------------------------------------------- */
			gbiHeader.setPaint(this.getBackground());
			gbiHeader.fill(new RoundRectangle2D.Double(0, 0, width, 20, 20, 20));
			/* ------------------------------------------------------- */
			// paint
			g2.drawImage(buffImgHeader, null, 0, 0);
			/* ------------------------------------------------------- */
		}

		/* ------------------------------------------------------- */
		int xpos = 5;
		if (icon != null) {
			g2.drawImage(icon.getImage(), xpos, 2, this);
			xpos += icon.getIconWidth() + 3;
		}

		// =================================================================================
		// actions below this point will be placed on top of the "colored glass"
		//
		// =================================================================================

		// ============================================================
		// paint the header, normally the time periode
		// ============================================================
		Font timeFont = this.getFont().deriveFont(Font.BOLD);
		/* ------------------------------------------------------- */
		// set alpha fontColor for backgroudn events
			g2.setPaint(fontColor);
//			g2.setPaint(alphaFontColor);
		/* ------------------------------------------------------- */
		g2.setFont(timeFont);
		int ypos = 10;
		if (itsHeadLine != null) {
			g2.drawString(itsHeadLine, xpos, ypos);
			ypos += 15;
			xpos = 5;
		}
		

		
		// ============================================================
		// paint the main view
		//
		// ============================================================
		if (this.appointment != null) {
			/* ------------------------------------------------------- */
			
			/* ------------------------------------------------------- */
			// day
			g2.setFont(dayFont);
			g2.drawString(dayFormat.format(appointment.getStartDate()), xpos+10, ypos+20);
			// date
			g2.setFont(rangeFont);
//			g2.drawString(dateFormat.format(appointment.getStartDate()), xpos+8, ypos+25+dayFont.getSize2D());
			g2.drawString(dateFormat.format(appointment.getStartDate()), xpos+130, ypos+21);
			/* ------------------------------------------------------- */
			// time
//			g2.drawString(timeFormat.format(appointment.getStartDate()) +" - " +
//					timeFormat.format(appointment.getEndDate()), xpos+75, ypos+40+(2*dayFont.getSize2D()));
			
			g2.drawString(timeFormat.format(appointment.getStartDate()) +" - " +
					timeFormat.format(appointment.getEndDate()), xpos+10, ypos+25+dayFont.getSize2D());
			/* ------------------------------------------------------- */
			
			// print the past warning // is it used ??? //TODO
			if (this.flatterband) {
				/* ------------------------------------------------------- */
				
				g2.setFont(flatterFont);
				g2.drawString(Translatrix.getTranslationString("Agenda.findfree.eventInThePast"), xpos+10, 
						this.getHeight()-20);
				
				/* ------------------------------------------------------- */
			}
			/* ------------------------------------------------------- */
		}
		
		Font descriptionFont = this.getFont();
		g2.setFont(descriptionFont);
		
		// ============================================================
		// paint the summary
		//
		// if the event is a background event, the summary is painted
		// in a diagonale
		// ============================================================
		if (itsDescription != null) {
			if (showHeader && itsHeadLine == null)
				ypos = HEADER_HEIGHT + 15;
				/* ------------------------------------------------------- */
				int fontHeight =	(int) this.getFont().getSize2D();
				// get the optimal width
				int itsW = g2.getFontMetrics().stringWidth(itsDescription);
				FontMetrics fm = g2.getFontMetrics();
				
				
				
				if (itsW > this.getWidth()) {
					/* ------------------------------------------------------- */
					// check if the width of the frame area has changed and we
					// must find a new fitting length to split the string
					int splitWidth = getWidth()-15;
					if (lineWrap < 0 
							|| fm.stringWidth(itsDescription.substring(0, lineWrap)) < splitWidth - 15 
							|| fm.stringWidth(itsDescription.substring(0, lineWrap)) > splitWidth) {
						/* ------------------------------------------------------- */
						// wrap the lines
						String s = itsDescription;
						this.lineWrap = s.length()-1;
						/* ------------------------------------------------------- */
						// shorten the string as often as its painted length
						// fits into the framearea
						while (fm.stringWidth(s)> splitWidth) {
							s = itsDescription.substring(0, lineWrap);
							lineWrap--;
						}
						/* ------------------------------------------------------- */
					}
					/* ------------------------------------------------------- */
					// paint the string
					int pos 		= 0;
					int yposString  = ypos;
					
					while (pos < itsDescription.length()) {
						if (pos+lineWrap >= itsDescription.length())
							g2.drawString(itsDescription.substring(pos, itsDescription.length()-1).trim(), xpos, yposString);
						else
							g2.drawString(itsDescription.substring(pos, pos+lineWrap).trim(), xpos, yposString);
						pos = pos+lineWrap;
						yposString = yposString + fontHeight + 5;
					}
					
					/* ------------------------------------------------------- */
				}
				/* ------------------------------------------------------- */
			}

		// ===============================================================
		// draw selection border on this frame area
		//
		// ===============================================================
			/* ------------------------------------------------------- */
			// ??
			// float dash1[] = { 1.0f };
			// BasicStroke dashed = new BasicStroke(10.0f, BasicStroke.CAP_BUTT,
			// BasicStroke.JOIN_BEVEL, 10.0f, dash1, 0.0f);

			g2.setPaint(selectionColor);
			g2.setStroke(new BasicStroke(1.5f));
			if (this.roundedRectangle)
				g2.draw(new RoundRectangle2D.Double(1, 1, width - 2,
						height - 2, 17, 17));
			else
				g2.draw(new Rectangle2D.Double(1, 1, width - 2, height - 2));
			/* ------------------------------------------------------- */
		if (gbiHeader != null)
			super.paint(gbiHeader);
		super.paint(g2);
		super.paint(gbi);
	}

	/**
	 * @return Returns the border.
	 */
	public boolean isBorder() {
		return border;
	}

	/**
	 * @param border
	 *            The border to set.
	 */
	public void setBorder(boolean border) {
		this.border = border;
	}

	public void setRoundedRectangle(boolean rounded) {
		this.roundedRectangle = rounded;
	}

	public boolean isRoundedRectangle() {
		return this.roundedRectangle;
	}

	/**
	 * Label text placed on the first line in the FrameArea. Example value:
	 * "08:00-11.30"
	 *
	 * @param aHeadLine
	 */
	public void setHeadLine(String aHeadLine) {
		itsHeadLine = aHeadLine;
		if (aHeadLine == null)
			itsHeadLine = "";
	}

	/**
	 * Label text placed below HeadLine in the FrameArea. Example value:
	 * "Meeting with group C"
	 *
	 * @param aDescription
	 */
	public void setDescription(String aDescription) {
		itsDescription = aDescription;
		if (aDescription == null)
			itsDescription = "";
	}

	public void setFontColor(Color aColor) {
		fontColor = aColor;
	}

	public Color getFontColor() {
		return fontColor;
	}

	public void addListener(Listener listener) {
		listeners.add(listener);
	}
	
	
	// ========================================================================
	// Methods for the component listener
	// ------------------------------------------------------------------------

	public void componentResized(ComponentEvent e) {
		/* ====================================================== */
		
//		this.lineWrap = -1;
		/* ====================================================== */
	}

	public void componentHidden(ComponentEvent e) {}
	public void componentMoved(ComponentEvent e) {}
	public void componentShown(ComponentEvent e) {}
	
	
	/**
	 * @author martin.heinemann@tudor.lu
	 * 18.07.2007
	 * 09:45:33
	 *
	 *
	 * @version
	 * <br>$Log: AppointmentProposalArea.java,v $
	 * <br>Revision 1.10  2012-07-17 14:36:01  troth
	 * <br>Ticket #873 - Constraints are now static and the dialog is redesigned, add a today and 6 week button.
	 * <br>
	 * <br>Revision 1.9  2011-04-05 08:43:15  troth
	 * <br>fix some warnings
	 * <br>
	 * <br>Revision 1.8  2008-09-25 09:42:27  heinemann
	 * <br>fixed copyrights
	 * <br>
	 * <br>Revision 1.7  2008-08-19 10:25:08  heinemann
	 * <br>cleanup
	 * <br>
	 * <br>Revision 1.6  2008-02-11 16:46:24  heinemann
	 * <br>moved computeColor to GECAMedColors
	 * <br>
	 * <br>Revision 1.5  2008-01-18 16:09:05  heinemann
	 * <br>code cleanup and java doc
	 * <br>
	 * <br>Revision 1.4  2007/08/28 11:34:36  heinemann
	 * <br>*** empty log message ***
	 * <br>
	 * <br>Revision 1.3  2007/08/09 14:10:34  heinemann
	 * <br>*** empty log message ***
	 * <br>
	 * <br>Revision 1.2  2007/08/01 07:08:21  heinemann
	 * <br>*** empty log message ***
	 * <br>
	 * <br>Revision 1.1  2007/07/19 14:09:47  heinemann
	 * <br>*** empty log message ***
	 * <br>
	 * <br>Revision 1.22  2007/07/18 07:27:19  heinemann
	 * <br>line wrap for description string
	 * <br>
	 *   
	 */
	public static interface Listener extends EventListener {
		public void selected(AppointmentProposalArea source) throws Exception;

		public void mouseOver(AppointmentProposalArea source) throws Exception;

		public void mouseOut(AppointmentProposalArea source) throws Exception;

		public void popupMenu(AppointmentProposalArea source) throws Exception;

		public void moved(Point pos1, Point pos2) throws Exception;
	}

	public void setSelected(boolean flag) {
		this.selected = flag;
		this.setBrightness(flag);
		repaint();
	}

	public boolean isSelected() {
		return this.selected;
	}

	/**
	 * Switches the brightnes of a frame area
	 * to bright or darker
	 *
	 * true - brighter
	 * false - darker
	 * @param b
	 */
	public void setBrightness(boolean b) {
		/* ================================================== */
		if (b)
			this.alphaValue = ALPHA_DEFAULT + SELECT_OFFSET;
		else
			this.alphaValue = ALPHA_DEFAULT - SELECT_OFFSET;
		/* ================================================== */
	}


	public ImageIcon getIcon() {
		return icon;
	}

	public void setIcon(ImageIcon icon) {
		this.icon = icon;
	}

	public void setLineDistance (int dst) {
		this.lineDistance = dst;
	}
	
	public int getLineDistance() {
		return this.lineDistance;
	}
	
	
	/**
	 * Show the darker header
	 * 
	 * @param b
	 */
	public void showHeader(boolean b) {
		/* ================================================== */
		this.showHeader = b;
		/* ================================================== */
	}
	
	
	  /**
	   * Draw nice lines in the area
	   *
	 * @param g
	 * @param x
	 * @param y
	 * @param width
	 * @param height
	 * @param round round rectangle or not
	 */
	public void drawHatchedRect(Graphics g, int x, int y, int width, int height, boolean round)
	  {
		final int DST = this.lineDistance;
		if (round)
			g.drawRoundRect(x, y, width, height, 20, 20);
		else
			g.drawRect(x, y, width, height);

		for (int i = DST; i < width + height; i += DST) {
		  int p1x = (i <= height) ? x : x + i - height;
		  int p1y = (i <= height) ? y + i : y + height;
		  int p2x = (i <= width) ? x + i : x + width;
		  int p2y = (i <= width) ? y : y + i - width;
		  g.drawLine(p1x, p1y, p2x, p2y);
		}

//		for (int i = DST; i < width + height; i += DST) {
//			int p1x = (i <= height) ? x : x + i -height;
//			int p1y = (i <= height) ? y + height - i : y;
//
//			int p2x = (i <= width) ? x + i : x + width;
//			int p2y = (i <= width) ? y + height : y + height - i;
//			g.drawLine(p1x, p1y, p2x, p2y);
//		}


//		// ||||||
//		for (int i = DST; i < width; i +=DST) {
//			int p1x = x + i;
//			int p1y = y;
//			int p2x = p1x;
//			int p2y = y + height;
//			g.drawLine(p1x, p1y, p2x, p2y);
//		}
//		// ----
//		// ----
//		// ----
//		for (int i = DST; i < height; i +=DST) {
//			int p1x = x;
//			int p1y = y + i;
//			int p2x = x + width;
//			int p2y = p1y;
//			g.drawLine(p1x, p1y, p2x, p2y);
//		}


	  }






	/* (non-Javadoc)
	 * @see javax.swing.JComponent#isOptimizedDrawingEnabled()
	 */
	@Override
	public boolean isOptimizedDrawingEnabled() {
		/* ====================================================== */
		return false;
		/* ====================================================== */
	}


}
