/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.meteoinfo.chart;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.meteoinfo.common.Extent;
import org.meteoinfo.common.XAlign;
import org.meteoinfo.common.YAlign;
import org.meteoinfo.geo.drawing.Draw;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.meteoinfo.geometry.shape.Shape;
import org.meteoinfo.geometry.shape.ShapeTypes;

/**
 *
 * @author yaqiang
 */
public class ChartText extends Shape {

    // <editor-fold desc="Variables">
    protected double x;
    protected double y;
    protected Font font;
    protected List<String> text;
    protected Color color;
    protected int lineSpace;
    protected CoordinateType coordinates;
    protected Color background;
    protected boolean drawBackground;
    protected boolean drawNeatline;
    protected Color neatLineColor;
    protected float neatLineSize;
    protected float gap;
    protected float angle;
    protected XAlign xAlign;
    protected YAlign yAlign;
    protected boolean useExternalFont;
    protected double xShift;
    protected double yShift;

    // </editor-fold>    
    // <editor-fold desc="Constructor">
    /**
     * Constructor
     */
    public ChartText() {
        font = new Font("Arial", Font.PLAIN, 14);
        color = Color.black;
        lineSpace = 5;
        coordinates = CoordinateType.DATA;
        this.background = Color.white;
        this.drawBackground = false;
        this.drawNeatline = false;
        this.neatLineColor = Color.black;
        this.neatLineSize = 1.0f;
        this.gap = 3.0f;
        this.angle = 0.0f;
        this.xAlign = XAlign.LEFT;
        this.yAlign = YAlign.BOTTOM;
        this.useExternalFont = false;
        this.xShift = 0;
        this.yShift = 0;
    }

    /**
     * Constructor
     *
     * @param text Text
     */
    public ChartText(String text) {
        this();
        this.text = new ArrayList<>();
        String[] lines = text.split("\n");
        this.text.addAll(Arrays.asList(lines));
    }

    /**
     * Constructor
     *
     * @param text Text
     */
    public ChartText(List<String> text) {
        this();
        this.text = text;
    }

    /**
     * Constructor
     *
     * @param text Text
     * @param font Font
     */
    public ChartText(String text, Font font) {
        this();
        this.text = new ArrayList<>();
        String[] lines = text.split("\n");
        this.text.addAll(Arrays.asList(lines));
        this.font = font;
    }

    /**
     * Constructor
     *
     * @param text Text
     * @param font Font
     * @param color Color
     */
    public ChartText(String text, Font font, Color color) {
        this();
        this.text = new ArrayList<>();
        String[] lines = text.split("\n");
        this.text.addAll(Arrays.asList(lines));
        this.font = font;
        this.color = color;
    }

    /**
     * Constructor
     *
     * @param text Text
     * @param font Font
     */
    public ChartText(List<String> text, Font font) {
        this();
        this.text = text;
        this.font = font;
    }

    /**
     * Constructor
     *
     * @param text Text
     * @param font Font
     * @param color Color
     */
    public ChartText(List<String> text, Font font, Color color) {
        this();
        this.text = text;
        this.font = font;
        this.color = color;
    }
    // </editor-fold>
    // <editor-fold desc="Get Set Methods">

    /**
     * Get text
     *
     * @return Text
     */
    public String getText() {
        return text.get(0);
    }

    /**
     * Set text
     *
     * @param value Text
     */
    public void setText(String value) {
        this.text = Arrays.asList(value.split("\n"));
    }

    /**
     * Get texts
     *
     * @return Text list
     */
    public List<String> getTexts() {
        return text;
    }

    /**
     * Set texts
     *
     * @param value Text list
     */
    public void setTexts(List<String> value) {
        text = value;
    }

    /**
     * Get font
     *
     * @return Font
     */
    public Font getFont() {
        return font;
    }

    /**
     * Set font
     *
     * @param value Font
     */
    public void setFont(Font value) {
        font = value;
    }

    /**
     * Get title color
     *
     * @return Title color
     */
    public Color getColor() {
        return color;
    }

    /**
     * Set title color
     *
     * @param value Title color
     */
    public void setColor(Color value) {
        this.color = value;
    }

    /**
     * Get x
     *
     * @return X
     */
    public double getX() {
        return this.x;
    }

    /**
     * Set x
     *
     * @param value X
     */
    public void setX(double value) {
        this.x = value;
    }

    /**
     * Get y
     *
     * @return Y
     */
    public double getY() {
        return this.y;
    }

    /**
     * Set y
     *
     * @param value Y
     */
    public void setY(double value) {
        this.y = value;
    }

    /**
     * Get line space
     *
     * @return Line space
     */
    public int getLineSpace() {
        return this.lineSpace;
    }

    /**
     * Set line space
     *
     * @param value Line space
     */
    public void setLineSpace(int value) {
        this.lineSpace = value;
    }

    /**
     * Get coordinates
     *
     * @return Coordinates
     */
    public CoordinateType getCoordinates() {
        return this.coordinates;
    }

    /**
     * Set coordinates
     *
     * @param value Coordinates
     */
    public void setCoordinates(CoordinateType value) {
        this.coordinates = value;
    }

    /**
     * Set coordinates
     *
     * @param value Coordinates
     */
    public void setCoordinates(String value) {
        switch (value) {
            case "axes":
                this.coordinates = CoordinateType.AXES;
                break;
            case "figure":
                this.coordinates = CoordinateType.FIGURE;
                break;
            case "data":
                this.coordinates = CoordinateType.DATA;
                break;
            case "inches":
                this.coordinates = CoordinateType.INCHES;
                break;
        }
    }

    /**
     * Get background color
     *
     * @return Background color
     */
    public Color getBackground() {
        return this.background;
    }

    /**
     * Set background color
     *
     * @param value Background color
     */
    public void setBackground(Color value) {
        this.background = value;
    }

    /**
     * Get if is fill background
     *
     * @return Boolean
     */
    public boolean isFill() {
        return this.drawBackground;
    }

    /**
     * Set fill background or not
     *
     * @param value Boolean
     */
    public void setFill(boolean value) {
        this.drawBackground = value;
    }

    /**
     * Get draw neatline or not
     *
     * @return Boolean
     */
    public boolean isDrawNeatline() {
        return this.drawNeatline;
    }

    /**
     * Set draw neatline or not
     *
     * @param value Boolean
     */
    public void setDrawNeatline(boolean value) {
        this.drawNeatline = value;
    }

    /**
     * Get neatline color
     *
     * @return Neatline color
     */
    public Color getNeatlineColor() {
        return this.neatLineColor;
    }

    /**
     * Set neatline color
     *
     * @param value Neatline color
     */
    public void setNeatlineColor(Color value) {
        this.neatLineColor = value;
    }

    /**
     * Get neatline size
     *
     * @return Neatline size
     */
    public float getNeatlineSize() {
        return this.neatLineSize;
    }

    /**
     * Set neatline size
     *
     * @param value Neatline size
     */
    public void setNeatlineSize(float value) {
        this.neatLineSize = value;
    }

    /**
     * Get gap
     *
     * @return Gap
     */
    public float getGap() {
        return this.gap;
    }

    /**
     * Set gap
     *
     * @param value Gap
     */
    public void setGap(float value) {
        this.gap = value;
    }

    /**
     * Get angle
     *
     * @return Angle
     */
    public float getAngle() {
        return this.angle;
    }

    /**
     * Set angle
     *
     * @param value Angle
     */
    public void setAngle(float value) {
        this.angle = value;
    }

    /**
     * Get x align
     *
     * @return X align
     */
    public XAlign getXAlign() {
        return this.xAlign;
    }

    /**
     * Set x align
     *
     * @param value X align
     */
    public void setXAlign(XAlign value) {
        this.xAlign = value;
    }

    /**
     * Set x align
     *
     * @param value X align string
     */
    public void setXAlign(String value) {
        this.xAlign = XAlign.valueOf(value.toUpperCase());
    }

    /**
     * Get y align
     *
     * @return Y align
     */
    public YAlign getYAlign() {
        return this.yAlign;
    }

    /**
     * Set y align
     *
     * @param value Y align
     */
    public void setYAlign(YAlign value) {
        this.yAlign = value;
    }

    /**
     * Set y align
     *
     * @param value Y align string
     */
    public void setYAlign(String value) {
        this.yAlign = YAlign.valueOf(value.toUpperCase());
    }

    /**
     * Get if use external font - only for LaTeX string
     *
     * @return Boolean
     */
    public boolean isUseExternalFont() {
        return this.useExternalFont;
    }

    /**
     * Set if use external font - only for LaTeX string
     *
     * @param value Boolean
     */
    public void setUseExternalFont(boolean value) {
        this.useExternalFont = value;
    }
    
    /**
     * Get x shift
     * @return X shift
     */
    public double getXShift() {
        return this.xShift;
    }
    
    /**
     * Set x shift
     * @param value X shift 
     */
    public void setXShift(double value) {
        this.xShift = value;
    }
     
    /**
     * Get y shift
     * @return Y shift
     */
    public double getYShift() {
        return this.yShift;
    }
    
    /**
     * Set y shift
     * @param value Y shift 
     */
    public void setYShift(double value) {
        this.yShift = value;
    }

    // </editor-fold>
    // <editor-fold desc="Methods">
    /**
     * Add text in new line
     *
     * @param value The text string
     */
    public void addText(String value) {
        this.text.add(value);
    }

    @Override
    public ShapeTypes getShapeType() {
        return ShapeTypes.TEXT;
    }

    /**
     * Get text line number
     *
     * @return Text line number
     */
    public int getLineNum() {
        return this.text.size();
    }

    /**
     * Get text dimension with angle
     *
     * @param g Graphics2D
     * @return Dimension
     */
    public Dimension getTrueDimension(Graphics2D g) {
        Dimension dim = getDimension(g);
        if (this.angle != 0) {
            int width = dim.width;
            int height = dim.height;
            int temp;
            if (angle == 90 || angle == -90) {
                temp = width;
                width = height;
                height = temp;
            } else {
                width = (int) ((width * Math.cos(Math.toRadians(angle))) + (height * Math.sin(Math.toRadians(angle))));
                height = (int) ((width * Math.sin(Math.toRadians(angle))) + (height * Math.cos(Math.toRadians(angle))));
            }
            return new Dimension(width, height);
        } else {
            return dim;
        }
    }

    /**
     * Get text dimension
     *
     * @param g Graphics2D
     * @return Dimension
     */
    public Dimension getDimension(Graphics2D g) {
        g.setFont(font);
        int width = 0, height = 0;
        for (String line : this.text) {
            Dimension dim = Draw.getStringDimension(line, g);
            if (width < dim.width) {
                width = dim.width;
            }
            height += dim.height + this.lineSpace;
        }
        height -= this.lineSpace;

        return new Dimension(width, height);
    }

    /**
     * To geometry method
     *
     * @param factory GeometryFactory
     * @return Geometry
     */
    @Override
    public Geometry toGeometry(GeometryFactory factory) {
        return null;
    }

    /**
     * Set point
     *
     * @param x X
     * @param y Y
     */
    public void setPoint(double x, double y) {
        this.x = x;
        this.y = y;
        Extent aExtent = new Extent();
        aExtent.minX = x;
        aExtent.maxX = x;
        aExtent.minY = y;
        aExtent.maxY = y;
        this.setExtent(aExtent);
    }

    /**
     * To string
     *
     * @return String
     */
    @Override
    public String toString() {
        if (this.text.size() == 1) {
            return this.text.get(0);
        } else {
            String r = "";
            for (int i = 0; i < this.text.size(); i++) {
                if (i == 0) {
                    r = this.text.get(i);
                } else {
                    r = r + "\n" + this.text.get(i);
                }
            }
            return r;
        }
    }

    /**
     * Draw text
     *
     * @param g Graphics2D
     * @param x X
     * @param y Y
     */
    public void draw(Graphics2D g, float x, float y) {
        Dimension dim = this.getDimension(g);
        x += this.xShift;
        y += this.yShift;

        AffineTransform tempTrans = g.getTransform();
        if (this.angle != 0) {
            //AffineTransform myTrans = new AffineTransform();
            AffineTransform myTrans = (AffineTransform) tempTrans.clone();
            myTrans.translate(x, y);
            //myTrans.translate(tempTrans.getTranslateX() + x, tempTrans.getTranslateY() + y);
            myTrans.rotate(-angle * Math.PI / 180);
            g.setTransform(myTrans);
            x = 0;
            y = 0;
        }

        Rectangle.Double rect = new Rectangle.Double(x, y - dim.getHeight(), dim.getWidth(), dim.getHeight());
        rect.setRect(rect.x - gap, rect.y - gap, rect.width + gap * 2,
                rect.height + gap * 2);
        if (this.drawBackground) {
            g.setColor(this.background);
            g.fill(rect);
        }
        if (this.drawNeatline) {
            g.setColor(this.neatLineColor);
            Stroke oldStroke = g.getStroke();
            g.setStroke(new BasicStroke(neatLineSize));
            g.draw(rect);
            g.setStroke(oldStroke);
        }

        g.setColor(this.color);
        g.setFont(font);
        switch (this.yAlign) {
            case BOTTOM:
                y = y - dim.height;
                break;
            case CENTER:
                y = y - dim.height * 0.5f;
                break;
        }

        for (String str : this.text) {
            dim = Draw.getStringDimension(str, g);
            Draw.drawString(g, x, y, str, xAlign, YAlign.TOP, useExternalFont);
            y += dim.height;
            y += this.lineSpace;
        }

        if (this.angle != 0) {
            g.setTransform(tempTrans);
        }
    }

    /**
     * Clone
     *
     * @return Cloned object
     */
    @Override
    public Object clone() {
        ChartText ct = new ChartText();
        ct.angle = this.angle;
        ct.background = this.background;
        ct.color = this.color;
        ct.coordinates = this.coordinates;
        ct.drawBackground = this.drawBackground;
        ct.drawNeatline = this.drawNeatline;
        ct.font = this.font;
        ct.gap = this.gap;
        ct.lineSpace = this.lineSpace;
        ct.neatLineColor = this.neatLineColor;
        ct.neatLineSize = this.neatLineSize;
        ct.text = this.text;
        ct.useExternalFont = this.useExternalFont;
        ct.x = this.x;
        ct.xAlign = this.xAlign;
        ct.y = this.y;
        ct.yAlign = this.yAlign;
        ct.xShift = this.xShift;
        ct.yShift = this.yShift;

        return ct;
    }
    // </editor-fold>
}
