class PathSimplification {

    public static PointList simplify(PathWrapper pathWrapper, DouglasPeucker douglasPeucker, boolean enableInstructions) {
        final PointList pointList = pathWrapper.getPoints();
        List<Partition> partitions = new ArrayList<>();
        // todo: maybe this code can be simplified if path details and instructions would be merged, see #1121
        if (enableInstructions) {
            final InstructionList instructions = pathWrapper.getInstructions();
            partitions.add(new Partition() {
                @Override
                public int size() {
                    return instructions.size();
                }

                @Override
                public int getIntervalLength(int index) {
                    return instructions.get(index).getLength();
                }

                @Override
                public void setInterval(int index, int start, int end) {
                    Instruction instruction = instructions.get(index);
                    if (instruction instanceof ViaInstruction || instruction instanceof FinishInstruction) {
                        if (start != end) {
                            throw new IllegalStateException("via- and finish-instructions are expected to have zero length");
                        }
                        // have to make sure that via instructions and finish instructions contain a single point
                        // even though their 'instruction length' is zero.
                        end++;
                    }
                    instruction.setPoints(pointList.shallowCopy(start, end, false));
                }
            });
        }

        for (final Map.Entry<String, List<PathDetail>> entry : pathWrapper.getPathDetails().entrySet()) {
            // If the pointList only contains one point, PathDetails have to be empty because 1 point => 0 edges
            final List<PathDetail> detail = entry.getValue();
            if (detail.isEmpty() && pointList.size() > 1)
                throw new IllegalStateException("PathDetails " + entry.getKey() + " must not be empty");

            partitions.add(new Partition() {
                @Override
                public int size() {
                    return detail.size();
                }

                @Override
                public int getIntervalLength(int index) {
                    return detail.get(index).getLength();
                }

                @Override
                public void setInterval(int index, int start, int end) {
                    PathDetail pd = detail.get(index);
                    pd.setFirst(start);
                    pd.setLast(end);
                }
            });
        }

        simplify(pathWrapper.getPoints(), partitions, douglasPeucker);
        assertConsistencyOfPathDetails(pathWrapper.getPathDetails());
        if (enableInstructions)
            assertConsistencyOfInstructions(pathWrapper.getInstructions(), pathWrapper.getPoints().size());
        return pointList;
    }

    private  PathSimplification(PointList pointList, List<Partition> partitions, DouglasPeucker douglasPeucker);

}