// https://synaptic.juancazala.com/scripts/homepage/vector.js

const { sin, cos, sqrt, atan2, abs } = Math
const eps = 0.0000001
const hypot = Math.hypot || ((a, b) => sqrt(a*a + b*b))

export const dot = (v1, v2) => v1[0] * v2[0] + v1[1] * v2[1]

export const length = (v) => hypot(v[0], v[1])

export const add = (v1, v2) => [ v1[0] + v2[0], v1[[1]] + v2[1] ]

export const subtract = (v1, v2) => [ v1[0] - v2[0], v1[[1]] - v2[1] ]

export const scale = (v, l) => [v[0] * l, v[1] * l]

export const unit = (v) =>  {
    const l = length(v)
    return l ? scale(v, 1 / l) : [0, 0]
}

export const rotate = (v, angle) => [
    v[0] * cos(angle) - v[1] * sin(angle),
    v[0] * sin(angle) + v[1] * cos(angle)
]

export const limit = (v, max) => {
    const l = length(v)
    return (l > max) ? scale(v, max / l) : v
}

export const angle = (v) => atan2(v[1], v[0])


export const intersect1 = (v1, v2, v3 ,v4) => {
    const denominator = ((v4[1] - v3[1]) * (v2[0] - v1[0])) - ((v4[0] - v3[0]) * (v2[1] - v1[1]))
    if (denominator === 0) {
        return null
    }

    const a = v1[1] - v3[1]
    const b = v1[0] - v3[0]

    const numerator1 = ((v4[0] - v3[0]) * a) - ((v4[1] - v3[1]) * b)
    const numerator2 = ((v2[0] - v1[0]) * a) - ((v2[1] - v1[1]) * b)

    const r1 = numerator1 / denominator
    const r2 = numerator2 / denominator

    const onLine1 = (r1 > 0 && r1 < 1)
    const onLine2 = (r2 > 0 && r2 < 1)

    if (!onLine1 || !onLine2) return null

    return [v1[0] + (a * (v2[0] - v1[0])), v1[1] + (a * (v2[1] - v1[1]))]
}

const cache = new Map()
export const intersect = (v1, v2, v3 ,v4) => {
    return intersect2(v1, v2, v3 ,v4)
    const key = `${v1[0]}.${v1[1]}.${v2[0]}.${v2[1]}.${v3[0]}.${v3[1]}.${v4[0]}.${v4[1]}`

    const val = cache.get(key)
    if (val !== undefined) return val

    const newval = intersect2(v1, v2, v3 ,v4)
    cache.set(key, newval)
    return newval
}

export const intersect2 = (v1, v2, v3 ,v4) => {
    const a1 = v2[1] - v1[1]
    const b1 = v1[0] - v2[0]
    const c1 = (v2[0] * v1[1]) - (v1[0] * v2[1])

    const r3 = ((a1 * v3[0]) + (b1 * v3[1]) + c1)
    const r4 = ((a1 * v4[0]) + (b1 * v4[1]) + c1)

    if ((r3 != 0) && (r4 != 0) && ((r3 * r4) >= eps)) {
        return null
    }

    const a2 = v4[1] - v3[1]
    const b2 = v3[0] - v4[0];
    const c2 = (v4[0] * v3[1]) - (v3[0] * v4[1]);

    const r1 = (a2 * v1[0]) + (b2 * v1[1]) + c2;
    const r2 = (a2 * v2[0]) + (b2 * v2[1]) + c2;

    if ((r1 != 0) && (r2 != 0) && ((r1 * r2) >= eps)) {
        return null
    }


    const denom = (a1 * b2) - (a2 * b1);

    if (denom == 0) {
        return null
    }

    const offset = denom < 0 ? -denom / 2 : denom / 2

    const numx = (b1 * c2) - (b2 * c1)
    const x = numx < 0 ? (numx - offset) / denom : (numx + offset) / denom

    const numy = (a2 * c1) - (a1 * c2)
    const y = numy < 0 ? (numy - offset) / denom : (numy + offset) / denom

    return [x, y]
}


const between = (a, b, c) =>  a - eps <= b && b <= c + eps
export const intersect3 = ([x1,y1], [x2,y2], [x3,y3], [x4,y4]) => {
    var x=((x1*y2-y1*x2)*(x3-x4)-(x1-x2)*(x3*y4-y3*x4)) /
            ((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4));
    var y=((x1*y2-y1*x2)*(y3-y4)-(y1-y2)*(x3*y4-y3*x4)) /
            ((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4));
    if (isNaN(x)||isNaN(y)) {
        return false;
    } else {
        if (x1>=x2) {
            if (!between(x2, x, x1)) {return false;}
        } else {
            if (!between(x1, x, x2)) {return false;}
        }
        if (y1>=y2) {
            if (!between(y2, y, y1)) {return false;}
        } else {
            if (!between(y1, y, y2)) {return false;}
        }
        if (x3>=x4) {
            if (!between(x4, x, x3)) {return false;}
        } else {
            if (!between(x3, x, x4)) {return false;}
        }
        if (y3>=y4) {
            if (!between(y4, y, y3)) {return false;}
        } else {
            if (!between(y3, y, y4)) {return false;}
        }
    }
    return [x, y]
}

export default {
    dot,
    length,
    add,
    subtract,
    scale,
    unit,
    rotate,
    limit,
    angle,
    intersect
}

