/*
 * Copyright (c) 2016 Vivid Solutions.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
 * and the Eclipse Distribution License is available at
 *
 * http://www.eclipse.org/org/documents/edl-v10.php.
 */

package org.locationtech.jts.triangulate;

import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.LineSegment;

/**
 * Models a constraint segment which can be split in two in various ways, 
 * according to certain geometric constraints.
 * 
 * @author Martin Davis
 */
public class SplitSegment {
    /**
     * Computes the {@link Coordinate} that lies a given fraction along the line defined by the
     * reverse of the given segment. A fraction of <code>0.0</code> returns the end point of the
     * segment; a fraction of <code>1.0</code> returns the start point of the segment.
     * 
     * @param seg the LineSegment
     * @param segmentLengthFraction the fraction of the segment length along the line
     * @return the point at that distance
     */
    private static Coordinate pointAlongReverse(LineSegment seg, double segmentLengthFraction) {
        Coordinate coord = new Coordinate();
        coord.x = seg.p1.x - segmentLengthFraction * (seg.p1.x - seg.p0.x);
        coord.y = seg.p1.y - segmentLengthFraction * (seg.p1.y - seg.p0.y);
        return coord;
    }

    private LineSegment seg;
    private double      segLen;
    private Coordinate  splitPt;
    private double      minimumLen = 0.0;

    public SplitSegment(LineSegment seg) {
        this.seg = seg;
        segLen = seg.getLength();
    }

    public void setMinimumLength(double minLen) {
        minimumLen = minLen;
    }

    public Coordinate getSplitPoint() {
        return splitPt;
    }

    public void splitAt(double length, Coordinate endPt) {
        double actualLen = getConstrainedLength(length);
        double frac = actualLen / segLen;
        if (endPt.equals2D(seg.p0))
            splitPt = seg.pointAlong(frac);
        else
            splitPt = pointAlongReverse(seg, frac);
    }

    public void splitAt(Coordinate pt) {
        // check that given pt doesn't violate min length
        double minFrac = minimumLen / segLen;
        if (pt.distance(seg.p0) < minimumLen) {
            splitPt = seg.pointAlong(minFrac);
            return;
        }
        if (pt.distance(seg.p1) < minimumLen) {
            splitPt = pointAlongReverse(seg, minFrac);
            return;
        }
        // passes minimum distance check - use provided point as split pt
        splitPt = pt;
    }

    private double getConstrainedLength(double len) {
        if (len < minimumLen)
            return minimumLen;
        return len;
    }

}
