from pydantic.dataclasses import dataclass
from enum import Enum
import typing


class OpticalDataType(Enum):
    DISCRETE = "Discrete"
    BAND = "Band"


class AngularResolutionType(Enum):
    DIRECT = "Direct"
    DIRECT_DIFFUSE = "Direct / Diffuse"
    DIFFUSE_DIFFUSE = "Diffuse / Diffuse"
    BSDF = "BSDF"


INCIDENCE_ANGULAR_RESOLUTION_TYPES = [
    AngularResolutionType.DIRECT,
    AngularResolutionType.BSDF
]

OUTGOING_ANGULAR_RESOLUTION_TYPES = [
    AngularResolutionType.DIRECT,
    AngularResolutionType.BSDF
]


@dataclass
class OpticalProperties:
    optical_data_type: str = OpticalDataType.DISCRETE.name
    incidence_angular_resolution_type: str = AngularResolutionType.DIRECT.name
    outgoing_angular_resolution_type: str = AngularResolutionType.DIRECT.name
    optical_data: dict = None


@dataclass
class OpticalStandardMethodFluxResults:
    direct_direct: float = None
    direct_diffuse: float = None
    direct_hemispherical: float = None
    diffuse_diffuse: float = None
    matrix: typing.List[typing.List[float]] = None


@dataclass
class OpticalStandardMethodResults:
    transmittance_front: OpticalStandardMethodFluxResults = None
    transmittance_back: OpticalStandardMethodFluxResults = None
    reflectance_front: OpticalStandardMethodFluxResults = None
    reflectance_back: OpticalStandardMethodFluxResults = None
    absorptance_front_direct: float = None
    absorptance_back_direct: float = None
    absorptance_front_hemispheric: float = None
    absorptance_back_hemispheric: float = None
    error = None


@dataclass
class ThermalIRResults:
    transmittance_front_diffuse_diffuse: float = None
    transmittance_back_diffuse_diffuse: float = None
    absorptance_front_hemispheric: float = None
    absorptance_back_hemispheric: float = None
    error = None

    @property
    def emissivity_front_hemispheric(self):
        return self.absorptance_front_hemispheric

    @property
    def emissivity_back_hemispheric(self):
        return self.absorptance_back_hemispheric


@dataclass
class TrichromaticResult:
    x: float = None
    y: float = None
    z: float = None


@dataclass
class LabResult:
    l: float = None
    a: float = None
    b: float = None


@dataclass
class RGBResult:
    r: float = None
    g: float = None
    b: float = None


@dataclass
class OpticalColorResult:
    trichromatic: TrichromaticResult = None
    lab: LabResult = None
    rgb: RGBResult = None


@dataclass
class OpticalColorFluxResults:
    direct_direct: OpticalColorResult = None
    direct_diffuse: OpticalColorResult = None
    direct_hemispherical: OpticalColorResult = None
    diffuse_diffuse: OpticalColorResult = None


@dataclass
class OpticalColorResults:
    transmittance_front: OpticalColorFluxResults = None
    transmittance_back: OpticalColorFluxResults = None
    reflectance_front: OpticalColorFluxResults = None
    reflectance_back: OpticalColorFluxResults = None
    error = None


@dataclass
class IntegratedSpectralAveragesSummaryValues:
    solar: OpticalStandardMethodResults = None
    photopic: OpticalStandardMethodResults = None
    thermal_ir: ThermalIRResults = None
    tuv: OpticalStandardMethodResults = None
    spf: OpticalStandardMethodResults = None
    tdw: OpticalStandardMethodResults = None
    tkr: OpticalStandardMethodResults = None
    color: OpticalColorResults = None


class IntegratedSpectralAveragesSummaryValuesFactory:

    @classmethod
    def create(cls) -> IntegratedSpectralAveragesSummaryValues:
        """
        Create a fully initialized instance of IntegratedSpectralAveragesSummaryValues
        """

        summary = IntegratedSpectralAveragesSummaryValues()
        summary.solar = OpticalStandardMethodResultsFactory.create()
        summary.photopic = OpticalStandardMethodResultsFactory.create()
        summary.thermal_ir = ThermalIRResults()
        summary.tuv = OpticalStandardMethodResultsFactory.create()
        summary.spf = OpticalStandardMethodResultsFactory.create()
        summary.tdw = OpticalStandardMethodResultsFactory.create()
        summary.tkr = OpticalStandardMethodResultsFactory.create()
        summary.color = OpticalColorResultsFactory.create()
        return summary


class OpticalStandardMethodResultsFactory:

    @classmethod
    def create(cls) -> OpticalStandardMethodResults:
        """
        Create a fully initialized instance of OpticalStandardMethodResults
        """
        results = OpticalStandardMethodResults()
        results.transmittance_front = OpticalStandardMethodFluxResults()
        results.transmittance_back = OpticalStandardMethodFluxResults()
        results.reflectance_front = OpticalStandardMethodFluxResults()
        results.reflectance_back = OpticalStandardMethodFluxResults()
        return results


class OpticalColorResultFactory:

    @classmethod
    def create(cls) -> OpticalColorResult:
        result = OpticalColorResult()
        result.trichromatic = TrichromaticResult()
        result.rgb = RGBResult()
        result.lab = LabResult()
        return result


class OpticalColorResultsFactory:

    @classmethod
    def create(cls) -> OpticalColorResults:
        """
        Create a fully initialized instance of OpticalColorResult
        """
        results = OpticalColorResults()
        results.transmittance_front = OpticalColorFluxResultsFactory.create()
        results.transmittance_back = OpticalColorFluxResultsFactory.create()
        results.reflectance_front = OpticalColorFluxResultsFactory.create()
        results.reflectance_back = OpticalColorFluxResultsFactory.create()
        return results


class OpticalColorFluxResultsFactory:

    @classmethod
    def create(cls) -> OpticalColorFluxResults:
        """
        Create a fully initialized instance of OpticalColorResult
        """
        results = OpticalColorFluxResults()

        results.direct_direct = OpticalColorResultFactory.create()
        results.direct_diffuse = OpticalColorResultFactory.create()
        results.direct_hemispherical = OpticalColorResultFactory.create()
        results.diffuse_diffuse = OpticalColorResultFactory.create()

        return results
