from pathlib import Path

from lxml import etree as ET
from OMSimulator.cref import CRef
from OMSimulator.values import Values

from OMSimulator import namespace


class Component:
  def __init__(self, name: CRef, fmuPath: Path | str, connectors=None, unitDefinitions=None):
    self.name = CRef(name)
    self.fmuPath = Path(fmuPath)
    self.connectors = connectors or list()
    self.unitDefinitions = unitDefinitions or list()
    self.value = Values() ## TODO propogate Values
    self.parameterResources = []

  def addConnector(self, connector):
    if connector in self.connectors:
      raise ValueError(f"Connector '{connector.name}' already exists in {self.name}")
    self.connectors.append(connector)

  def addSSV(self, resource: str):
    self.parameterResources.append(resource)

  def list(self, prefix=""):
    print(f"{prefix} FMU: ({self.name})")
    prefix += ' |--'
    print(f"{prefix} path: {self.fmuPath}")

    if len(self.connectors) > 0:
      print(f"{prefix} Connectors:")
      for connector in self.connectors:
        connector.list(prefix=prefix + " |--")

    ## list parameters inline
    if not self.value.empty():
      print(f"{prefix} Inline Parameter Bindings:")
      self.value.list(prefix=prefix + " |--")

    ## list parameteres in ssv files
    if len(self.parameterResources) > 0:
      for resource in self.parameterResources:
        print(f"{prefix} Parameter Bindings: {resource}")

  def exportToSSD(self, node):
    component_node = ET.SubElement(node, namespace.tag("ssd", "Component"))
    component_node.set("name", str(self.name))
    component_node.set("type", "application/x-fmu-sharedlibrary")
    component_node.set("source", str(self.fmuPath))

    if len(self.connectors) > 0:
      connectors_node = ET.SubElement(component_node, namespace.tag("ssd", "Connectors"))
      ## export component connectors
      for connector in self.connectors:
        connector.exportToSSD(connectors_node)

    ## export parameter bindings
    self.value.exportToSSD(component_node)

    ## export parameters binding to ssd file with reference to ssv file
    if len(self.parameterResources) > 0:
      for resource in self.parameterResources:
        parameter_bindings_node = ET.SubElement(component_node, namespace.tag("ssd", "ParameterBindings"))
        parameter_binding_node = ET.SubElement(parameter_bindings_node, namespace.tag("ssd", "ParameterBinding"))
        parameter_binding_node.set("source", resource)

  def setValue(self, cref:str, value, unit=None):
    self.value.setValue(cref, value, unit)
