package kiyut.sketsa.api.symbollibrary;

import java.awt.Color;
import java.awt.Component;
import java.awt.Point;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.PathIterator;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import javax.swing.Icon;
import javax.swing.JOptionPane;
import kiyut.sketsa.canvas.VectorCanvas;
import kiyut.sketsa.undo.DOMUndoManager;
import kiyut.sketsa.util.ColorConvertion;
import kiyut.sketsa.util.DOMUtilities;
import kiyut.sketsa.util.SVGConstants;
import org.w3c.dom.Element;
import org.w3c.dom.svg.SVGDocument;
import org.w3c.dom.svg.SVGElement;
import org.w3c.dom.svg.SVGPathElement;
import org.w3c.dom.svg.SVGPathSegClosePath;
import org.w3c.dom.svg.SVGPathSegCurvetoCubicAbs;
import org.w3c.dom.svg.SVGPathSegLinetoAbs;
import org.w3c.dom.svg.SVGPathSegMovetoAbs;

/** Simple Shape Symbol.
 * Implementator only need to overide: <br/>
 * - getShape() <br/>
 * - icon by default it return null <br/>
 * - strokeColor Optional by default it return null <br/>
 * - fillColor Optional by default it return black <br/>
 * - tooltipText Optional by default it return the super.getTooltipText <br/>
 *
 * Note: Undo and Redo text is from getTooltipText
 *
 * @author Kiyut
 */
public abstract class AbstractShapeSymbol extends AbstractSymbol {
    /** Icon */
    protected Icon icon;
    
    /** Tooltip Text */
    protected String tooltipText;
       
    /** strokeColor */
    protected Color strokeColor;
    
    /** fillColor */
    protected Color fillColor;
    
    public AbstractShapeSymbol() {
        strokeColor = null;
        fillColor = new Color(0,0,0);
        tooltipText = super.getTooltipText();
    }
    
    /** This method returns the content of tooltipText.
     * {@inheritDoc} 
     */
    public String getTooltipText() {
        return tooltipText;
    }
    
    /** {@inheritDoc} */
    public Icon getIcon() {
        return icon;
    }
    
    /** Return Shape that will used to generate SVGElement.
     *@return Shape
     */
    protected abstract Shape getShape();
    
    /** {@inheritDoc} */
    public void insertSymbol(VectorCanvas canvas, Point location) {
        SVGElement elt = createSVGElement(canvas,location);
        SVGDocument doc = canvas.getSVGDocument();
        Element svgRoot = doc.getDocumentElement();
        
        DOMUndoManager undoManager = canvas.getUndoManager();
        undoManager.start("Add " + getTooltipText());
        try {
            svgRoot.appendChild(elt);
        } finally {
            undoManager.end();
        }
        
        List<SVGElement> newList = new ArrayList<SVGElement>();
        newList.add(elt);
        
        canvas.getCanvasSelection().setSelectionList(newList);
        canvas.refresh();
    }
    
    /** Create SVGElement, base on the value returned by getShape
     * @param canvas VectorCanvas
     * @param location The location where the insertion should take place
     * @return SVGElement
     * @see #getShape
     */
    protected SVGElement createSVGElement(VectorCanvas canvas, Point location) {
        SVGDocument doc = canvas.getSVGDocument();
        
        float[] coords = new float[6];
        coords[0] = (float)location.getX();
        coords[1] = (float)location.getY();
        
        AffineTransform at = canvas.getTransform(true);
        try {
            at = at.createInverse();
        } catch (NoninvertibleTransformException e) {}
        at.transform(coords,0,coords,0,2);
        
        float cx = (float)coords[0];
        float cy = (float)coords[1];
        
        /* Get shape */
        Shape shape = getShape();
        
        /* Set shape the location into Center Mouse Coord */
        Rectangle2D bounds = shape.getBounds2D();
        double tx = cx - bounds.getCenterX();
        double ty = cy - bounds.getCenterY();
        AffineTransform coordAt = AffineTransform.getTranslateInstance(tx, ty);
        PathIterator pi = shape.getPathIterator(coordAt);
        
        SVGPathElement elt = (SVGPathElement)doc.createElementNS(SVGConstants.SVG_NAMESPACE_URI, SVGConstants.SVG_PATH_TAG);
        
        // adding the path seg
        while (!pi.isDone()) {
            int segment = pi.currentSegment(coords);
            if (segment == PathIterator.SEG_MOVETO) {
                SVGPathSegMovetoAbs svgPathSeg = elt.createSVGPathSegMovetoAbs(coords[0],coords[1]);
                elt.getPathSegList().appendItem(svgPathSeg);
            } else if (segment == PathIterator.SEG_CLOSE) {
                SVGPathSegClosePath svgPathSeg = elt.createSVGPathSegClosePath();
                elt.getPathSegList().appendItem(svgPathSeg);
            } else if (segment == PathIterator.SEG_LINETO) {
                SVGPathSegLinetoAbs svgPathSeg = elt.createSVGPathSegLinetoAbs(coords[0],coords[1]);
                elt.getPathSegList().appendItem(svgPathSeg);
            } else if (segment == PathIterator.SEG_CUBICTO) {
                SVGPathSegCurvetoCubicAbs svgPathSeg = elt.createSVGPathSegCurvetoCubicAbs(coords[4],coords[5],coords[0],coords[1],coords[2],coords[3]);
                elt.getPathSegList().appendItem(svgPathSeg);
            }
            
            pi.next();
        }
        
        if (strokeColor == null) {
            DOMUtilities.updateProperty(elt, null, SVGConstants.SVG_STROKE_ATTRIBUTE,SVGConstants.SVG_NONE_VALUE);
        } else {
            DOMUtilities.updateProperty(elt, null, SVGConstants.SVG_STROKE_ATTRIBUTE,ColorConvertion.toHexString(strokeColor));
        }
        
        if (fillColor == null) {
            DOMUtilities.updateProperty(elt, null, SVGConstants.SVG_FILL_ATTRIBUTE,SVGConstants.SVG_NONE_VALUE);
        } else {
            DOMUtilities.updateProperty(elt, null, SVGConstants.SVG_FILL_ATTRIBUTE,ColorConvertion.toHexString(fillColor));
        }
        
        return elt;
    }
    
    /** This will display DefaultSymbolOptionPane
     * {@inheritDoc} 
     */
    public void showOptionDialog(Component parentComponent) {
        DefaultSymbolOptionPane option = new DefaultSymbolOptionPane();
        option.setValue(strokeColor,fillColor);
        boolean valid = false;
        while (!valid) {
            int choice = JOptionPane.showConfirmDialog(parentComponent,option,getTooltipText(),JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
            if (choice == JOptionPane.OK_OPTION) {
                Color color1 = option.getStrokeColor();
                Color color2 = option.getFillColor();
                if (color1 == null && color2 == null) {
                    // show message both can't none
                    ResourceBundle rb = option.getResourceBundle();
                    JOptionPane.showMessageDialog(parentComponent,rb.getString("MSG_StrokeNoneFillNone.Text"),rb.getString("MSG_StrokeNoneFillNone.Title"),JOptionPane.ERROR_MESSAGE);
                } else {
                    strokeColor = color1;
                    fillColor = color2;
                    valid = true;
                }
            } else {
                valid = true;
            }
        }
    }
}
