/******************************************************************************
 * Copyright (c) 2006, Intalio Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Intalio Inc. - initial API and implementation
 *******************************************************************************/

/**
 * Date             Author               Changes
 * Jul 12, 2006     hmalphettes          Created
 * Nov 2006         Hallvard Traetteberg Fix for caching the image and disposing it
 *                                       correctly.
 **/

package org.eclipse.stp.bpmn.figures;

import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.FigureUtilities;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.ImageUtilities;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gmf.runtime.draw2d.ui.figures.WrapLabel;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Transform;
import org.eclipse.swt.widgets.Display;

/**
 * A vertical label. Rotatable.
 * 
 * @author htraetteberg caching of the image and dispose of it
 * @author vlevytskyy added support for scale
 * @author hmalphettes
 * @author <a href="http://www.intalio.com">&copy; Intalio, Inc.</a>
 */
public class VerticalLabel extends WrapLabel {
    
//  cache the image
    private Image labelImage = null;
    private double cacheScale = -1;
    
    /** true when vertical
     * false when standard horizontal label */
    private boolean _isVertical = true;
    
    /**
     * Creates a new label.
     */
    public VerticalLabel() {
        setOpaque(true);
        setBackgroundColor(ColorConstants.blue);
    }
    /**
     * Creates a new label.
     */
    public VerticalLabel(boolean isVertical) {
        this();
        setVertical(isVertical);
    }
    
    
    /**
     * Revalidate should be called whenever visual attributes change
     */
    public void revalidate() {
        disposeImage();
        super.revalidate();
    }

// 
    /**
     * removeNotify will be called whenever the figure is removed from its parent
     */
    public void removeNotify() {
        super.removeNotify();
        disposeImage();
    }
    
    /**
     * dispose the image
     */
    private void disposeImage() {
        if (labelImage != null) {
            if (!labelImage.isDisposed()) {
                    labelImage.dispose();
            }
            labelImage = null;
        }
    }


    /**
     * 
     */
    public void paintFigure(final Graphics graphics) {
        if (!_isVertical) {
            super.paintFigure(graphics);
            return;
        }
        
        if (labelImage != null && cacheScale != graphics.getAbsoluteScale()) {
            disposeImage();
        }
        if (labelImage == null) {
            String theText = super.getText();
            if (theText == null || theText.trim().length() == 0) {
                theText = " ";
            }
            labelImage = createRotatedImageOfString(graphics, theText, super
                    .getFont(), getForegroundColor(), getParent()
                    .getBackgroundColor(), false);
            cacheScale = graphics.getAbsoluteScale();
        }
        
        double scale = graphics.getAbsoluteScale();
        graphics.pushState();
        graphics.scale(1 / scale);
        Point topLeft = getBounds().getTopLeft();
        
        topLeft = new Point(topLeft.x * scale, topLeft.y * scale);
        graphics.drawImage(labelImage, topLeft);
        graphics.popState();
    }

    /**
     * Fix the issues in the ImageUtilities where the size of the image is the
     * ascent of the font instead of being its height.
     * 
     * Also uses the GC for the rotation.
     * 
     * The biggest issue is the very idea of using an image. The size of the
     * font should be given by the mapmode, not in absolute device pixel as it
     * does look ugly when zooming in.
     * 
     * 
     * @param string
     *            the String to be rendered
     * @param font
     *            the font
     * @param foreground
     *            the text's color
     * @param background
     *            the background color
     * @param useGCTransform true to use the Transform on the GC object.
     * false to rely on the homemade algorithm. Transform seems to 
     * not be supported in eclipse-3.2 except on windows.
     * It is working on macos-3.3M3.
     * @return an Image which must be disposed
     */
    public Image createRotatedImageOfString(Graphics g, String string,
            Font font, Color foreground, Color background,
            boolean useGCTransform) {
        Display display = Display.getCurrent();
        if (display == null) {
            SWT.error(SWT.ERROR_THREAD_INVALID_ACCESS);
        }

        FontData data = font.getFontData()[0];
        data.setHeight((int) (data.getHeight() * g.getAbsoluteScale()));
        Font zoomedFont = new Font(display, data);
        FontMetrics metrics = FigureUtilities.getFontMetrics(zoomedFont);
        Dimension strSize = FigureUtilities
                .getStringExtents(string, zoomedFont);

        Image srcImage = useGCTransform ?
            new Image(display, metrics.getHeight(), strSize.width) :
                new Image(display, strSize.width, metrics.getHeight());
        GC gc = new GC(srcImage);
        if (useGCTransform) {
            Transform transform = new Transform(display);
            transform.rotate(-90);
            gc.setTransform(transform);
        }
        gc.setFont(zoomedFont);
        gc.setForeground(foreground);
        gc.setBackground(background);
        gc.fillRectangle(gc.getClipping());
        gc.drawString(string, gc.getClipping().x, gc.getClipping().y
                //- metrics.getLeading()
                );
        gc.dispose();
        if (useGCTransform) {
            return srcImage;
        }
        Image rotated = ImageUtilities.createRotatedImage(srcImage);
        srcImage.dispose();
        return rotated;
    }

    @Override
    public Rectangle getTextBounds() {
        Rectangle rect = super.getTextBounds();
        
        if (!_isVertical) {
            return rect;
        }
        
        // let's move label start to parent's left side
        Rectangle bounds = new Rectangle(getParent().getBounds().x, rect.y,
                rect.height, rect.width);
        return bounds;
    }

    @Override
    protected Dimension calculateLabelSize(Dimension txtSize) {
        return new Dimension(Math.max(txtSize.height, 20), txtSize.width + 4);
    }
    
    /**
     * @param isVertical true when the label should in fact be vertical
     * false to keep the standard horizontal painting of the label.
     */
    public void setVertical(boolean isVertical) {
        if (isVertical != _isVertical) {
            _isVertical = isVertical;
            disposeImage();
            revalidate();
        }
    }
    
}
