package org.locationtech.jtstest.testbuilder.geom;

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

public class EnvelopeClipper {
  
  private Envelope env;

  public EnvelopeClipper(Envelope env) {
    this.env = env;
  }
  public LineSegment clip(Coordinate p0, Coordinate p1) {
    Coordinate clip0 = clipEndpoint(env, p0, p1);
    Coordinate clip1 = clipEndpoint(env, p1, p0);
    
    // if clipped segment lies on boundary of envelope
    // return null to indicate this
    if (isOnBoundary(clip0, clip1))
      return null;
    
    return new LineSegment(clip0, clip1);
  }

  private boolean isOnBoundary(Coordinate p0, Coordinate p1) {
    if (p0.x == p1.x) {
      if (p0.x == env.getMinX() || p0.x == env.getMaxX())
        return true;
    }
    if (p0.y == p1.y) {
      if (p0.y == env.getMinY() || p0.x == env.getMaxY())
        return true;
    }
    return false;
  }
  
  private Coordinate clipEndpoint(Envelope env, Coordinate p0, Coordinate p1) {
    Coordinate clipPt = new Coordinate(p0);
    if (p0.x < env.getMinX()) {
      clipPt.x = intersectionLineX(p0, p1, env.getMinX());
    }
    else if (p0.x > env.getMaxX()) {
      clipPt.x = intersectionLineX(p0, p1, env.getMaxX());
    }
    if (p0.y < env.getMinY()) {
      clipPt.y = intersectionLineY(p0, p1, env.getMinY());
    }
    else if (p0.y > env.getMaxX()) {
      clipPt.y = intersectionLineY(p0, p1, env.getMaxY());
    }
    return clipPt;
  }

  private double intersectionLineY(Coordinate a, Coordinate b, double y) {
    // short-circuit if segment is parallel to Y axis
    if (b.x == a.x) return a.x;
    
    double m = (b.x - a.x) / (b.y - a.y);
    double intercept = (y - a.y) * m;
    return a.x + intercept;
  }

  private double intersectionLineX(Coordinate a, Coordinate b, double x) {
    // short-circuit if segment is parallel to X axis
    if (b.y == a.y) return a.y;
    
    double m = (b.y - a.y) / (b.x - a.x);
    double intercept = (x - a.x) * m;
    return a.y + intercept;
  }

}
