// License: GPL. For details, see LICENSE file.
package indoor_sweepline;

import java.util.Vector;

import org.openstreetmap.josm.data.coor.ILatLon;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.Way;

public class CorridorGeography {

    public CorridorGeography(DataSet dataSet) {
        this.dataSet = dataSet;
    }

    private static final double MIN_LENGTH = 10.;

    private void setExtraElements(CorridorPart.ReachableSide side, ILatLon from, ILatLon to,
            boolean extraWayUp, double minLength) {
        LatLon middleCoor = new LatLon((from.lat() + to.lat())/2.,
                (from.lon() + to.lon())/2.);
        if (middleNode == null) {
            middleNode = new Node(middleCoor);
            dataSet.addPrimitive(middleNode);
        } else
            middleNode.setCoor(middleCoor);

        ILatLon start = from;
        if (side == CorridorPart.ReachableSide.LEFT) {
            if (middleCoor.lat() < start.lat())
                start = to;
        } else if (side == CorridorPart.ReachableSide.RIGHT) {
            if (start.lat() < middleCoor.lat())
                start = to;
        } else if (side == CorridorPart.ReachableSide.FRONT) {
            if (start.lon() < middleCoor.lon())
                start = to;
        } else if (side == CorridorPart.ReachableSide.BACK) {
            if (middleCoor.lon() < start.lon())
                start = to;
        }

        double scale = Math.cos(middleCoor.lat() * (Math.PI/180.));
        double length = Math.sqrt((start.lat() - middleCoor.lat()) * (start.lat() - middleCoor.lat()) +
                (start.lon() - middleCoor.lon()) * (start.lon() - middleCoor.lon()) * scale * scale) / 180. * 20000000.;
        double lengthFactor = length < minLength ? minLength / length : 1.;
        LatLon detachedCoor = new LatLon(middleCoor.lat() + (start.lon() - middleCoor.lon()) * scale * lengthFactor,
                middleCoor.lon() - (start.lat() - middleCoor.lat()) / scale * lengthFactor);
        if (detachedNode == null) {
            detachedNode = new Node(detachedCoor);
            dataSet.addPrimitive(detachedNode);
        } else
            detachedNode.setCoor(detachedCoor);

        Vector<Node> extraWayNodes = new Vector<>();
        if (extraWayUp) {
            extraWayNodes.add(middleNode);
            extraWayNodes.add(detachedNode);
        } else {
            extraWayNodes.add(detachedNode);
            extraWayNodes.add(middleNode);
        }
        if (extraWay == null) {
            extraWay = new Way();
            extraWay.setNodes(extraWayNodes);
            dataSet.addPrimitive(extraWay);
        } else
            extraWay.setNodes(extraWayNodes);
    }

    public void appendNodes(CorridorPart.Type type, CorridorPart.ReachableSide side, String level,
            ILatLon from, ILatLon to, ModelGeography target) {
        if (type == CorridorPart.Type.STAIRS_UP || type == CorridorPart.Type.STAIRS_DOWN) {
            setExtraElements(side, from, to, type == CorridorPart.Type.STAIRS_UP, MIN_LENGTH);
            target.appendNode(middleNode);

            detachedNode.removeAll();

            extraWay.removeAll();
            extraWay.put("highway", "steps");
            extraWay.put("incline", "up");
            extraWay.put("level", level);
        } else if (type == CorridorPart.Type.ESCALATOR_UP_LEAVING
                || type == CorridorPart.Type.ESCALATOR_UP_ARRIVING
                || type == CorridorPart.Type.ESCALATOR_UP_BIDIRECTIONAL
                || type == CorridorPart.Type.ESCALATOR_DOWN_LEAVING
                || type == CorridorPart.Type.ESCALATOR_DOWN_ARRIVING
                || type == CorridorPart.Type.ESCALATOR_DOWN_BIDIRECTIONAL) {
            setExtraElements(side, from, to,
                    type == CorridorPart.Type.ESCALATOR_UP_LEAVING
                    || type == CorridorPart.Type.ESCALATOR_UP_ARRIVING
                    || type == CorridorPart.Type.ESCALATOR_UP_BIDIRECTIONAL, MIN_LENGTH);
            target.appendNode(middleNode);

            detachedNode.removeAll();

            extraWay.removeAll();
            extraWay.put("highway", "steps");
            extraWay.put("incline", "up");
            if (type == CorridorPart.Type.ESCALATOR_UP_LEAVING
                    || type == CorridorPart.Type.ESCALATOR_DOWN_ARRIVING)
                extraWay.put("conveying", "forward");
            else if (type == CorridorPart.Type.ESCALATOR_UP_ARRIVING
                    || type == CorridorPart.Type.ESCALATOR_DOWN_LEAVING)
                extraWay.put("conveying", "backward");
            else
                extraWay.put("conveying", "reversible");
            extraWay.put("level", level);
        } else if (type == CorridorPart.Type.ELEVATOR) {
            setExtraElements(side, from, to, true, 0.);
            target.appendNode(middleNode);

            detachedNode.removeAll();
            detachedNode.put("highway", "elevator");

            extraWay.removeAll();
            extraWay.put("highway", "footway");
            extraWay.put("level", level);
        } else {
            if (extraWay != null) {
                extraWay.setDeleted(true);
                extraWay = null;
            }
            if (middleNode != null) {
                middleNode.setDeleted(true);
                middleNode = null;
            }
            if (detachedNode != null) {
                detachedNode.setDeleted(true);
                detachedNode = null;
            }
        }
    }

    private DataSet dataSet;
    private Node middleNode;
    private Node detachedNode;
    private Way extraWay;
}
