import { cloneDeep } from "lodash";
import { simplify } from "../../utils/simplify";
export const DEFAULT_TOLERANCE = 0.01;
export function removeConsecutiveDuplicates(coords, mds) {
    // Filter out consecutive duplicate vertices.
    const keep = coords.map((e, index, arr) => {
        if (index < arr.length - 1) {
            return (e[0] !== arr[index + 1][0] ||
                e[1] !== arr[index + 1][1] ||
                e[2] !== arr[index + 1][2]);
        }
        return true;
    });
    coords = coords.filter((_e, index) => {
        return keep[index];
    });
    mds = mds.filter((_e, index) => {
        return keep[index];
    });
    return [coords, mds];
}
// Remove duplicates in well string
// If z value of well head not defined set it to top of well string.
export function checkWells(data) {
    var _a, _b;
    const no_wells = data.features.length;
    for (let well_no = 0; well_no < no_wells; well_no++) {
        const geometryCollection = data.features[well_no]
            .geometry;
        const lineString = geometryCollection === null || geometryCollection === void 0 ? void 0 : geometryCollection.geometries[1];
        if (((_a = lineString.coordinates) === null || _a === void 0 ? void 0 : _a.length) === undefined) {
            continue;
        }
        let coords = lineString.coordinates;
        // If not defined set wellhead z value to top of well string.
        const wellHead = geometryCollection === null || geometryCollection === void 0 ? void 0 : geometryCollection.geometries[0];
        if (wellHead.coordinates && wellHead.coordinates.length === 2) {
            wellHead.coordinates.push(coords[0][2]);
        }
        const mds = (_b = data.features[well_no].properties) === null || _b === void 0 ? void 0 : _b["md"];
        if (mds === undefined) {
            continue;
        }
        const nOrig = coords.length;
        [coords, mds[0]] = removeConsecutiveDuplicates(coords, mds[0]);
        const n = coords.length;
        if (n != nOrig) {
            console.warn("Well number ", well_no, " contains duplicates.");
        }
        if (n <= 1) {
            continue;
        }
        data.features[well_no].geometry
            .geometries[1].coordinates = coords;
        if (data.features[well_no].properties) {
            data.features[well_no].properties["md"] = mds; // eslint-disable-line
        }
    }
}
/**
 * Given four points P0, P1, P2, P4 and a argument t in the interval [0,1].
 * returns function value at t. t == 0 corresponds to P1 and t == 1 corrsponds to P2
 *
 * See https://qroph.github.io/2018/07/30/smooth-paths-using-catmull-rom-splines.html
 */
export function CatmullRom1D(P0, P1, P2, P3, t) {
    const alpha = 0.5;
    const tt = t * t;
    const ttt = t * t * t;
    const dist_p0_p1 = Math.sqrt((P1 - P0) * (P1 - P0) + (P1 - P0) * (P1 - P0) + (P1 - P0) * (P1 - P0));
    const dist_p1_p2 = Math.sqrt((P1 - P2) * (P1 - P2) + (P1 - P2) * (P1 - P2) + (P1 - P2) * (P1 - P2));
    const dist_p2_p3 = Math.sqrt((P3 - P2) * (P3 - P2) + (P3 - P2) * (P3 - P2) + (P3 - P2) * (P3 - P2));
    const t01 = Math.pow(dist_p0_p1, alpha);
    const t12 = Math.pow(dist_p1_p2, alpha);
    const t23 = Math.pow(dist_p2_p3, alpha);
    const m1 = P2 - P1 + t12 * ((P1 - P0) / t01 - (P2 - P0) / (t01 + t12));
    const m2 = P2 - P1 + t12 * ((P3 - P2) / t23 - (P3 - P1) / (t12 + t23));
    const a_x = 2 * (P1 - P2) + m1 + m2;
    const b_x = -3 * (P1 - P2) - m1 - m1 - m2;
    const c_x = m1;
    const d_x = P1;
    const x = a_x * ttt + b_x * tt + c_x * t + d_x;
    return x;
}
/**
 * Given four 3D points P0, P1, P2, P4 and a scalar argument t in the interval [0,1].
 * returns function value (3D) at t. t == 0 corresponds to P1 and t == 1 corrsponds to P2
 *
 * See https://qroph.github.io/2018/07/30/smooth-paths-using-catmull-rom-splines.html
 */
// prettier-ignore
export function CatmullRom(P0, P1, P2, P3, t) {
    const alpha = 0.5;
    const tt = t * t;
    const ttt = t * t * t;
    // disable eslint for some lines due to readability.
    const dist_p0_p1 = Math.sqrt((P1[0] - P0[0]) * (P1[0] - P0[0]) + (P1[1] - P0[1]) * (P1[1] - P0[1]) + (P1[2] - P0[2]) * (P1[2] - P0[2])); // eslint-disable-line
    const dist_p1_p2 = Math.sqrt((P1[0] - P2[0]) * (P1[0] - P2[0]) + (P1[1] - P2[1]) * (P1[1] - P2[1]) + (P1[2] - P2[2]) * (P1[2] - P2[2])); // eslint-disable-line
    const dist_p2_p3 = Math.sqrt((P3[0] - P2[0]) * (P3[0] - P2[0]) + (P3[1] - P2[1]) * (P3[1] - P2[1]) + (P3[2] - P2[2]) * (P3[2] - P2[2])); // eslint-disable-line
    const t01 = Math.pow(dist_p0_p1, alpha);
    const t12 = Math.pow(dist_p1_p2, alpha);
    const t23 = Math.pow(dist_p2_p3, alpha);
    const m1_x = (P2[0] - P1[0] + t12 * ((P1[0] - P0[0]) / t01 - (P2[0] - P0[0]) / (t01 + t12))); // eslint-disable-line
    const m1_y = (P2[1] - P1[1] + t12 * ((P1[1] - P0[1]) / t01 - (P2[1] - P0[1]) / (t01 + t12))); // eslint-disable-line
    const m1_z = (P2[2] - P1[2] + t12 * ((P1[2] - P0[2]) / t01 - (P2[2] - P0[2]) / (t01 + t12))); // eslint-disable-line
    const m2_x = (P2[0] - P1[0] + t12 * ((P3[0] - P2[0]) / t23 - (P3[0] - P1[0]) / (t12 + t23))); // eslint-disable-line
    const m2_y = (P2[1] - P1[1] + t12 * ((P3[1] - P2[1]) / t23 - (P3[1] - P1[1]) / (t12 + t23))); // eslint-disable-line
    const m2_z = (P2[2] - P1[2] + t12 * ((P3[2] - P2[2]) / t23 - (P3[2] - P1[2]) / (t12 + t23))); // eslint-disable-line
    const a_x = 2 * (P1[0] - P2[0]) + m1_x + m2_x;
    const a_y = 2 * (P1[1] - P2[1]) + m1_y + m2_y;
    const a_z = 2 * (P1[2] - P2[2]) + m1_z + m2_z;
    const b_x = -3 * (P1[0] - P2[0]) - m1_x - m1_x - m2_x;
    const b_y = -3 * (P1[1] - P2[1]) - m1_y - m1_y - m2_y;
    const b_z = -3 * (P1[2] - P2[2]) - m1_z - m1_z - m2_z;
    const c_x = m1_x;
    const c_y = m1_y;
    const c_z = m1_z;
    const d_x = P1[0];
    const d_y = P1[1];
    const d_z = P1[2];
    const x = a_x * ttt + b_x * tt + c_x * t + d_x;
    const y = a_y * ttt + b_y * tt + c_y * t + d_y;
    const z = a_z * ttt + b_z * tt + c_z * t + d_z;
    return [x, y, z];
}
/**
 * Will interpolate and refine wellpaths using spline interploation resulting
 * in smoother curves with more points.
 * Assumes 3D data.
 */
export function splineRefine(data_in, stepCount = 5) {
    var _a, _b;
    if (stepCount < 1) {
        return data_in;
    }
    const data = cloneDeep(data_in);
    const no_wells = data.features.length;
    const step = 1 / stepCount;
    const steps = Array(stepCount - 1)
        .fill(0)
        .map((_x, index) => (index + 1) * step);
    for (let well_no = 0; well_no < no_wells; well_no++) {
        const mds = (_a = data.features[well_no].properties) === null || _a === void 0 ? void 0 : _a["md"];
        if (mds === undefined) {
            continue;
        }
        const geometryCollection = data.features[well_no].geometry;
        const lineString = geometryCollection === null || geometryCollection === void 0 ? void 0 : geometryCollection.geometries[1];
        if (((_b = lineString.coordinates) === null || _b === void 0 ? void 0 : _b.length) === undefined) {
            continue;
        }
        const coords = lineString.coordinates;
        const n = coords.length;
        if (n <= 1) {
            continue;
        }
        const ts = n > 3 ? steps : [];
        // Point before first.
        const x0 = coords[0][0] - coords[1][0] + coords[0][0];
        const y0 = coords[0][1] - coords[1][1] + coords[0][1];
        const z0 = coords[0][2] - coords[1][2] + coords[0][2];
        const P_first = [x0, y0, z0];
        const md_first = mds[0][0] - mds[0][1] + mds[0][0];
        // Point after last.
        const xn = coords[n - 1][0] - coords[n - 2][0] + coords[n - 1][0];
        const yn = coords[n - 1][1] - coords[n - 2][1] + coords[n - 1][1];
        const zn = coords[n - 1][2] - coords[n - 2][2] + coords[n - 1][2];
        const P_n = [xn, yn, zn];
        const md_n = mds[0][n - 1] - mds[0][n - 2] + mds[0][n - 1];
        const newCoordinates = [];
        const newMds = [];
        newMds.push([]);
        for (let i = 0; i < n - 1; i += 1) {
            let P0, P1, P2, P3;
            let md0, md1, md2, md3;
            if (i === 0) {
                P0 = P_first;
                P1 = coords[i + 0];
                P2 = coords[i + 1];
                P3 = coords[i + 2];
                md0 = md_first;
                md1 = mds[0][i + 0];
                md2 = mds[0][i + 1];
                md3 = mds[0][i + 2];
            }
            else if (i === n - 2) {
                P0 = coords[n - 3];
                P1 = coords[n - 2];
                P2 = coords[n - 1];
                P3 = P_n;
                md0 = mds[0][n - 3];
                md1 = mds[0][n - 2];
                md2 = mds[0][n - 1];
                md3 = md_n;
            }
            else {
                P0 = coords[i - 1];
                P1 = coords[i - 0];
                P2 = coords[i + 1];
                P3 = coords[i + 2];
                md0 = mds[0][i - 1];
                md1 = mds[0][i - 0];
                md2 = mds[0][i + 1];
                md3 = mds[0][i + 2];
            }
            newCoordinates.push(P1);
            newMds[0].push(md1);
            // Skip first leg from platform to first survey point.
            if (i > 1) {
                for (let t_i = 0; t_i < ts.length; t_i += 1) {
                    const t = ts[t_i];
                    const [x, y, z] = CatmullRom(P0, P1, P2, P3, t);
                    const md = CatmullRom1D(md0, md1, md2, md3, t);
                    newCoordinates.push([x, y, z]);
                    newMds[0].push(md);
                }
            }
        }
        newCoordinates.push(coords[n - 1]);
        newMds[0].push(mds[0][n - 1]);
        data.features[well_no].geometry.geometries[1].coordinates = newCoordinates;
        if (data.features[well_no].properties) {
            data.features[well_no].properties["md"] = newMds; // eslint-disable-line
        }
    }
    return data;
}
/**
 * Will reduce/coarse the wellpaths.
 */
export function coarsenWells(dataIn, tolerance = DEFAULT_TOLERANCE) {
    var _a, _b;
    const data = cloneDeep(dataIn);
    const wellCount = data.features.length;
    for (let wellIndex = 0; wellIndex < wellCount; wellIndex++) {
        const geometryCollection = data.features[wellIndex]
            .geometry;
        const lineString = geometryCollection === null || geometryCollection === void 0 ? void 0 : geometryCollection.geometries[1];
        if (((_a = lineString.coordinates) === null || _a === void 0 ? void 0 : _a.length) === undefined) {
            continue;
        }
        const properties = data.features[wellIndex]
            .properties;
        if (properties) {
            const mds = (_b = properties["md"]) === null || _b === void 0 ? void 0 : _b[0];
            const [newPoints, newMds] = simplify(lineString.coordinates, mds !== null && mds !== void 0 ? mds : [], tolerance);
            lineString.coordinates = newPoints;
            if (properties["md"]) {
                properties["md"][0] = newMds;
            }
        }
    }
    return data;
}
export function flattenPath(data_in) {
    var _a;
    const data = cloneDeep(data_in);
    const no_wells = data.features.length;
    for (let well_no = 0; well_no < no_wells; well_no++) {
        const geometryCollection = data.features[well_no]
            .geometry;
        const lineString = geometryCollection === null || geometryCollection === void 0 ? void 0 : geometryCollection.geometries[1];
        if (((_a = lineString.coordinates) === null || _a === void 0 ? void 0 : _a.length) === undefined) {
            continue;
        }
        const coords = lineString.coordinates;
        // flatten by setting z value constant.
        const coords_flat = coords.map((e) => {
            return [e[0], e[1], 0.0];
        });
        data.features[well_no].geometry
            .geometries[1].coordinates = coords_flat;
    }
    return data;
}
export function invertPath(data_in) {
    var _a, _b;
    const data = cloneDeep(data_in);
    const no_wells = data.features.length;
    for (let well_no = 0; well_no < no_wells; well_no++) {
        const geometryCollection = data.features[well_no].geometry;
        const lineString = geometryCollection === null || geometryCollection === void 0 ? void 0 : geometryCollection.geometries[1];
        const wellHead = geometryCollection === null || geometryCollection === void 0 ? void 0 : geometryCollection.geometries[0];
        if ((_a = wellHead.coordinates) === null || _a === void 0 ? void 0 : _a[2]) {
            wellHead.coordinates[2] *= -1;
        }
        if (((_b = lineString.coordinates) === null || _b === void 0 ? void 0 : _b.length) === undefined) {
            continue;
        }
        const coords = lineString.coordinates;
        // Invert path by multiplying depth with -1.
        const coords_inverted = coords.map((e) => {
            return [e[0], e[1], -e[2]];
        });
        data.features[well_no].geometry.geometries[1].coordinates = coords_inverted;
    }
    return data;
}
/**
 * Calculates bounding box of all wells.
 */
export function GetBoundingBox(data) {
    var _a;
    let xMin = 9999999999;
    let yMin = 9999999999;
    let zMin = 9999999999;
    let xMax = -9999999999;
    let yMax = -9999999999;
    let zMax = -9999999999;
    const no_wells = data.features.length;
    for (let well_no = 0; well_no < no_wells; well_no++) {
        const geometryCollection = data.features[well_no]
            .geometry;
        const lineString = geometryCollection === null || geometryCollection === void 0 ? void 0 : geometryCollection.geometries[1];
        if (((_a = lineString.coordinates) === null || _a === void 0 ? void 0 : _a.length) === undefined) {
            continue;
        }
        const coords = lineString.coordinates;
        const n = coords.length;
        for (let i = 0; i < n; i++) {
            const xyz = coords[i];
            xMin = xyz[0] < xMin ? xyz[0] : xMin;
            yMin = xyz[1] < yMin ? xyz[1] : yMin;
            zMin = xyz[2] < zMin ? xyz[2] : zMin;
            xMax = xyz[0] > xMax ? xyz[0] : xMax;
            yMax = xyz[1] > yMax ? xyz[1] : yMax;
            zMax = xyz[2] > zMax ? xyz[2] : zMax;
        }
    }
    return [xMin, yMin, zMin, xMax, yMax, zMax];
}
//# sourceMappingURL=spline.js.map