/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

package netscape.plugin.composer.mapedit;


import netscape.plugin.composer.io.Tag;
import java.awt.*;
import java.util.*;
import java.net.URL;


class PolyArea extends Area {

  /** Should be given a closed polygon, first point is the same as last. */
  private PolyArea(Polygon p,boolean compl,String u) {
      super(u);
      polygon = p;
      isComplete = compl;
      if (!(p.xpoints[p.npoints - 1] == p.xpoints[0] &&
                      p.ypoints[p.npoints - 1] == p.ypoints[0]))
        Debug.println("Internal error: invalid polygon.");
  }

  public Rectangle boundingBox() {
    if (!completed()) {
      Debug.assert(false,"rect not completed");
      return null;
    }

    // upper-left and lower-right points.
    Point ul = new Point(Integer.MAX_VALUE,Integer.MAX_VALUE);
    Point lr = new Point(Integer.MIN_VALUE,Integer.MIN_VALUE);
    for (int n = 0; n < polygon.npoints; n++) {
      ul.x = Math.min(ul.x,polygon.xpoints[n]);
      ul.y = Math.min(ul.y,polygon.ypoints[n]);
      lr.x = Math.max(lr.x,polygon.xpoints[n]);
      lr.y = Math.max(lr.y,polygon.ypoints[n]);
    }

    return new Rectangle(ul.x,ul.y,lr.x - ul.x + 1,lr.y - ul.y + 1);
  }


  static PolyArea firstMouseDown(Point pImage) {
    Polygon p = new Polygon();
    // Initial point.
    p.addPoint(pImage.x,pImage.y);
    // New moving point.
    p.addPoint(pImage.x,pImage.y);
    // Final point is same as initial point.
    p.addPoint(pImage.x,pImage.y);

    // There are always a least three points, i.e. two sides.
    // (p.npoints - 2) is the floating one.

    // not completed
    return new PolyArea(p,false,"");
  }

  void mouseUp(Point pImage,Rectangle clp) {
  }

  void mouseDown(Point pImage,Rectangle clp) {
    if (completed())
      return;

    update(pImage);

    // Duplicate the moving point and add another point.
    polygon.xpoints[polygon.npoints - 1] = polygon.xpoints[polygon.npoints - 2];
    polygon.ypoints[polygon.npoints - 1] = polygon.ypoints[polygon.npoints - 2];
    // New point is initial point to keep polygon closed.
    polygon.addPoint(polygon.xpoints[0],polygon.ypoints[0]);
  }

  void mouseMoved(Point pImage,Rectangle clp) {
    if (completed())
      return;

    update(pImage);
  }

  void mouseDragged(Point pImage,Rectangle clp) {
    if (completed())
      return;

    update(pImage);
  }

  private void update(Point pImage) {
    int n = polygon.npoints - 2;
    if (n < 1) {
      Debug.println("problem with polygon");
    }

    polygon.xpoints[n] = pImage.x;
    polygon.ypoints[n] = pImage.y;
  }

  boolean forceCompletion() {
    if (polygon.npoints > 3) {
      // At least 3 sides to polygon.
      isComplete = true;
      return true;
    }
    else {
      return false;
    }
  }

  boolean completed() {return isComplete;}

  boolean containsPoint(int x,int y) {
    Debug.println("Check point " + x + "," + y);
    Debug.println("polygon " + polygon);

    return polygon.inside(x,y);
  //  return polygon.containsPoint(x,y);
  }

  void draw(Graphics g,Dimension off,boolean selected) {
      drawPolygon(polygon,g,off,selected);
  }

  private static void drawPolygon(Polygon p,Graphics g,Dimension off,boolean selected) {
      // Four polygons, each shifted one pixel in a diagonal,
      // in the color "outer".
      g.setColor(getOuter(selected));
      g.drawPolygon(polygonShift(p,1+off.width,1+off.height));
      g.drawPolygon(polygonShift(p,1+off.width,-1+off.height));
      g.drawPolygon(polygonShift(p,-1+off.width,1+off.height));
      g.drawPolygon(polygonShift(p,-1+off.width,-1+off.height));
      // Polyangle at the real location in the color, "inner".
      g.setColor(getInner(selected));
      g.drawPolygon(polygonShift(p,+off.width,+off.height));
  }

  // Returns a new polygon with all coordinates shifted (dx,dy) from the original
  // polygon.
  private static Polygon polygonShift(Polygon p,int dx,int dy) {
      Polygon ret = new Polygon();
      for (int n = 0; n < p.npoints; n++) {
          ret.addPoint(p.xpoints[n] + dx,p.ypoints[n] + dy);
      }
      return ret;
  }

  void clip(Rectangle clp) {
    // Don't know if it is safe to change the points directly like this.

    for (int n = 0; n < polygon.npoints; n++) {
      polygon.xpoints[n] = Math.max(clp.x,polygon.xpoints[n]);
      polygon.ypoints[n] = Math.max(clp.y,polygon.ypoints[n]);
      polygon.xpoints[n] = Math.min(clp.x + clp.width,polygon.xpoints[n]);
      polygon.ypoints[n] = Math.min(clp.y + clp.height,polygon.ypoints[n]);
    }
  }

  boolean moveBy(int dx,int dy,Rectangle clp) {
    // Check to see if we can move the polygon.
    for (int n = 0; n < polygon.npoints; n++) {
      if (polygon.xpoints[n] + dx < clp.x) {
        dx = Math.min(clp.x - polygon.xpoints[n],0);
      }
      else if (polygon.xpoints[n] + dx >= clp.x + clp.width) {
        dx = Math.max(clp.x + clp.width - (polygon.xpoints[n] + 1),0);
      }

      if (polygon.ypoints[n] + dy < clp.y) {
        dy = Math.min(clp.y - polygon.ypoints[n],0);
      }
      else if (polygon.ypoints[n] + dy >= clp.y + clp.height) {
        dy = Math.max(clp.y + clp.height - (polygon.ypoints[n] + 1),0);
      }
    }

    if (dx != 0 || dy != 0) {
      polygon = polygonShift(polygon,dx,dy);
      return true;
    }

    // actually move the polygon
/*    for (int n = 0; n < polygon.npoints; n++) {
        polygon.xpoints[n] += dx;
        polygon.ypoints[n] += dy;
    } */
    // java.awt.Polygon doesn't seem to work right if you change the
    // points directly.  Polygon.containsPoint messes up.

    return false;
  }

  boolean resizeBy(int dx,int dy,Rectangle clp) {
    // We could do something like expand the entire polygon.
    // Just ignore it.
    return false;
  }

  // Return a new Tag with all the information in the PolyArea.
  Tag toTag() {
      Tag tag = new Tag("area");
      tag.addAttribute("shape","polygon");
      StringBuffer coords = new StringBuffer();
      // Ignore last point, it is the same as the first.
      for (int n = 0; n < polygon.npoints - 1; n++) {
          if (n > 0)
              coords.append(",");
          coords.append("" + polygon.xpoints[n] + ","
                          + polygon.ypoints[n]);
      }

      tag.addAttribute("coords",coords.toString());
      String href = getURL().toString();
      if (href.length() > 0) {
        tag.addAttribute("href",href);
      }
      return tag;
  }

  static PolyArea fromTag(Tag tag,URL base) {
    if (tag.getName().equals("AREA") &&
//        tag.containsAttribute("HREF") &&
        tag.containsAttribute("COORDS")) {

      // If ANYTHING goes wrong in parsing, we just ignore the tag.
      try {
        String shape = tag.lookupAttribute("SHAPE");

        if (shape.equalsIgnoreCase("POLY") ||
            shape.equalsIgnoreCase("POLYGON")) {
          String coords = tag.lookupAttribute("COORDS");

          StringTokenizer tok = new StringTokenizer(coords," ,");
          Polygon poly = new Polygon();
          // Add as many points to polygon as possible.
          try {
            while (true) {
              poly.addPoint(Integer.parseInt(tok.nextToken()),
                            Integer.parseInt(tok.nextToken()));
            }
          } catch (java.util.NoSuchElementException e) {}

          // Don't bother if not at least a triangle.
          if (poly.npoints < 3)
            return null;

          // Add trailing point that is same as starting point.
          poly.addPoint(poly.xpoints[0],poly.ypoints[0]);

          // a completed polygon.
          return new PolyArea(poly,true,get_href(tag,base));
        }
      }
      catch (Throwable t) {}
    }
    return null;
  }


  private boolean isComplete;

  /** The actual size/location of the PolyArea.
   A closed polygon, first point is same as last. */
  private Polygon polygon;
}
