##############################################################################
# Institute for the Design of Advanced Energy Systems Process Systems
# Engineering Framework (IDAES PSE Framework) Copyright (c) 2018-2020, by the
# software owners: The Regents of the University of California, through
# Lawrence Berkeley National Laboratory,  National Technology & Engineering
# Solutions of Sandia, LLC, Carnegie Mellon University, West Virginia
# University Research Corporation, et al. All rights reserved.
#
# Please see the files COPYRIGHT.txt and LICENSE.txt for full copyright and
# license information, respectively. Both files are also available online
# at the URL "https://github.com/IDAES/idaes-pse".
##############################################################################
"""
IDAES Phase objects

Created on Tue Feb 18 10:54:52 2020

@author: alee
"""
from enum import Enum

from pyomo.environ import Set
from pyomo.common.config import ConfigBlock, ConfigValue

from .process_base import (declare_process_block_class,
                           ProcessBlockData)


# Enumerate recognised Phase types
class PhaseType(Enum):
    undefined = 0
    liquidPhase = 1
    vaporPhase = 2
    solidPhase = 3
    aqueousPhase = 4


# TODO: Document EoS options and parameter_Data
@declare_process_block_class("Phase")
class PhaseData(ProcessBlockData):
    CONFIG = ConfigBlock()
    CONFIG.declare("component_list", ConfigValue(
            default=None,
            domain=list,
            description="List of components in phase",
            doc="List of components which are present in phase. This is used "
            "to construct the phase-component Set for the property package."))
    CONFIG.declare("equation_of_state", ConfigValue(
            default=None,
            description="Equation of state for phase",
            doc="""A valid Python class with the necessary methods for
                constructing the desired equation of state (or similar
                model)."""))
    CONFIG.declare("equation_of_state_options", ConfigValue(
            default=None,
            description="Options for equation of state",
            doc="""A dict or ConfigBlock of options to be used when setting
                up equation of state for phase."""))
    CONFIG.declare("parameter_data", ConfigValue(
        default={},
        domain=dict,
        description="Dict containing initialization data for parameters"))
    CONFIG.declare("_phase_list_exists", ConfigValue(
            default=False,
            doc="Internal config argument indicating whether phase_list "
            "needs to be populated."))

    def build(self):
        super(PhaseData, self).build()

        # If the phase_list does not exist, add a reference to the new Phase
        # The IF is mostly for backwards compatability, to allow for old-style
        # property packages where the phase_list already exists but we need to
        # add new Phase objects
        if not self.config._phase_list_exists:
            self.__add_to_phase_list()

    # For the base Phase class, determine phase type based on component name
    # Derived classes will overload these and return the correct type
    # This will handle backwards compatability for old-style property packages
    def is_liquid_phase(self):
        if "Liq" in self.name:
            return True
        else:
            return False

    def is_solid_phase(self):
        if "Sol" in self.name:
            return True
        else:
            return False

    def is_vapor_phase(self):
        if "Vap" in self.name:
            return True
        else:
            return False

    def is_aqueous_phase(self):
        # Returns bool indicating if this phase involve electrolytes
        return False

    def __add_to_phase_list(self):
        """
        Method to add reference to new Phase in phase_list
        """
        parent = self.parent_block()
        try:
            phase_list = getattr(parent, "phase_list")
            phase_list.add(self.local_name)
        except AttributeError:
            # Parent does not have a phase_list yet, so create one
            parent.phase_list = Set(initialize=[self.local_name],
                                    ordered=True)


@declare_process_block_class("LiquidPhase")
class LiquidPhaseData(PhaseData):
    def is_liquid_phase(self):
        return True

    def is_solid_phase(self):
        return False

    def is_vapor_phase(self):
        return False


@declare_process_block_class("SolidPhase")
class SolidPhaseData(PhaseData):
    def is_liquid_phase(self):
        return False

    def is_solid_phase(self):
        return True

    def is_vapor_phase(self):
        return False


@declare_process_block_class("VaporPhase")
class VaporPhaseData(PhaseData):
    def is_liquid_phase(self):
        return False

    def is_solid_phase(self):
        return False

    def is_vapor_phase(self):
        return True


@declare_process_block_class("AqueousPhase")
class AqueousPhaseData(LiquidPhaseData):
    # Special phase type for liquid phases involving electrolytes
    # This is used to determine if we need to do the more complex component
    # list determinations
    def is_aqueous_phase(self):
        return True


# List of all Phase types to use for validation
__all_phases__ = [Phase, LiquidPhase, SolidPhase, VaporPhase, AqueousPhase]
