package walltest;

public class Segment {

    public boolean inStorage = false;   // true: segment is part stored in rTree
    public int id = -1;                  // search key etc.
    public float[] data;
    public float[] center = new float[2];   // center point
    public float direction;         // in degrees, between 0 and 180
    public float length;
    public float vecX, vecY;        // end-startpoint
    public float sigmaX, sigmaY;    // directional variance describing the segment as distribution
    public double directionalNormalization;
    public int iTemp;               // a general purpose work variable
    public float weight;

    public Segment() {
    }

    public Segment(float[] iData, float sx, float sy) {
        data = iData;   // shallow copy is ok
        center[0] = (data[0] + data[2]) / 2;
        center[1] = (data[1] + data[3]) / 2;
        vecX = data[2] - data[0];
        vecY = data[3] - data[1];
        length = (float) Math.sqrt((double) (vecX * vecX + vecY * vecY));
        weight = length;
        direction = (float) (Math.atan2(vecY, vecX) / Math.PI * 180.0);
        if (direction < 0.0) {
            direction += 180.0;
        } else if (direction >= 180.0) {
            direction -= 180.0;
        }
        sigmaX = sx / length;
        sigmaY = sy / length;

    }

    // -------------------------------------------------------------------------
    public static float[] centerOfMass(Segment[] s) {
        float[] coM = new float[2];
        float sumL = 0.0f;
        for (int i = 0; i < s.length; i++) {
            sumL += s[i].length;
            coM[0] += s[i].length * s[i].center[0];
            coM[1] += s[i].length * s[i].center[1];
        }
        coM[0] /= sumL;
        coM[1] /= sumL;
        return (coM);
    }

    // -------------------------------------------------------------------------
    public static void translate(Segment[] s, double[] t) {
        for (int i = 0; i < s.length; i++) {
            s[i].data[0] += t[0];
            s[i].data[1] += t[1];
            s[i].data[2] += t[0];
            s[i].data[3] += t[1];
            s[i].center[0] += t[0];
            s[i].center[1] += t[1];
        }
    }

    // -------------------------------------------------------------------------
    public static void rotateAroundCenter(Segment[] s, double a, float[] c) {

        // rotate
        double cos = Math.cos(a);
        double sin = Math.sin(a);
        for (int i = 0; i < s.length; i++) {
            // translate
            s[i].data[0] -= c[0];
            s[i].data[1] -= c[1];
            s[i].data[2] -= c[0];
            s[i].data[3] -= c[1];

            // rotate
            double nx = cos * s[i].data[0] - sin * s[i].data[1];
            double ny = sin * s[i].data[0] + cos * s[i].data[1];
            s[i].data[0] = (float) nx;
            s[i].data[1] = (float) ny;
            nx = cos * s[i].data[2] - sin * s[i].data[3];
            ny = sin * s[i].data[2] + cos * s[i].data[3];
            s[i].data[2] = (float) nx;
            s[i].data[3] = (float) ny;

            // translate back
            s[i].data[0] += c[0];
            s[i].data[1] += c[1];
            s[i].data[2] += c[0];
            s[i].data[3] += c[1];

            // adjust affected fields
            s[i].center[0] = (s[i].data[0] + s[i].data[2]) / 2;
            s[i].center[1] = (s[i].data[1] + s[i].data[3]) / 2;
            s[i].vecX = s[i].data[2] - s[i].data[0];
            s[i].vecY = s[i].data[3] - s[i].data[1];
            s[i].direction += a*180.0f/Math.PI;
            if (s[i].direction < 0.0) {
                s[i].direction += 180.0;
            } else if (s[i].direction > 180.0) {
                s[i].direction -= 180.0;
            }
        }
    }

    // distance point-segment
    public double[] distanceToPoint(float[] p) {
        double x = p[0];
        double y = p[1];
        double dist = 0;
        double xs = this.data[0];
        double ys = this.data[1];
        double xe = this.data[2];
        double ye = this.data[3];

        double dx = xe - xs;
        double dy = ye - ys;
        double dpx = x - xs;
        double dpy = y - ys;
        double px = 0, py = 0;
        if (dx * dx + dy * dy < 1e-10) {
            dist = (double) Math.sqrt(dpx * dpx + dpy * dpy);
        } else {
            double t = (dx * dpx + dy * dpy) / (dx * dx + dy * dy);
            if (t <= 0) {
                px = xs;
                py = ys;
            } else if (t >= 1) {
                px = xe;
                py = ye;
            } else {
                px = xs + t * dx;
                py = ys + t * dy;
            }
            dpx = x - px;
            dpy = y - py;
            dist = (double) Math.sqrt(dpx * dpx + dpy * dpy);
        }
        double[] d = {dist, px, py};
        return (d);
    }
    
    //fast way to find intersection point between two INFINITE lines
    // (lines = infinitely extended segments)
    public float[] getIntersectingPoint(Segment s){
        
        float[] intPoint = new float[2];
        
        float x1 = this.data[0];
        float y1 = this.data[1];
        float x2 = this.data[2];
        float y2 = this.data[3];
        float x3 = s.data[0];
        float y3 = s.data[1];
        float x4 = s.data[2];
        float y4 = s.data[3];        
        
        float det1And2 = det(x1, y1, x2, y2);
        float det3And4 = det(x3, y3, x4, y4);
        float x1LessX2 = x1 - x2;
        float y1LessY2 = y1 - y2;
        float x3LessX4 = x3 - x4;
        float y3LessY4 = y3 - y4;
        float det1Less2And3Less4 = det(x1LessX2, y1LessY2, x3LessX4, y3LessY4);
        
        if (det1Less2And3Less4 == 0){
            // the denominator is zero so the lines are parallel and there's either no solution (or multiple solutions if the lines overlap) so return null.
            return null;
        }
        
        intPoint[0] = (det(det1And2, x1LessX2,
                det3And4, x3LessX4) /
                det1Less2And3Less4);
        intPoint[1] = (det(det1And2, y1LessY2,
                det3And4, y3LessY4) /
                det1Less2And3Less4);
        return intPoint;
   }
   
   //helper method to get determinant for line intersection
   protected float det(float a, float b, float c, float d) {
      return a * d - b * c;
   }
   
   //checks if two line SEGMENTS intersect
   protected boolean linesIntersect(Segment s){
       
        float x1 = this.data[0];
        float y1 = this.data[1];
        float x2 = this.data[2];
        float y2 = this.data[3];
        float x3 = s.data[0];
        float y3 = s.data[1];
        float x4 = s.data[2];
        float y4 = s.data[3]; 
        
        // Return false if either of the lines have zero length
        if (length == 0 || s.length == 0){
            return false;
        }
        
        // Fastest method, based on Franklin Antonio's "Faster Line Segment Intersection" 
        //topic "in Graphics Gems III" book (http://www.graphicsgems.org/)
        double ax = x2-x1;
        double ay = y2-y1;
        double bx = x3-x4;
        double by = y3-y4;
        double cx = x1-x3;
        double cy = y1-y3;

        double alphaNumerator = by*cx - bx*cy;
        double commonDenominator = ay*bx - ax*by;
        if (commonDenominator > 0){
            if (alphaNumerator < 0 || alphaNumerator > commonDenominator){
            return false;
            }
        }else if (commonDenominator < 0){
            if (alphaNumerator > 0 || alphaNumerator < commonDenominator){
            return false;
            }
        }
        double betaNumerator = ax*cy - ay*cx;
        if (commonDenominator > 0){
            if (betaNumerator < 0 || betaNumerator > commonDenominator){
            return false;
            }
        }else if (commonDenominator < 0){
            if (betaNumerator > 0 || betaNumerator < commonDenominator){
            return false;
            }
        }
        if (commonDenominator == 0){
            // This code wasn't in Franklin Antonio's method. It was added by Keith Woodward.
            // The lines are parallel.
            // Check if they're collinear.
            double y3LessY1 = y3-y1;
            double collinearityTestForP3 = x1*(y2-y3) + x2*(y3LessY1) + x3*(y1-y2);   // see http://mathworld.wolfram.com/Collinear.html
            // If p3 is collinear with p1 and p2 then p4 will also be collinear, since p1-p2 is parallel with p3-p4
            if (collinearityTestForP3 == 0){
            // The lines are collinear. Now check if they overlap.
            if (x1 >= x3 && x1 <= x4 || x1 <= x3 && x1 >= x4 ||
                    x2 >= x3 && x2 <= x4 || x2 <= x3 && x2 >= x4 ||
                    x3 >= x1 && x3 <= x2 || x3 <= x1 && x3 >= x2){
                if (y1 >= y3 && y1 <= y4 || y1 <= y3 && y1 >= y4 ||
                        y2 >= y3 && y2 <= y4 || y2 <= y3 && y2 >= y4 ||
                        y3 >= y1 && y3 <= y2 || y3 <= y1 && y3 >= y2){
                    return true;
                }
            }
            }
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        return (id);
    }

    @Override
    public String toString() {
        String s = "ID: " + id + " || Data: " + data[0] + "," + data[1] + "," + data[2] + "," + data[3] + " || weight: " + weight;
        return (s);
    }
}
