/*
 * $Id: PolygonEditor.java 4513 2008-11-21 19:44:28Z uckelman $
 *
 * Copyright (c) 2004 by Rodney Kinney
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License (LGPL) as published by the Free Software Foundation.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, copies are available
 * at http://www.opensource.org.
 */
package VASSAL.build.module.map.boardPicker.board.mapgrid;

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.event.MouseInputAdapter;
import VASSAL.tools.SequenceEncoder;

public class PolygonEditor extends JPanel {
  private static final long serialVersionUID = 1L;

  private Polygon polygon;
  private int selected = -1;
  protected JScrollPane myScroll;

  public PolygonEditor(Polygon p) {
    polygon = p;
    reset();
  }
  
  protected void reset() {
    if (polygon == null || polygon.npoints == 0) {
      setupForCreate();
    }
    else {
      setupForEdit();
    }
  }

  public Polygon getPolygon() {
    return polygon;
  }
  
  public Polygon clonePolygon() {
    return new Polygon(polygon.xpoints, polygon.ypoints, polygon.npoints);
  }

  public void setPolygon(Polygon polygon) {
    this.polygon = polygon;
  }

  public void setScroll(JScrollPane scroll) {
    myScroll = scroll;
  }
  
  private void setupForCreate() {
    DefineRectangle dr = new DefineRectangle();
    addMouseListener(dr);
  }

  private void setupForEdit() {
    ModifyPolygon mp = new ModifyPolygon();
    addMouseListener(mp);
    addMouseMotionListener(mp);
    ActionListener l = new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        if (selected >= 0) {
          for (int i = selected; i < polygon.npoints - 1; ++i) {
            polygon.xpoints[i] = polygon.xpoints[i + 1];
            polygon.ypoints[i] = polygon.ypoints[i + 1];
          }
          polygon.npoints--;
          selected = -1;
          repaint();
        }
      }
    };
    registerKeyboardAction(l, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0), WHEN_IN_FOCUSED_WINDOW);
    registerKeyboardAction(l, KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE,0), WHEN_IN_FOCUSED_WINDOW);
    requestFocus();
    selected = 2;
    repaint();
  }

  public void center(Point p) {
    Rectangle r = this.getVisibleRect();
    if (r.width == 0) {
      r.width = 600;
      r.height = 600;
    }
    int x = p.x-r.width/2;
    int y = p.y-r.height/2;
    if (x < 0) x = 0;
    if (y < 0) y = 0;
    scrollRectToVisible(new Rectangle(x, y, r.width, r.height));
  }
  
  public static void reset(Polygon p, String path) {
    p.reset();
    SequenceEncoder.Decoder sd = new SequenceEncoder.Decoder(path, ';');
    while (sd.hasMoreTokens()) {
      String s = sd.nextToken();
      SequenceEncoder.Decoder pd = new SequenceEncoder.Decoder(s, ',');
      if (pd.hasMoreTokens()) {
        try {
          int x = Integer.parseInt(pd.nextToken().trim());
          if (pd.hasMoreTokens()) {
            int y = Integer.parseInt(pd.nextToken().trim());
            p.addPoint(x, y);
          }
        }
        // FIXME: review error message
        catch (NumberFormatException e) {
        }
      }
    }
  }

  public static String polygonToString(Polygon p) {
    final StringBuilder s = new StringBuilder();
    for (int i = 0; i < p.npoints; i++) {
      s.append(Math.round(p.xpoints[i])).append(',').append(Math.round(p.ypoints[i]));
      if (i < (p.npoints - 1)) {
        s.append(';');
      }
    }
    return s.toString();
  }

  public void paint(Graphics g) {
    paintBackground(g);
    if (polygon != null && polygon.npoints > 0) {
      final Graphics2D g2d = (Graphics2D) g;

      g2d.setColor(Color.white);
      g2d.setComposite(
        AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5F));
      g2d.fill(polygon);

      if (selected >= 0 && selected < polygon.xpoints.length) {
        g2d.setColor(Color.red);
        int x = polygon.xpoints[selected];
        int y = polygon.ypoints[selected];
        g2d.fillOval(x - 10, y - 10, 20, 20);
      }

      g2d.setComposite(AlphaComposite.SrcAtop);
      g2d.setColor(Color.black);
      g2d.setStroke(new BasicStroke(2.0F));
      g2d.drawPolygon(polygon);
    }
  }

  protected void paintBackground(Graphics g) {
    super.paint(g);
  }

  private class ModifyPolygon extends MouseInputAdapter {
    // implements java.awt.event.MouseMotionListener
    public void mouseDragged(MouseEvent e) {
      moveSelectedPoint(e);
      if (SwingUtilities.isLeftMouseButton(e)) {
        scrollAtEdge(e.getPoint(), 15);
      }
      repaint();
    }

    private void moveSelectedPoint(MouseEvent e) {
      if (selected >= 0 && selected < polygon.xpoints.length) {
        polygon.xpoints[selected] = e.getX();
        polygon.ypoints[selected] = e.getY();
      }
    }

    // implements java.awt.event.MouseListener
    public void mouseReleased(MouseEvent e) {
      moveSelectedPoint(e);
      repaint();
    }

    // implements java.awt.event.MouseListener
    public void mousePressed(MouseEvent e) {
      selected = -1;
      double minDist = Float.MAX_VALUE;
      for (int i = 0; i < polygon.npoints; ++i) {
        double dist = Point2D.distance(polygon.xpoints[i], polygon.ypoints[i], e.getX(), e.getY());
        if (dist < minDist) {
          minDist = dist;
          selected = i;
        }
      }
      if (e.isMetaDown()) {
        polygon.addPoint(e.getX(), e.getY());
        if (selected >= 0) {
          for (int i = polygon.npoints - 1; i > selected; --i) {
            polygon.xpoints[i] = polygon.xpoints[i - 1];
            polygon.ypoints[i] = polygon.ypoints[i - 1];
          }
          polygon.xpoints[selected] = e.getX();
          polygon.ypoints[selected] = e.getY();
        }
      }
    }

    public void scrollAtEdge(Point evtPt, int dist) {
      
      Point p = new Point(evtPt.x - myScroll.getViewport().getViewPosition().x,
          evtPt.y - myScroll.getViewport().getViewPosition().y);
      int dx = 0, dy = 0;
      if (p.x < dist && p.x >= 0)
        dx = -1;
      if (p.x >= myScroll.getViewport().getSize().width - dist
          && p.x < myScroll.getViewport().getSize().width)
        dx = 1;
      if (p.y < dist && p.y >= 0)
        dy = -1;
      if (p.y >= myScroll.getViewport().getSize().height - dist
          && p.y < myScroll.getViewport().getSize().height)
        dy = 1;

      if (dx != 0 || dy != 0) {
        Rectangle r = new Rectangle(myScroll.getViewport().getViewRect());
        r.translate(2 * dist * dx, 2 * dist * dy);
        r = r.intersection(new Rectangle(new Point(0, 0), getPreferredSize()));
        scrollRectToVisible(r);
      }
    }
  }

  private class DefineRectangle extends MouseInputAdapter {
    // implements java.awt.event.MouseListener
    public void mousePressed(MouseEvent e) {
      polygon = new Polygon();
      polygon.addPoint(e.getX(), e.getY());
      polygon.addPoint(e.getX(), e.getY());
      polygon.addPoint(e.getX(), e.getY());
      polygon.addPoint(e.getX(), e.getY());
      addMouseMotionListener(this);
    }

    // implements java.awt.event.MouseMotionListener
    public void mouseDragged(MouseEvent e) {
      polygon.xpoints[1] = e.getX();
      polygon.xpoints[2] = e.getX();
      polygon.ypoints[2] = e.getY();
      polygon.ypoints[3] = e.getY();
      repaint();
    }

    public void mouseReleased(MouseEvent e) {
      removeMouseListener(this);
      removeMouseMotionListener(this);
      setupForEdit();
    }

  }

  public static void main(String[] args) {
    JFrame f = new JFrame();
    f.add(new PolygonEditor(null));
    f.setSize(500, 500);
    f.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
    f.setVisible(true);
  }
}
