/*
 * Copyright 2010-2015 Institut Pasteur.
 * 
 * This file is part of Icy.
 * 
 * Icy is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Icy 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
 */
package plugins.kernel.roi.roi2d;

import icy.painter.Anchor2D;
import icy.painter.LineAnchor2D;
import icy.resource.ResourceUtil;
import icy.type.geom.Polygon2D;
import icy.type.point.Point5D;
import icy.util.XMLUtil;

import java.awt.Color;
import java.awt.Polygon;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;

import org.w3c.dom.Element;
import org.w3c.dom.Node;

/**
 * ROI 2D polygon class.
 * 
 * @author Stephane
 */
public class ROI2DPolygon extends ROI2DShape
{
    protected class ROI2DPolygonAnchor2D extends LineAnchor2D
    {
        public ROI2DPolygonAnchor2D(Point2D position, Color color, Color selectedColor)
        {
            super(position, color, selectedColor);
        }

        @Override
        protected Anchor2D getPreviousPoint()
        {
            final int ind = controlPoints.indexOf(this);

            if (ind == 0)
            {
                if (controlPoints.size() > 1)
                    return controlPoints.get(1);

                return null;
            }

            if (ind != -1)
                return controlPoints.get(ind - 1);

            return null;
        }
    }

    public static final String ID_POINTS = "points";
    public static final String ID_POINT = "point";

    /**
     * @deprecated
     */
    @Deprecated
    public ROI2DPolygon(Point2D pt, boolean cm)
    {
        this(pt);
    }

    public ROI2DPolygon(Point2D pt)
    {
        super(new Polygon2D());

        final Anchor2D point = createAnchor(pt);
        point.setSelected(true);
        addPoint(point);

        // set icon (default name is defined by getDefaultName())
        setIcon(ResourceUtil.ICON_ROI_POLYGON);
    }

    /**
     * Generic constructor for interactive mode
     */
    public ROI2DPolygon(Point5D pt)
    {
        this(pt.toPoint2D());
        // getOverlay().setMousePos(pt);
    }

    public ROI2DPolygon(List<Point2D> points)
    {
        this(new Point2D.Double());

        setPoints(points);
        unselectAllPoints();
    }

    /**
     * @deprecated Better to use {@link #ROI2DPolygon(Polygon2D)} instead to have double point precision
     */
    @Deprecated
    public ROI2DPolygon(Polygon polygon)
    {
        this(new Point2D.Double());

        setPolygon(polygon);
        unselectAllPoints();
    }

    public ROI2DPolygon(Polygon2D polygon)
    {
        this(new Point2D.Double());

        setPolygon2D(polygon);
        unselectAllPoints();
    }

    public ROI2DPolygon()
    {
        this(new Point2D.Double());
    }

    @Override
    public String getDefaultName()
    {
        return "Polygon2D";
    }

    @Override
    protected Anchor2D createAnchor(Point2D pos)
    {
        return new ROI2DPolygonAnchor2D(pos, getColor(), getFocusedColor());
    }

    /**
     * @deprecated Use {@link #getPolygon2D()} instead
     */
    @Deprecated
    protected Path2D getPath()
    {
        return new Path2D.Double(shape);
    }

    public void setPoints(List<Point2D> pts)
    {
        beginUpdate();
        try
        {
            removeAllPoint();
            for (Point2D pt : pts)
                addNewPoint(pt, false);
        }
        finally
        {
            endUpdate();
        }
    }

    /**
     * @deprecated Use {@link #setPoints(List)} instead.
     */
    @Deprecated
    public void setPoints(ArrayList<Point2D> pts)
    {
        setPoints((List<Point2D>) pts);
    }

    public Polygon2D getPolygon2D()
    {
        return (Polygon2D) shape;
    }

    public void setPolygon2D(Polygon2D polygon2D)
    {
        beginUpdate();
        try
        {
            removeAllPoint();
            for (int i = 0; i < polygon2D.npoints; i++)
                addNewPoint(new Point2D.Double(polygon2D.xpoints[i], polygon2D.ypoints[i]), false);
        }
        finally
        {
            endUpdate();
        }
    }

    public Polygon getPolygon()
    {
        return getPolygon2D().getPolygon();
    }

    public void setPolygon(Polygon polygon)
    {
        beginUpdate();
        try
        {
            removeAllPoint();
            for (int i = 0; i < polygon.npoints; i++)
                addNewPoint(new Point2D.Double(polygon.xpoints[i], polygon.ypoints[i]), false);
        }
        finally
        {
            endUpdate();
        }
    }

    @Override
    protected void updateShape()
    {
        final int len;
        final double[] ptsX;
        final double[] ptsY;

        synchronized (controlPoints)
        {
            len = controlPoints.size();
            ptsX = new double[len];
            ptsY = new double[len];

            for (int i = 0; i < len; i++)
            {
                final Anchor2D pt = controlPoints.get(i);

                ptsX[i] = pt.getX();
                ptsY[i] = pt.getY();
            }
        }

        final Polygon2D polygon2d = getPolygon2D();

        // we can have a problem here if we try to redraw while we are modifying the polygon points
        synchronized (polygon2d)
        {
            polygon2d.npoints = len;
            polygon2d.xpoints = ptsX;
            polygon2d.ypoints = ptsY;
            polygon2d.calculatePath();
        }

        // call super method after shape has been updated
        super.updateShape();
    }

    @Override
    public boolean loadFromXML(Node node)
    {
        beginUpdate();
        try
        {
            if (!super.loadFromXML(node))
                return false;

            removeAllPoint();

            final List<Node> nodesPoint = XMLUtil.getChildren(XMLUtil.getElement(node, ID_POINTS), ID_POINT);
            if (nodesPoint != null)
            {
                for (Node n : nodesPoint)
                {
                    final Anchor2D pt = createAnchor(new Point2D.Double());
                    pt.loadPositionFromXML(n);
                    addPoint(pt);
                }
            }
        }
        finally
        {
            endUpdate();
        }

        return true;
    }

    @Override
    public boolean saveToXML(Node node)
    {
        if (!super.saveToXML(node))
            return false;

        final Element nodePoints = XMLUtil.setElement(node, ID_POINTS);
        synchronized (controlPoints)
        {
            for (Anchor2D pt : controlPoints)
                pt.savePositionToXML(XMLUtil.addElement(nodePoints, ID_POINT));
        }
        return true;
    }
}