import { Point } from '../../..';
import { HubRadiiCalculator } from './HubRadiiCalculator';
import { GreaterOrEqual } from './MetroGraphData';
//  Calculates the cost of the routing
export class CostCalculator {
    constructor(metroGraphData, bundlingSettings) {
        this.metroGraphData = metroGraphData;
        this.bundlingSettings = bundlingSettings;
    }
    //  Error of ink
    static InkError(oldInk, newInk, bundlingSettings) {
        return (oldInk - newInk) * bundlingSettings.InkImportance;
    }
    //  Error of path lengths
    static PathLengthsError(oldLength, newLength, idealLength, bundlingSettings) {
        return (oldLength - newLength) * (bundlingSettings.PathLengthImportance / idealLength);
    }
    //  Error of hubs
    static RError(idealR, nowR, bundlingSettings) {
        if (idealR <= nowR) {
            return 0;
        }
        const res = bundlingSettings.HubRepulsionImportance * ((1 - nowR / idealR) * (idealR - nowR));
        return res;
    }
    //  Error of bundles
    static BundleError(idealWidth, nowWidth, bundlingSettings) {
        if (idealWidth <= nowWidth) {
            return 0;
        }
        const res = bundlingSettings.BundleRepulsionImportance * ((1 - nowWidth / idealWidth) * (idealWidth - nowWidth));
        return res;
    }
    //  Cost of the whole graph
    static Cost(metroGraphData, bundlingSettings) {
        let cost = bundlingSettings.InkImportance * metroGraphData.Ink;
        //path lengths
        for (const metroline of metroGraphData.Metrolines) {
            cost += (bundlingSettings.PathLengthImportance * metroline.Length) / metroline.IdealLength;
        }
        cost += this.CostOfForces(metroGraphData);
        return cost;
    }
    //  Cost of the whole graph (hubs and bundles)
    static CostOfForces(metroGraphData) {
        let cost = 0;
        // hubs
        for (const v of metroGraphData.VirtualNodes()) {
            cost = cost + v.cachedRadiusCost;
        }
        // bundles
        for (const edge of metroGraphData.VirtualEdges()) {
            const v = edge[0];
            const u = edge[1];
            cost += metroGraphData.GetIjInfo(v, u).cachedBundleCost;
        }
        return cost;
    }
    //  Gain of ink
    InkGain(node, newPosition) {
        // ink
        const oldInk = this.metroGraphData.Ink;
        let newInk = this.metroGraphData.Ink;
        for (const adj of node.Neighbors) {
            const adjPosition = adj.Position;
            newInk -= adjPosition.sub(node.Position).length;
            newInk += adjPosition.sub(newPosition).length;
        }
        return CostCalculator.InkError(oldInk, newInk, this.bundlingSettings);
    }
    //  Gain of path lengths
    PathLengthsGain(node, newPosition) {
        let gain = 0;
        //edge lengths
        for (const e of this.metroGraphData.MetroNodeInfosOfNode(node)) {
            const oldLength = e.Metroline.Length;
            const prev = e.PolyPoint.prev.point;
            const next = e.PolyPoint.next.point;
            const newLength = e.Metroline.Length +
                next.sub(newPosition).length +
                prev.sub(newPosition).length -
                next.sub(node.Position).length -
                prev.sub(node.Position).length;
            gain += CostCalculator.PathLengthsError(oldLength, newLength, e.Metroline.IdealLength, this.bundlingSettings);
        }
        return gain;
    }
    //  Gain of radii
    RadiusGain(node, newPosition) {
        let gain = 0;
        gain = gain + node.cachedRadiusCost;
        gain = gain - this.RadiusCost(node, newPosition);
        return gain;
    }
    RadiusCost(node, newPosition) {
        let idealR;
        if (Point.closeDistEps(node.Position, newPosition)) {
            idealR = node.cachedIdealRadius;
        }
        else {
            idealR = HubRadiiCalculator.CalculateIdealHubRadiusWithNeighborsMBNP(this.metroGraphData, this.bundlingSettings, node, newPosition);
        }
        const t = { touchedObstacles: [] };
        if (!this.metroGraphData.looseIntersections.HubAvoidsObstaclesSPNBA(node, newPosition, idealR, t)) {
            return CostCalculator.Inf;
        }
        let cost = 0;
        for (const d of t.touchedObstacles) {
            const dist = d[1].sub(newPosition).length;
            cost += CostCalculator.RError(idealR, dist, this.bundlingSettings);
        }
        return cost;
    }
    //  Gain of bundles
    //  if a newPosition is not valid (e.g. intersect obstacles) the result is -inf
    BundleGain(node, newPosition) {
        let gain = node.cachedBundleCost;
        for (const adj of node.Neighbors) {
            const lgain = this.BundleCost(node, adj, newPosition);
            if (GreaterOrEqual(lgain, CostCalculator.Inf))
                return -CostCalculator.Inf;
            gain -= lgain;
        }
        return gain;
    }
    BundleCost(node, adj, newPosition) {
        const idealWidth = this.metroGraphData.GetWidthSSN(node, adj, this.bundlingSettings.EdgeSeparation);
        const t = { closestDist: [] };
        //find conflicting obstacles
        if (!this.metroGraphData.cdtIntersections.BundleAvoidsObstacles(node, adj, newPosition, adj.Position, idealWidth, t)) {
            return CostCalculator.Inf;
        }
        let cost = 0;
        for (const pair of t.closestDist) {
            const dist = pair[0].sub(pair[1]).length;
            cost += CostCalculator.BundleError(idealWidth / 2, dist, this.bundlingSettings);
        }
        return cost;
    }
}
CostCalculator.Inf = 1000000000;
//# sourceMappingURL=CostCalculator.js.map