// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.pdfimport;

import static org.openstreetmap.josm.tools.I18n.tr;

import java.awt.Color;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.Relation;
import org.openstreetmap.josm.data.osm.RelationMember;
import org.openstreetmap.josm.data.osm.Way;
import org.openstreetmap.josm.gui.progress.ProgressMonitor;

public class OsmBuilder {

    enum Mode { Draft, Final, Debug }

    private final FilePlacement placement;

    private String layerName;
    private String fillName;
    private String lineName;
    private Mode mode;

    private ProgressMonitor monitor;
    private int monitorPos;
    private int monitorTotal;

    public OsmBuilder(FilePlacement placement) {
        this.placement = placement;
    }

    public DataSet build(List<LayerContents> data, Mode mode, ProgressMonitor monitor) {

        this.monitor = monitor;
        this.monitorPos = 0;
        this.mode = mode;
        DataSet result = new DataSet();

        //count total items for progress monitor.
        this.monitorTotal = 0;
        for (LayerContents layer: data) {
            this.monitorTotal += layer.paths.size();
            for (PdfMultiPath mp: layer.multiPaths) {
                this.monitorTotal += mp.paths.size();
            }
        }

        monitor.beginTask(tr("Building JOSM layer"), this.monitorTotal);


        for (LayerContents layer: data) {
            this.addLayer(result, layer);
        }

        monitor.finishTask();
        return result;
    }

    private void addLayer(DataSet target, LayerContents layer) {
        Map<Point2D, Node> point2Node = new HashMap<>();

        this.fillName = this.printColor(layer.info.fill);
        this.lineName = this.printColor(layer.info.stroke);
        this.layerName = "" + layer.info.nr;

        //insert nodes
        for (Point2D pt: layer.points) {
            Node node = new Node();
            node.setCoor(this.placement.tranformCoords(pt));

            target.addPrimitive(node);
            point2Node.put(pt, node);
        }

        //insert ways
        Map<PdfPath, Way> path2Way = new HashMap<>();

        for (PdfPath path: layer.paths) {
            Way w = this.insertWay(path, point2Node, -1, false);
            target.addPrimitive(w);
            path2Way.put(path, w);
        }

        int pathId = 0;
        for (PdfMultiPath mpath: layer.multiPaths) {
            for (PdfPath path: mpath.paths) {
                Way w = this.insertWay(path, point2Node, pathId, true);
                target.addPrimitive(w);
                path2Way.put(path, w);
            }
            pathId++;
        }

        if (this.mode != Mode.Draft) {
            //insert relations
            for (PdfMultiPath mpath: layer.multiPaths) {
                Relation rel = new Relation();

                Map<String, String> keys = new HashMap<>();
                keys.put("type", "multipolygon");
                keys.put("area", "yes");
                rel.setKeys(keys);

                for (PdfPath path: mpath.paths) {
                    Way w = path2Way.get(path);
                    rel.addMember(new RelationMember("", w));
                }

                target.addPrimitive(rel);
            }
        }
    }

    private Way insertWay(PdfPath path, Map<Point2D, Node> point2Node, int multipathId, boolean multipolygon) {

        if (this.monitorPos % 100 == 0) {
            monitor.setExtraText(tr(" "+this.monitorPos+"/"+this.monitorTotal));
            monitor.setTicks(this.monitorPos);
        }
        this.monitorPos++;

        List<Node> nodes = new ArrayList<>(path.points.size());

        for (Point2D point: path.points) {
            Node node = point2Node.get(point);
            if (node == null) {
                throw new RuntimeException();
            }

            nodes.add(node);
        }

        Map<String, String> keys = new HashMap<>();

        if (this.mode != Mode.Draft) {
            keys.put("PDF_nr", "" + path.nr);
            keys.put("PDF_layer", this.layerName);

            if (path.isClosed()) {
                keys.put("PDF_closed", "yes");
            }

            if (this.fillName != null) {
                keys.put("PDF_fillColor", this.fillName);
            }

            if (this.lineName != null) {
                keys.put("PDF_lineColor", this.lineName);
            }

            if (multipathId != -1) {
                keys.put("PDF_multipath", ""+ multipathId);
            } else if (path.layer.info.fill != null && !multipolygon) {
                keys.put("area", "yes");
            }
        }

        if (this.mode == Mode.Debug) {

            keys.put("PDF_pathLayer", ""+path.layer.info.nr);
            keys.put("PDF_lineWidth", ""+path.layer.info.width);
            keys.put("PDF_lineDash", ""+path.layer.info.dash);
            keys.put("PDF_layerHash", ""+path.layer.info.hashCode());
            keys.put("PDF_layerDivider", ""+path.layer.info.divider);
        }

        Way newWay = new Way();
        newWay.setNodes(nodes);
        newWay.setKeys(keys);
        return newWay;
    }

    private String printColor(Color col) {
        if (col == null) {
            return null;
        }

        String s = Integer.toHexString(col.getRGB() & 0xffffff);
        while (s.length() < 6) {
            s = "0" + s;
        }

        return "#" + s;
    }
}
