from math import log

from helix.constants.exposure_category import ExposureCategory
from helix.constants.global_constants import k_zt, k_d


class WindPressureCalculator(object):
    def __init__(self, user_values):
        self.values = user_values

    def q_z(self, k_z):
        v = self.values.wind_speed()
        return 0.00256 * k_z * k_zt * k_d * v ** 2

    def K_z(self):
        height = self.values.building_height()
        exposure = self.values.exposure_category()
        transition_distance = self.values.exposure_category_transition_distance()
        return self.calculate_k_z(height, exposure, transition_distance)

    def calculate_k_z(self, h, exp, transition_distance):
        if exp == ExposureCategory.B and h < 30:
            return 0.7
        elif exp == ExposureCategory.B_C or exp == ExposureCategory.C_B:
            k_zd = self.k_zd(exp, h)
            return k_zd + self.delta_k(exp, transition_distance, k_zd)
        else:
            return 2.01 * (self.h_eff(h, exp) / exp.z_g()) ** (2 / exp.alpha())

    def h_eff(self, h, exposure):
        if (exposure == ExposureCategory.C or exposure == ExposureCategory.D) and h < 15:
            return 15.
        else:
            return h

    def k_zd(self, exp, h):
        if exp == ExposureCategory.B_C:
            h_eff = max(15, h)
            return self.calculate_expression(h_eff, exp.z_g()[1], exp.alpha()[1])
        elif exp == ExposureCategory.C_B and h < 30:
            return 0.7
        else:
            return self.calculate_expression(h, exp.z_g()[1], exp.alpha()[1])

    def delta_k(self, exp, transition_distance, k_zd):
        k_upwind = self.calculate_expression(33, exp.z_g()[0], exp.alpha()[0])
        k_downwind = self.calculate_expression(33, exp.z_g()[1], exp.alpha()[1])
        return (k_upwind - k_downwind) * (k_zd / k_downwind) * self.dk_x(k_upwind, k_downwind, transition_distance / 5280.)

    def dk_x(self, k_upwind, k_downwind, transition_distance_in_miles):
        x0 = 0.621 * 10 ** (-1 * ((k_upwind - k_downwind) ** 2) - 2.3)
        x1 = 6.21 if k_upwind > k_downwind else 62.1
        return log(x1 / transition_distance_in_miles, x1 / x0)

    def calculate_expression(self, height, z_g, alpha):
        return 2.01 * (height / z_g) ** (2 / alpha)
