package maps.osm;

import javax.swing.JFrame;
import javax.swing.JComponent;

import java.awt.Point;
import java.awt.Insets;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;

import java.io.Writer;
import java.io.File;
import java.io.FileWriter;

import org.dom4j.Document;
import org.dom4j.io.XMLWriter;
import org.dom4j.io.OutputFormat;

/**
   This class extracts a portion of an OSMMap.
*/
public class OSMMapExtractor extends MouseAdapter {
    private static final int VIEWER_SIZE = 500;
    private static final Color DRAG_COLOUR = new Color(128, 128, 128, 64);

    private JComponent glass;
    private Point press;
    private Point drag;
    private Point release;

    private OSMMap map;
    private OSMMapViewer viewer;
    private Writer out;

    /**
       Construct an OSMMapExtractor.
       @param map The map.
       @param viewer The viewer.
       @param out The writer to write extracted data to.
    */
    public OSMMapExtractor(OSMMap map, OSMMapViewer viewer, Writer out) {
        this.map = map;
        this.viewer = viewer;
        this.out = out;
        this.glass = new DragGlass();
    }

    /**
       Start the OSMMapExtractor.
       @param args Command line arguments: source target.
    */
    public static void main(String[] args) {
        try {
            OSMMap map = new OSMMap(new File(args[0]));
            Writer out = new FileWriter(new File(args[1]));
            OSMMapViewer viewer = new OSMMapViewer(map);

            OSMMapExtractor extractor = new OSMMapExtractor(map, viewer, out);
            viewer.addMouseListener(extractor);
            viewer.setPreferredSize(new Dimension(VIEWER_SIZE, VIEWER_SIZE));

            JFrame frame = new JFrame();
            frame.setGlassPane(extractor.getGlass());
            frame.setContentPane(viewer);
            frame.pack();
            frame.setVisible(true);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        }
        // CHECKSTYLE:OFF:IllegalCatch
        catch (Exception e) {
            e.printStackTrace();
        }
        // CHECKSTYLE:ON:IllegalCatch
    }

    /**
       Get a glass component for drawing the selection overlay.
       @return A glass component.
    */
    public JComponent getGlass() {
        return glass;
    }

    @Override
    public void mousePressed(MouseEvent e) {
        if (e.getButton() == MouseEvent.BUTTON3) {
            Point p = e.getPoint();
            Insets insets = viewer.getInsets();
            p.translate(-insets.left, -insets.top);
            press = new Point(p);
        }
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        if (e.getButton() == MouseEvent.BUTTON3) {
            Point p = e.getPoint();
            Insets insets = viewer.getInsets();
            p.translate(-insets.left, -insets.top);
            drag = new Point(p);
            glass.repaint();
        }
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        if (e.getButton() == MouseEvent.BUTTON3) {
            Point p = e.getPoint();
            Insets insets = viewer.getInsets();
            p.translate(-insets.left, -insets.top);
            release = new Point(p);
            drag = null;
            write();
        }
    }

    private void write() {
        double pressLat = viewer.getLatitude(press.y);
        double pressLon = viewer.getLongitude(press.x);
        double releaseLat = viewer.getLatitude(release.y);
        double releaseLon = viewer.getLongitude(release.x);
        try {
            OSMMap newMap = new OSMMap(map,
                                       Math.min(pressLat, releaseLat),
                                       Math.min(pressLon, releaseLon),
                                       Math.max(pressLat, releaseLat),
                                       Math.max(pressLon, releaseLon));
            Document d = newMap.toXML();
            XMLWriter writer = new XMLWriter(out, OutputFormat.createPrettyPrint());
            writer.write(d);
            writer.flush();
            writer.close();
            System.out.println("Wrote map");
        }
        // CHECKSTYLE:OFF:IllegalCatch
        catch (Exception ex) {
            ex.printStackTrace();
        }
        // CHECKSTYLE:ON:IllegalCatch
    }

    private class DragGlass extends JComponent {
        public void paintComponent(Graphics g) {
            if (drag == null) {
                return;
            }
            g.setColor(DRAG_COLOUR);
            int x = Math.min(press.x, drag.x);
            int y = Math.max(press.y, drag.y);
            int width = (int)Math.abs(press.x - drag.x);
            int height = (int)Math.abs(press.y - drag.y);
            g.fillRect(x, y, width, height);
        }
    }
}