from __future__ import annotations

from enum import Enum
from typing import Optional, Tuple, overload

import numpy as np

class NodeType(Enum):
    DIFFUSIVE = ...
    BOUNDARY = ...

class Node:
    def __init__(self, node: int | Node) -> None: ...
    @property
    def node_num(self) -> int: ...
    @node_num.setter
    def node_num(self, value: int) -> None: ...
    @property
    def type(self) -> NodeType: ...
    @type.setter
    def type(self, value: NodeType) -> None: ...
    @property
    def T(self) -> float: ...
    @T.setter
    def T(self, value: float) -> None: ...
    @property
    def C(self) -> float: ...
    @C.setter
    def C(self, value: float) -> None: ...
    @property
    def qs(self) -> float: ...
    @qs.setter
    def qs(self, value: float) -> None: ...
    @property
    def qa(self) -> float: ...
    @qa.setter
    def qa(self, value: float) -> None: ...
    @property
    def qe(self) -> float: ...
    @qe.setter
    def qe(self, value: float) -> None: ...
    @property
    def qi(self) -> float: ...
    @qi.setter
    def qi(self, value: float) -> None: ...
    @property
    def qr(self) -> float: ...
    @qr.setter
    def qr(self, value: float) -> None: ...
    @property
    def a(self) -> float: ...
    @a.setter
    def a(self, value: float) -> None: ...
    @property
    def fx(self) -> float: ...
    @fx.setter
    def fx(self, value: float) -> None: ...
    @property
    def fy(self) -> float: ...
    @fy.setter
    def fy(self, value: float) -> None: ...
    @property
    def fz(self) -> float: ...
    @fz.setter
    def fz(self, value: float) -> None: ...
    @property
    def eps(self) -> float: ...
    @eps.setter
    def eps(self, value: float) -> None: ...
    @property
    def aph(self) -> float: ...
    @aph.setter
    def aph(self, value: float) -> None: ...
    @property
    def literal_C(self) -> float: ...
    @literal_C.setter
    def literal_C(self, value: float) -> None: ...
    def int_node_num(self) -> int: ...
    def parent_pointer(self) -> Optional[Nodes]: ...
    def parent_pointer_address(self) -> int: ...

class Nodes:
    estimated_number_of_nodes: int

    def __init__(self) -> None: ...
    def add_node(self, node: Node) -> None: ...
    def remove_node(self, node_num: int) -> None: ...
    def is_node(self, node_num: int) -> bool: ...
    def get_type(self, node_num: int) -> NodeType: ...
    def set_type(self, node_num: int, node_type: NodeType) -> None: ...
    def get_T(self, node_num: int) -> float: ...
    def set_T(self, node_num: int, value: float) -> None: ...
    def get_C(self, node_num: int) -> float: ...
    def set_C(self, node_num: int, value: float) -> None: ...
    def get_qs(self, node_num: int) -> float: ...
    def set_qs(self, node_num: int, value: float) -> None: ...
    def get_qa(self, node_num: int) -> float: ...
    def set_qa(self, node_num: int, value: float) -> None: ...
    def get_qe(self, node_num: int) -> float: ...
    def set_qe(self, node_num: int, value: float) -> None: ...
    def get_qi(self, node_num: int) -> float: ...
    def set_qi(self, node_num: int, value: float) -> None: ...
    def get_qr(self, node_num: int) -> float: ...
    def set_qr(self, node_num: int, value: float) -> None: ...
    def get_a(self, node_num: int) -> float: ...
    def set_a(self, node_num: int, value: float) -> None: ...
    def get_fx(self, node_num: int) -> float: ...
    def set_fx(self, node_num: int, value: float) -> None: ...
    def get_fy(self, node_num: int) -> float: ...
    def set_fy(self, node_num: int, value: float) -> None: ...
    def get_fz(self, node_num: int) -> float: ...
    def set_fz(self, node_num: int, value: float) -> None: ...
    def get_eps(self, node_num: int) -> float: ...
    def set_eps(self, node_num: int, value: float) -> None: ...
    def get_aph(self, node_num: int) -> float: ...
    def set_aph(self, node_num: int, value: float) -> None: ...
    def get_literal_C(self, node_num: int) -> float: ...
    def set_literal_C(self, node_num: int, literal: float) -> None: ...
    def get_idx_from_node_num(self, node_num: int) -> int: ...
    def get_node_num_from_idx(self, idx: int) -> int: ...
    def get_node_from_node_num(self, node_num: int) -> Node: ...
    def get_node_from_idx(self, idx: int) -> Node: ...
    @property
    def num_nodes(self) -> int: ...
    @property
    def num_diff_nodes(self) -> int: ...
    @property
    def num_bound_nodes(self) -> int: ...
    def is_mapped(self) -> bool: ...
    def get_T_value_pointer(self, node_num: int) -> int: ...
    def get_C_value_pointer(self, node_num: int) -> int: ...
    def get_qs_value_pointer(self, node_num: int) -> int: ...
    def get_qa_value_pointer(self, node_num: int) -> int: ...
    def get_qe_value_pointer(self, node_num: int) -> int: ...
    def get_qi_value_pointer(self, node_num: int) -> int: ...
    def get_qr_value_pointer(self, node_num: int) -> int: ...
    def get_a_value_pointer(self, node_num: int) -> int: ...
    def get_fx_value_pointer(self, node_num: int) -> int: ...
    def get_fy_value_pointer(self, node_num: int) -> int: ...
    def get_fz_value_pointer(self, node_num: int) -> int: ...
    def get_eps_value_pointer(self, node_num: int) -> int: ...
    def get_aph_value_pointer(self, node_num: int) -> int: ...

class Coupling:
    def __init__(self, node_1: int, node_2: int, value: float) -> None: ...
    @property
    def node_1(self) -> int: ...
    @node_1.setter
    def node_1(self, value: int) -> None: ...
    @property
    def node_2(self) -> int: ...
    @node_2.setter
    def node_2(self, value: int) -> None: ...
    @property
    def value(self) -> float: ...
    @value.setter
    def value(self, value: float) -> None: ...

class CouplingMatrices:
    def __init__(self) -> None: ...
    @property
    def num_diff_nodes(self) -> int: ...
    @property
    def num_bound_nodes(self) -> int: ...
    @property
    def num_nodes(self) -> int: ...
    def add_ovw_coupling_from_node_idxs(
        self, idx1: int, idx2: int, value: float
    ) -> None: ...
    def add_ovw_coupling_from_node_idxs_verbose(
        self, idx1: int, idx2: int, value: float
    ) -> None: ...
    def add_sum_coupling_from_node_idxs(
        self, idx1: int, idx2: int, value: float
    ) -> None: ...
    def add_sum_coupling_from_node_idxs_verbose(
        self, idx1: int, idx2: int, value: float
    ) -> None: ...
    def add_new_coupling_from_node_idxs(
        self, idx1: int, idx2: int, value: float
    ) -> None: ...
    def get_conductor_value_from_idx(self, idx1: int, idx2: int) -> float: ...
    def set_conductor_value_from_idx(
        self, idx1: int, idx2: int, value: float
    ) -> None: ...
    def get_conductor_value_pointer_from_idx(self, idx1: int, idx2: int) -> int: ...
    def get_conductor_value_address_from_idx(self, idx1: int, idx2: int) -> int: ...
    def sparse_dd_copy(self) -> np.ndarray: ...
    def sparse_db_copy(self) -> np.ndarray: ...
    def sparse_bb_copy(self) -> np.ndarray: ...
    @property
    def num_diff_diff_couplings(self) -> int: ...
    @property
    def num_diff_bound_couplings(self) -> int: ...
    @property
    def num_bound_bound_couplings(self) -> int: ...
    @property
    def num_total_couplings(self) -> int: ...
    def get_idxs_and_coupling_value_from_coupling_idx(
        self, coupling_idx: int
    ) -> Tuple[int, int, float]: ...
    def coupling_exists_from_idxs(self, idx1: int, idx2: int) -> bool: ...
    def print_sparse(self) -> None: ...
    def reserve(self, nnz: int) -> None: ...

class Couplings:
    def __init__(self, nodes: Nodes) -> None: ...
    def get_coupling_matrices(self) -> CouplingMatrices: ...
    def get_coupling_value(self, node_num_1: int, node_num_2: int) -> float: ...
    def set_coupling_value(
        self, node_num_1: int, node_num_2: int, value: float
    ) -> None: ...
    @overload
    def add_ovw_coupling(
        self, node_num_1: int, node_num_2: int, value: float
    ) -> None: ...
    @overload
    def add_ovw_coupling(self, coupling: Coupling) -> None: ...
    @overload
    def add_ovw_coupling_verbose(
        self, node_num_1: int, node_num_2: int, value: float
    ) -> None: ...
    @overload
    def add_ovw_coupling_verbose(self, coupling: Coupling) -> None: ...
    @overload
    def add_sum_coupling(
        self, node_num_1: int, node_num_2: int, value: float
    ) -> None: ...
    @overload
    def add_sum_coupling(self, coupling: Coupling) -> None: ...
    @overload
    def add_sum_coupling_verbose(
        self, node_num_1: int, node_num_2: int, value: float
    ) -> None: ...
    @overload
    def add_sum_coupling_verbose(self, coupling: Coupling) -> None: ...
    @overload
    def add_new_coupling(
        self, node_num_1: int, node_num_2: int, value: float
    ) -> None: ...
    @overload
    def add_new_coupling(self, coupling: Coupling) -> None: ...
    @overload
    def add_coupling(self, node_num_1: int, node_num_2: int, value: float) -> None: ...
    @overload
    def add_coupling(self, coupling: Coupling) -> None: ...
    def get_coupling_value_pointer(self, node_num_1: int, node_num_2: int) -> int: ...
    def get_coupling_value_address(self, node_num_1: int, node_num_2: int) -> int: ...
    def coupling_exists(self, node_num_1: int, node_num_2: int) -> bool: ...
    def get_coupling_from_coupling_idx(self, idx: int) -> Coupling: ...

class ConductiveCouplings:
    def __init__(self, nodes: Nodes) -> None: ...
    @overload
    def add_coupling(self, node_num_1: int, node_num_2: int, value: float) -> None: ...
    @overload
    def add_coupling(self, coupling: Coupling) -> None: ...
    def set_coupling_value(
        self, node_num_1: int, node_num_2: int, value: float
    ) -> None: ...
    def get_coupling_value(self, node_num_1: int, node_num_2: int) -> float: ...

class RadiativeCouplings:
    def __init__(self, nodes: Nodes) -> None: ...
    @overload
    def add_coupling(self, node_num_1: int, node_num_2: int, value: float) -> None: ...
    @overload
    def add_coupling(self, coupling: Coupling) -> None: ...
    def set_coupling_value(
        self, node_num_1: int, node_num_2: int, value: float
    ) -> None: ...
    def get_coupling_value(self, node_num_1: int, node_num_2: int) -> float: ...

class ThermalNetwork:
    @overload
    def __init__(self) -> None: ...
    @overload
    def __init__(
        self,
        nodes: Nodes,
        conductive: ConductiveCouplings,
        radiative: RadiativeCouplings,
    ) -> None: ...
    def add_node(self, node: Node) -> None: ...
    def remove_node(self, node_num: int) -> None: ...
    @property
    def nodes(self) -> Nodes: ...
    @property
    def conductive_couplings(self) -> ConductiveCouplings: ...
    @property
    def radiative_couplings(self) -> RadiativeCouplings: ...
    @property
    def nodes_ptr(self) -> Optional[Nodes]: ...
