AbstractVoltageStyleProvider.java

/**
 * Copyright (c) 2022-2025, RTE (http://www.rte-france.com)
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package com.powsybl.nad.svg.iidm;

import com.powsybl.commons.config.BaseVoltagesConfig;
import com.powsybl.iidm.network.Branch;
import com.powsybl.iidm.network.Bus;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.Terminal;
import com.powsybl.iidm.network.ThreeSides;
import com.powsybl.iidm.network.TwoSides;
import com.powsybl.nad.model.*;
import com.powsybl.nad.model.BranchEdge.Side;
import com.powsybl.nad.svg.AbstractStyleProvider;
import com.powsybl.nad.svg.StyleProvider;
import com.powsybl.nad.utils.iidm.IidmUtils;

import java.util.*;

/**
 * @author Florian Dupuy {@literal <florian.dupuy at rte-france.com>}
 */
public abstract class AbstractVoltageStyleProvider extends AbstractStyleProvider {

    protected final Network network;
    private final Map<String, String> subnetworksHighlightMap = new HashMap<>();

    protected AbstractVoltageStyleProvider(Network network) {
        this.network = network;
        buildSubnetworkMaps();
    }

    protected AbstractVoltageStyleProvider(Network network, BaseVoltagesConfig baseVoltageStyle) {
        super(baseVoltageStyle);
        this.network = network;
        buildSubnetworkMaps();
    }

    @Override
    public List<String> getBusNodeStyleClasses(BusNode busNode) {
        if (busNode == BusNode.UNKNOWN) {
            return List.of(UNKNOWN_BUSNODE_CLASS);
        }
        List<String> styles = new ArrayList<>();
        Bus b = network.getBusView().getBus(busNode.getEquipmentId());
        if (b != null) {
            if (b.getV() > b.getVoltageLevel().getHighVoltageLimit()) {
                styles.add(StyleProvider.VL_OVERVOLTAGE_CLASS);
            } else if (b.getV() < b.getVoltageLevel().getLowVoltageLimit()) {
                styles.add(StyleProvider.VL_UNDERVOLTAGE_CLASS);
            }
        }
        return styles;
    }

    @Override
    public List<String> getBranchEdgeStyleClasses(BranchEdge branchEdge) {
        List<String> styleClasses = new ArrayList<>(super.getBranchEdgeStyleClasses(branchEdge));
        if (IidmUtils.isIidmBranch(branchEdge)) {
            Branch<?> branch = network.getBranch(branchEdge.getEquipmentId());
            if (branch.isOverloaded()) {
                styleClasses.add(StyleProvider.LINE_OVERLOADED_CLASS);
            }
        }
        return styleClasses;
    }

    @Override
    protected Optional<String> getBaseVoltageStyle(ThreeWtEdge threeWtEdge) {
        Terminal terminal = network.getThreeWindingsTransformer(threeWtEdge.getEquipmentId())
                .getTerminal(IidmUtils.getIidmSideFromThreeWtEdgeSide(threeWtEdge.getSide()));
        return getBaseVoltageStyle(terminal);
    }

    @Override
    protected boolean isDisconnected(BranchEdge branchEdge) {
        return isDisconnected(branchEdge, BranchEdge.Side.ONE) && isDisconnected(branchEdge, BranchEdge.Side.TWO);
    }

    @Override
    protected boolean isDisconnected(ThreeWtEdge threeWtEdge) {
        Terminal terminal = network.getThreeWindingsTransformer(threeWtEdge.getEquipmentId())
                .getTerminal(IidmUtils.getIidmSideFromThreeWtEdgeSide(threeWtEdge.getSide()));
        return terminal == null || !terminal.isConnected();
    }

    @Override
    protected boolean isDisconnected(BranchEdge edge, BranchEdge.Side side) {
        return IidmUtils.isDisconnected(network, edge, side);
    }

    @Override
    protected Optional<String> getBaseVoltageStyle(Edge edge) {
        if (edge instanceof ThreeWtEdge threeWtEdge) {
            Terminal terminal = network.getThreeWindingsTransformer(edge.getEquipmentId())
                    .getTerminal(IidmUtils.getIidmSideFromThreeWtEdgeSide(threeWtEdge.getSide()));
            return getBaseVoltageStyle(terminal);
        }
        return Optional.empty();
    }

    @Override
    protected Optional<String> getBaseVoltageStyle(BranchEdge edge, BranchEdge.Side side) {
        String branchType = edge.getType();
        if (branchType.equals(BranchEdge.DANGLING_LINE_EDGE)) {
            return getBaseVoltageStyle(network.getDanglingLine(edge.getEquipmentId()).getTerminal().getVoltageLevel().getNominalV());
        }
        Terminal terminal = IidmUtils.getTerminalFromEdge(network, edge, side);
        return getBaseVoltageStyle(terminal);
    }

    protected abstract Optional<String> getBaseVoltageStyle(Terminal terminal);

    @Override
    public List<String> getHighlightNodeStyleClasses(Node node) {
        String subnetworkId = network.getIdentifiable(node.getEquipmentId()).getParentNetwork().getId();
        return List.of(subnetworksHighlightMap.get(subnetworkId));
    }

    @Override
    public List<String> getHighlightSideEdgeStyleClasses(BranchEdge edge, BranchEdge.Side side) {
        return getSubnetworkId(edge, side).map(id -> List.of(subnetworksHighlightMap.get(id))).orElse(Collections.emptyList());
    }

    @Override
    public List<String> getHighlightThreeWtEdgStyleClasses(ThreeWtEdge edge) {
        String subnetworkId = getSubnetworkId(edge.getEquipmentId(), edge.getSide());
        return List.of(subnetworksHighlightMap.get(subnetworkId));
    }

    private Optional<String> getSubnetworkId(BranchEdge edge, Side side) {
        final TwoSides iidmSide = IidmUtils.getIidmSideFromBranchEdgeSide(side);
        Terminal terminal = switch (edge.getType()) {
            case BranchEdge.LINE_EDGE -> network.getLine(edge.getEquipmentId()).getTerminal(iidmSide);
            case BranchEdge.TWO_WT_EDGE, BranchEdge.PST_EDGE -> network.getTwoWindingsTransformer(edge.getEquipmentId()).getTerminal(iidmSide);
            case BranchEdge.DANGLING_LINE_EDGE -> network.getDanglingLine(edge.getEquipmentId()).getTerminal();
            case BranchEdge.TIE_LINE_EDGE -> network.getTieLine(edge.getEquipmentId()).getTerminal(iidmSide);
            case BranchEdge.HVDC_LINE_EDGE -> network.getHvdcLine(edge.getEquipmentId()).getConverterStation(iidmSide).getTerminal();
            default -> null;
        };
        return Optional.ofNullable(terminal).map(t -> t.getVoltageLevel().getParentNetwork().getId());
    }

    private String getSubnetworkId(String id, ThreeWtEdge.Side side) {
        return network.getThreeWindingsTransformer(id).getLeg(ThreeSides.valueOf(side.name())).getTerminal().getVoltageLevel().getParentNetwork().getId();
    }

    private void buildSubnetworkMaps() {
        network.getSubnetworks().forEach(n -> subnetworksHighlightMap.put(n.getId(), getHighlightClass(subnetworksHighlightMap.size())));
    }

    private String getHighlightClass(int index) {
        return StyleProvider.HIGHLIGHT_CLASS + "-" + index % 5;
    }

}