/*
 * NewPolygon2D.java
 *
 * Copyright (C) 2002-2009 Alexei Drummond and Andrew Rambaut
 *
 * This file is part of BEAST.
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership and licensing.
 *
 * BEAST is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * BEAST 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with BEAST; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

package dr.geo;

import dr.xml.*;
import org.jdom.Element;

import java.awt.geom.*;
import java.util.List;
import java.util.StringTokenizer;

/**
 * @author Marc A. Suchard
 * @author Philippe Lemey
 */

public class NewPolygon2D {

    public static final String POLYGON = "polygon";
    public static final String CLOSED = "closed";
    public static final String FILL_VALUE = "fillValue";

    public NewPolygon2D(GeneralPath path) {
        this.path = path;
    }

    public NewPolygon2D(Element e) {

        List<Element> children = e.getChildren();
//        for (int a = 0; a < children.size(); a++) {
        for (Element childElement : children) {
//            Element childElement = (Element) children.get(a);
            if (childElement.getName().equals(KMLCoordinates.COORDINATES)) {

                String value = childElement.getTextTrim();
                StringTokenizer st1 = new StringTokenizer(value, "\n");
                int count = st1.countTokens();  //System.out.println(count);

//                point2Ds = new LinkedList<Point2D>();
                this.path = new GeneralPath();

                for (int i = 0; i < count; i++) {
                    String line = st1.nextToken();
                    StringTokenizer st2 = new StringTokenizer(line, ",");
                    if (st2.countTokens() != 3)
                        throw new IllegalArgumentException("All KML coordinates must contain (X,Y,Z) values.  Three dimensions not found in element '" + line + "'");
                    final float x = Float.valueOf(st2.nextToken());
                    final float y = Float.valueOf(st2.nextToken());
                    if (i == 0) {
                        path.moveTo(x, y);
                    } else {
                        path.lineTo(x, y);
                    }
//                    point2Ds.add(new Point2D.Double(x, y));
                }
//                length = point2Ds.size() - 1;
                break;

            }
        }
    }

    public NewPolygon2D() {
        path = new GeneralPath();
    }

    public void moveTo(Point2D pt) {
        path.moveTo((float) pt.getX(), (float) pt.getY());
    }

    public void lineTo(Point2D pt) {
        path.lineTo((float) pt.getX(), (float) pt.getY());
    }

    public boolean contains(Point2D pt) {
        return path.contains(pt);
    }

    public void closePath() {
        path.closePath();
    }

    public void setFillValue(double value) {
        fillValue = value;
    }

    public double getFillValue() {
        return fillValue;
    }

    public NewPolygon2D clip(Rectangle2D boundingBox) {
        Area thisArea = new Area(path);
        thisArea.intersect(new Area(boundingBox));
        PathIterator iterator = thisArea.getPathIterator(null);
        double[] v = new double[2];
        while (!iterator.isDone()) {
            int type = iterator.currentSegment(v);
            System.err.println(":" + v[0] + v[1] + "\n");
            iterator.next();
        }
        System.exit(-1);

        GeneralPath path = new GeneralPath(thisArea);
        path.closePath();
        NewPolygon2D newPolygon = new NewPolygon2D(path);
        newPolygon.setFillValue(this.getFillValue());
        return newPolygon;
    }

    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("polygon(");
        sb.append(fillValue);
        sb.append(")[\n");
        PathIterator iterator = path.getPathIterator(null);
        float[] values = new float[2];
        while (!iterator.isDone()) {
            int type = iterator.currentSegment(values);
            Point2D pt = new Point2D.Double(values[0], values[1]);
            sb.append("\t");
            sb.append(pt);
            sb.append("\n");
            iterator.next();
        }
//        sb.append(iterator);
//        for(Point2D pt : point2Ds) {
//            sb.append("\t");
//            sb.append(pt);
//            sb.append("\n");
//        }
//        sb.append(path.toString());
        sb.append("]");
        return sb.toString();
    }

    public static XMLObjectParser PARSER = new AbstractXMLObjectParser() {

        public String getParserName() {
            return POLYGON;
        }

        public Object parseXMLObject(XMLObject xo) throws XMLParseException {

            KMLCoordinates coordinates = (KMLCoordinates) xo.getChild(KMLCoordinates.class);
            boolean closed = xo.getAttribute(CLOSED, false);

            if ((!closed && coordinates.length < 3) ||
                    (closed && coordinates.length < 4))
                throw new XMLParseException("Insufficient points in polygon '" + xo.getId() + "' to define a polygon in 2D");

            NewPolygon2D polygon = new NewPolygon2D();
            polygon.moveTo(new Point2D.Double(coordinates.x[0], coordinates.y[0]));
            int length = coordinates.length;
            if (closed)
                length--;
            for (int i = 1; i < length; i++)
                polygon.lineTo(new Point2D.Double(coordinates.x[i], coordinates.y[i]));
            polygon.lineTo(new Point2D.Double(coordinates.x[0], coordinates.y[0]));
//            polygon.closePath();

            polygon.setFillValue(xo.getAttribute(FILL_VALUE, 0.0));

            return polygon;
        }

        //************************************************************************
        // AbstractXMLObjectParser implementation
        //************************************************************************

        public String getParserDescription() {
            return "This element represents a polygon.";
        }

        public Class getReturnType() {
            return Polygon2D.class;
        }

        public XMLSyntaxRule[] getSyntaxRules() {
            return rules;
        }

        private XMLSyntaxRule[] rules = new XMLSyntaxRule[]{
                new ElementRule(KMLCoordinates.class),
                AttributeRule.newBooleanRule(CLOSED, true),
                AttributeRule.newDoubleRule(FILL_VALUE, true),
        };
    };

    public static void main(String[] args) {
        NewPolygon2D polygon = new NewPolygon2D();
        polygon.moveTo(new Point2D.Double(-10, -10));
        polygon.lineTo(new Point2D.Double(-10, 50));
        polygon.lineTo(new Point2D.Double(10, 50));
        polygon.lineTo(new Point2D.Double(10, -10));
        polygon.lineTo(new Point2D.Double(-10, -10));
//        polygon.closePath();
        System.out.println(polygon);
        System.out.println("");
//        System.exit(-1);

        Point2D pt = new Point2D.Double(0, 0);
        System.out.println("polygon contains " + pt + ": " + polygon.contains(pt));
        pt = new Point2D.Double(100, 100);
        System.out.println("polygon contains " + pt + ": " + polygon.contains(pt));
        System.out.println("");

        Rectangle2D boundingBox = new Rectangle2D.Double(0, 0, 100, 100);  // defines lower-left corner and width/height
        System.out.println(boundingBox);
        NewPolygon2D myClip = polygon.clip(boundingBox);
        System.out.println(myClip);

    }

    private double fillValue;
    private GeneralPath path;
}