import se.datadosen.jalbum.JAFilter;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.*;
import se.datadosen.util.*;
import java.awt.font.*;
import java.awt.geom.*;

/**
 * Title:        JAlbum text image filter
 * Company:      JAlbum AB
 * @author David Ekholm
 * @version 1.1
 */
public class TextFilter implements JAFilter, Cloneable {
    public static final String LEFT_ALIGNMENT = "left";
    public static final String CENTER_ALIGNMENT = "center";
    public static final String RIGHT_ALIGNMENT = "right";
    public static final String TOP_ALIGNMENT = "top";
    public static final String BOTTOM_ALIGNMENT = "bottom";

    private String text = "$comment";
    private Font font = new Font("SansSerif", Font.BOLD, 16);
    private Color color = new Color(0,0,0,100);
    private boolean antialias = true;
    private String align = LEFT_ALIGNMENT;
    private String valign = TOP_ALIGNMENT;
    private int margin = 5;
    private int offset = 0;
    private int voffset = 0;
    private float angle = 0;

    // Implements JAFilter
    public String getName() {
        return "Text filter";
    }

	// Implements JAFilter
    public String getDescription() {
        return "Overlay a text string on top of an image. The string may contain $variables";
    }

	// Implements JAFilter
    public BufferedImage filter(BufferedImage bi, Map vars) {
		Graphics2D g = bi.createGraphics();
        if (antialias)
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g.setFont(font);
        g.setColor(color);
        
        if (angle != 0) {
	        g.translate(bi.getWidth()/2, bi.getHeight()/2);
	        g.rotate(angle/180 * Math.PI);
	        g.translate(-bi.getWidth()/2, -bi.getHeight()/2);        	
        }

        Replacer r = new Replacer();
        fillReplacer(r, vars);
        String s = stripUnknownVariables(r.replace(text));
        FontRenderContext frc = g.getFontRenderContext();
        Rectangle2D rect = font.getStringBounds(s, frc);
        int width = bi.getWidth();
        int height = bi.getHeight();
        int x,y;
        if (align.equals(LEFT_ALIGNMENT))
            x = (int)rect.getX() + margin;
        else if (align.equals(RIGHT_ALIGNMENT))
            x = (int)(width-margin-rect.getWidth() - rect.getX());
        else // CENTER
            x = (int)((width-rect.getWidth())/2 -rect.getX());

        if (valign.equals(TOP_ALIGNMENT))
            y = (int)-rect.getY() + margin;
        else if (valign.equals(BOTTOM_ALIGNMENT))
            y = (int)(height-margin-rect.getHeight() - rect.getY());
        else // CENTER
            y = (int)((height-rect.getHeight())/2 -rect.getY());

        g.drawString(s, x+offset, y+voffset);
        return bi;
    }

    // Implements Cloneable
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public void setText(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }

    public void setFont(Font font) {
        this.font = font;
    }

    public Font getFont() {
        return font;
    }

    public void setSize(int newSize) {
        font = new Font(font.getName(), font.getStyle(), newSize);
    }
    public int getSize() {
        return font.getSize();
    }

    public void setAngle(float newAngle) {
        angle = newAngle;
    }
    public float getAngle() {
        return angle;
    }
    
    public void setFace(String fontFace) {
        font = new Font(fontFace, font.getStyle(), font.getSize());
    }
    public String getFace() {
        return font.getFamily();
    }

    public void setStyle(String fontStyle) {
        int style = Font.PLAIN;
        if (fontStyle.equalsIgnoreCase("bold")) style = Font.BOLD;
        else if (fontStyle.equalsIgnoreCase("italic")) style = Font.ITALIC;
        else if (fontStyle.equalsIgnoreCase("bolditalic")) style = Font.BOLD | Font.ITALIC;
        font = new Font(font.getName(), style, font.getSize());
    }
    public String getStyle() {
        switch (font.getStyle()) {
        case Font.BOLD : return "bold";
        case Font.ITALIC : return "italic";
        case Font.BOLD|Font.ITALIC: return "bolditalic";
        default: return "plain";
        }
    }

    public void setColor(Color color) {
        this.color = color;
    }

    public void setColor(String htmlColor) {
        this.color = Colors.getHTMLColor(htmlColor);
    }

    public Color getColor() {
        return color;
    }

    static Replacer fillReplacer(Replacer replacer, Map variables) {
        Iterator it = variables.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry e=(Map.Entry)it.next();
//            if (e.getValue() != null) // We have a safe Map now, so this works, but is slower as Maps are expanded but seldom printed
            if (e.getValue() != null && !(e.getValue() instanceof Map)) // We have circular references e.g. fileVariables
                replacer.add("$" + (String)e.getKey(), e.getValue().toString());
        }
        return replacer;
    }
    public void setMargin(int newMargin) {
        margin = newMargin;
    }
    public int getMargin() {
        return margin;
    }
    public void setAlign(String newAlign) {
        align = newAlign;
    }
    public String getAlign() {
        return align;
    }
    public void setValign(String newValign) {
        valign = newValign;
    }
    public String getValign() {
        return valign;
    }
    public void setAntialias(boolean newAntialias) {
        antialias = newAntialias;
    }
    public boolean isAntialias() {
        return antialias;
    }
    public void setOffset(int newOffset) {
        offset = newOffset;
    }
    public int getOffset() {
        return offset;
    }
    public void setVoffset(int newVoffset) {
        voffset = newVoffset;
    }
    public int getVoffset() {
        return voffset;
    }

    private String stripUnknownVariables(String s) {
		StringBuffer sb = new StringBuffer();
		char[] chars = s.toCharArray();
		for (int i=0; i<chars.length; i++) {
			if (chars[i] == '$') { // Skip past next space
				i++;
				while (i < chars.length && Character.isJavaIdentifierPart(chars[i])) i++;
			}
			else sb.append(chars[i]);
		}
		return sb.toString();
	}
}