# step7_fuse.py — one class for every signal, and a coordinator that joins them.
# Five signals, five scores. The coordinator does not add them. It multiplies.
# New here: the Signal class, fuse() and decide().

CRITICAL = 3.70   # at 3.70 m the Irrigation Office warns the city
PRIOR = 0.10      # our starting guess for a flood tomorrow, before we look at any signal


def ramp(value, low, high):
    """0 below low, 1 above high, a straight line between them."""
    if value <= low:
        return 0.0
    if value >= high:
        return 1.0
    return (value - low) / (high - low)


def rain_score(mm):
    return ramp(mm, 20, 80)


def river_score(metres):
    return ramp(metres, 3.0, CRITICAL)


def forecast_score(metres):
    return ramp(metres, 3.3, 3.9)


def satellite_score(fraction):
    return ramp(fraction, 0.05, 0.30)


def archive_score(shared_words):
    return ramp(shared_words, 1, 4)


# The kind of a signal picks the scorer. Same class, different job.
SCORERS = {"rain": rain_score, "river": river_score, "forecast": forecast_score,
           "satellite": satellite_score, "archive": archive_score}


class Signal:
    """One signal. The kind says how its value becomes a score."""

    def __init__(self, kind, value):
        self.kind = kind
        self.value = value

    def score(self):
        """0 is calm, 1 is danger. A missing value is 0.5: it says nothing."""
        if self.value is None:
            return 0.5
        return SCORERS[self.kind](self.value)

    def weight(self):
        """How much this signal multiplies the odds. Score 0.5 gives x1: no change."""
        return 10 ** (2 * self.score() - 1)


def fuse(signals, prior):
    """Start with the prior. Multiply the odds by every signal. Give back a chance."""
    odds = prior / (1 - prior)
    for signal in signals:
        odds = odds * signal.weight()
    return odds / (1 + odds)


def decide(chance, threshold):
    if chance >= threshold:
        return "RED - warn people now"
    if chance >= threshold / 2:
        return "YELLOW - watch closely"
    return "GREEN - calm"


def main():
    # The morning of 24 September 2024 in Chiang Mai. No satellite picture yet.
    signals = [Signal("rain", 98), Signal("river", 3.34), Signal("forecast", 3.63),
               Signal("satellite", None), Signal("archive", 2)]
    for signal in signals:
        print(f"{signal.kind:10} value {str(signal.value):>5}   "
              f"score {signal.score():.2f}   odds x{signal.weight():.2f}")
    chance = fuse(signals, PRIOR)
    print(f"chance of a flood tomorrow: {chance:.0%}")
    print(decide(chance, 0.5))


if __name__ == "__main__":
    main()
