# Source code for svir.recovery_modeling.building

    import random
    import math
    from qgis.PyQt.QtCore import QSettings
    from svir.utilities.shared import RECOVERY_DEFAULTS
    from svir.utilities.utils import get_layer_setting

    from svir import IS_SCIPY_INSTALLED
    if IS_SCIPY_INSTALLED:
        from scipy.stats import norm

    class Building(object):

        def __init__(self, iface, inspectionTimes, recoveryTimes, repairTimes,
                     currentDamageStateProbabilities, timeList, assessmentTimes,
                     mobilizationTimes):
            self.inspectionTimes = inspectionTimes
            self.recoveryTimes = recoveryTimes
            self.repairTimes = repairTimes
            self.currentDamageStateProbabilities = currentDamageStateProbabilities
            self.timeList = timeList
            self.assessmentTimes = assessmentTimes
            self.mobilizationTimes = mobilizationTimes
            self.iface = iface
            self.layer = self.iface.activeLayer()
            # PAOLO: how many of these files whould be read from here?
            self.leadTimeDispersion = get_layer_setting(
                self.layer, 'lead_time_dispersion')
            if self.leadTimeDispersion is None:
                self.leadTimeDispersion = float(QSettings().value(
                    'irmt/lead_time_dispersion',
                    RECOVERY_DEFAULTS['lead_time_dispersion']))
            self.repairTimeDispersion = get_layer_setting(
                self.layer, 'repair_time_dispersion')
            if self.repairTimeDispersion is None:
                self.repairTimeDispersion = float(QSettings().value(
                    'irmt/repair_time_dispersion',
                    RECOVERY_DEFAULTS['repair_time_dispersion']))

        def generateBldgLevelRecoveryFunction(self, approach, seed=None):
            if approach == 'Disaggregate':
                return \
                    self._disaggregateBuildingLevelRecoveryFunction(seed)
            elif approach == 'Aggregate':
                return self._aggregateBuildingLevelRecoveryFunction(seed)
            else:
                raise NotImplementedError(approach)

        def _aggregateBuildingLevelRecoveryFunction(self, seed):
            # Simulate lead time dispersions
            if seed is not None:
                random.seed(seed)
            randomNumber = random.random()
            # Simulate lead times
            simulateRecoveryTimes = []
            for i in range(len(self.inspectionTimes)):
                simulateRecoveryTimes.append("")
            # Calculate simulated inspection time/assessment time/mobilization time
            # based on lead time dispersion
            for i in range(len(self.recoveryTimes)):
                if int(self.repairTimes[i]) > 0:
                    simulateRecoveryTimes[i] = math.exp(
                        norm.ppf(randomNumber,
                                 loc=math.log(float(self.recoveryTimes[i])),
                                 scale=self.repairTimeDispersion))
                else:
                    simulateRecoveryTimes[i] = 0
            # Inititalize array that stores the recovery function
            Functionality = [[0 for x in range(len(self.inspectionTimes))]
                             for y in range(len(self.timeList))]
            buildingLevelRecoveryFunction = [0 for x in range(len(self.timeList))]

            # Loop over time array and solve for functionality
            for i in range(len(self.timeList)):
                expectedFunctionality = 0
                # Loop over number of damage states
                for j in range(len(self.inspectionTimes)):
                    if i < simulateRecoveryTimes[j]:
                        Functionality[i][j] = 0
                    else:
                        Functionality[i][j] = 1
                    expectedFunctionality += (
                        Functionality[i][j]
                        * self.currentDamageStateProbabilities[j])
                # Expected functionality
                buildingLevelRecoveryFunction[i] = expectedFunctionality
            return buildingLevelRecoveryFunction

        def _disaggregateBuildingLevelRecoveryFunction(self, seed):
            # PAOLO: moved leadTimeDispersion and repairTimeDispersion to the
            # building init

            # Simulate lead time dispersions
            if seed is not None:
                random.seed(seed)
            randomNumber = random.random()

            # Simulate lead times
            simulateInspectionTimes = []
            simulateAssessmentTimes = []
            simulateMobilizationTimes = []
            simulateRepairTimes = []

            for i in range(len(self.inspectionTimes)):
                simulateInspectionTimes.append("")
                simulateAssessmentTimes.append("")
                simulateMobilizationTimes.append("")
                simulateRepairTimes.append("")

            # Calculate simulated inspection time/assessment time/mobilization time
            # based on lead time dispersion
            for i in range(len(self.inspectionTimes)):
                if int(self.inspectionTimes[i]) > 0:
                    simulateInspectionTimes[i] = math.exp(
                        norm.ppf(randomNumber,
                                 loc=math.log(float(self.inspectionTimes[i])),
                                 scale=self.leadTimeDispersion))
                else:
                    simulateInspectionTimes[i] = 0
                if int(self.assessmentTimes[i]) > 0:
                    simulateAssessmentTimes[i] = math.exp(
                        norm.ppf(randomNumber,
                                 loc=math.log(float(self.assessmentTimes[i])),
                                 scale=self.leadTimeDispersion))
                else:
                    simulateAssessmentTimes[i] = 0
                if int(self.mobilizationTimes[i]) > 0:
                    simulateMobilizationTimes[i] = math.exp(
                        norm.ppf(randomNumber,
                                 loc=math.log(float(self.mobilizationTimes[i])),
                                 scale=self.leadTimeDispersion))
                else:
                    simulateMobilizationTimes[i] = 0
                if int(self.repairTimes[i]) > 0:
                    simulateRepairTimes[i] = math.exp(
                        norm.ppf(randomNumber,
                                 loc=math.log(float(self.repairTimes[i])),
                                 scale=self.repairTimeDispersion))
                else:
                    simulateRepairTimes[i] = 0

            # Inititalize array that stores the recovery function
            Functionality = [[0 for x in range(len(self.inspectionTimes))]
                             for y in range(len(self.timeList))]
            buildingLevelRecoveryFunction = [0 for x in range(len(self.timeList))]

            # Loop over time array and solve for functionality
            for i in range(len(self.timeList)):
                expectedFunctionality = 0
                # Loop over number of damage states
                for j in range(len(self.inspectionTimes)):
                    # Functionality for Inspection damage state
                    if j == 0:
                        Functionality[i][j] = 1
                    elif j == 1:
                        if i < simulateInspectionTimes[j]:
                            Functionality[i][j] = 0
                        else:
                            Functionality[i][j] = 1
                    # Functionality for Loss Function or NotOccupiable damage state
                    elif j == 2 or j == 3:
                        if i < (simulateInspectionTimes[j]
                                + simulateAssessmentTimes[j]
                                + simulateMobilizationTimes[j]
                                + simulateRepairTimes[j]):
                            Functionality[i][j] = 0
                        else:
                            Functionality[i][j] = 1
                    # Functionality for Irreparable damage state
                    elif j == 4:
                        if i < (simulateAssessmentTimes[j] +
                                simulateMobilizationTimes[j] +
                                simulateRepairTimes[j]):
                            Functionality[i][j] = 0
                        else:
                            Functionality[i][j] = 1
                    # Functionality for Collapse damage state
                    elif j == 5:
                        if i < (simulateMobilizationTimes[j] +
                                simulateRepairTimes[j]):
                            Functionality[i][j] = 0
                        else:
                            Functionality[i][j] = 1

                    expectedFunctionality += (
                        Functionality[i][j]
                        * self.currentDamageStateProbabilities[j])
                # Expected functionality
                buildingLevelRecoveryFunction[i] = expectedFunctionality
            return buildingLevelRecoveryFunction