
"""
Author: M. Graham Macy
Date Created: 6/4/2024
Date Edited: 8/5/2024

Description:

Contains Operations class, Policy enum,
Maint_type enum, operationalData class.

"""
from aircraft import *
from airports import *
from fleet import *
from constants import *
from plot import *
import pandas as pd
import numpy as np
from enum import Enum

class Policy(Enum):
    NO_POLICY = 1
    RANGE_MAX = 2
    RANGE_MIN = 3

class Maint_Type(Enum):
    FAN_REPLACEMENT = 1
    CORE_REPLACEMENT = 2
    LPT_REPLACEMENT = 3
    EGT_RESTORATION = 4

class operationalData:
    total_aog_time: int = 0
    avg_fleet_EGTM: list = []
    aclat: list = []
    aclon: list = []
    maint_log: list = []


class Flight_Operations():

    """
    Upon initialization:
        Takes in fleet object (see fleet object info)
        Time (hrs since midnight, Jan 1)
        airport filename (a csv file containing airport information)
        schedule filename (a schedule of flights)

    Functions in Flight_operations include:

        operate()
            Takes in a flight operational policy as an Enum
            If schedule is not fixed, function
            
            Makes decisions about where aircraft should go based on flight
            operational policy.
            -these policies can include time of day flights are flying
            -these can include decisions about fuel prices
            
            Returns data as a operationalData object
    """

    fleet: Fleet
    time: int
    airport_filename: str
    schedule_filename: str
    simulation_days: int
    simulation_start_time: int

    def __init__(self,
                 fleet: Fleet,
                 time: int,
                 airport_filename:str,
                 simulation_days: int,
                 simulation_start_time: int):
        
        self.fleet = fleet
        self.time = time
        self.airport_filename = airport_filename
        self.simulation_days = simulation_days
        self.simulation_start_time = simulation_start_time

        return

    def operate(self,
                policy: Policy) -> operationalData:
        """
        Takes in a filename for list of airports

        Runs flight simulations for a fleet for designated number of days
        (The fleet will have to be initialized when class is initialized)

        Runs with certain policy, which is enumerated in the Policy class.

        Start time will be hours after midnight that the simulation starts

        Max operational time is the number of hours an airplane may operate
        in a day.

        Returns data as an operationalData type
        """

        airports = Airports.create_AirportList(self.airport_filename)
        
        simulation_hours = simulation_days * hrs_per_day

        data = operationalData()

        # starts simulation time at stated beginning of operation time
        self.time = start_time

        while self.time < simulation_hours:

            for ac in self.fleet:

                daily_op_time = 0

                while daily_op_time < max_daily_op_hrs:

                    destination = random.choice(airports)

                    flight_time_hrs = ac.flight_time(destination)
                    distance_nmi = ac.flight_distance(destination)

                    if policy == Policy.RANGE_MAX:
                        # choose a different random destination until
                        # the a/c can fly somewhere what maximizes range
                        count = 1
                        while (distance_nmi < range_max_nmi or
                                (destination.code == ac.location.code)):  # and \
                        # (count < 10):
                            destination = random.choice(airports)
                            distance_nmi = \
                                ac.flight_distance(destination)
                            count += 1

                    elif policy == Policy.RANGE_MIN:
                        count = 1
                        while ((distance_nmi > range_min_nmi) or
                            (destination.code == ac.location.code)) and \
                            (count < 10):
                            destination = random.choice(airports)
                            distance_nmi = \
                                ac.flight_distance(destination)
                            count += 1

                    # check to see if the ac is grounded due to eng maint
                    go_fly, aog_time, maint_reason = self.go_no_go(ac)

                    if go_fly == False:
                        data.maint_log.append(maint_reason)
                        data.total_aog_time += aog_time
                        daily_op_time += aog_time
                    else:
                        takeoff_time = self.time + daily_op_time
                        ac.fly(destination, takeoff_time)
                        data.aclat.append(ac.location.lat)
                        data.aclon.append(ac.location.lon)

                        daily_op_time += flight_time_hrs + ground_time_hrs

                # print(ac.log)

            # print(f"====================End day {ceil(self.time/24)}")
    
            data.avg_fleet_EGTM.append(Fleet.avg_EGTM(self.fleet))

            self.time += hrs_per_day

        return data

    def go_no_go(self, aircraft: Aircraft) -> bool | int | list:
        """
        The decision to go or not depending on engine only
        If the decision is a no, engines are swapped and
        time is forwarded a few hours.
        """
        go_fly = True
        aog_time = 0  # aircraft on ground
        maint_reason = False

        if aircraft.l_engine.EGTM_degC < EGTM_limit_degC or \
                aircraft.l_engine.core_CR < cr_threshold or \
                aircraft.l_engine.fan_CR < cr_threshold or \
                aircraft.l_engine.lpt_CR < cr_threshold:
            if aircraft.l_engine.EGTM_degC < EGTM_limit_degC:
                print(f"{aircraft.l_engine.esn} has low EGTM")
                maint_reason = Maint_Type.EGT_RESTORATION.value
            elif aircraft.l_engine.core_CR < cr_threshold:
                print(f"{aircraft.l_engine.esn} has low core C/R")
                maint_reason = Maint_Type.CORE_REPLACEMENT.value
            elif aircraft.l_engine.fan_CR < cr_threshold:
                print(f"{aircraft.l_engine.esn} has low fan or LPC C/R")
                maint_reason = Maint_Type.FAN_REPLACEMENT.value
            elif aircraft.l_engine.lpt_CR < cr_threshold:
                print(f"{aircraft.l_engine.esn} has low LPT C/R")
                maint_reason = Maint_Type.LPT_REPLACEMENT.value
            aircraft.l_engine = Engines.create_Engine(
                esn="ESN0" + str(self.time) + "L")
            # print("L engine is being swapped, aircraft grounded")
            go_fly = False
            aog_time = 3

        if aircraft.r_engine.EGTM_degC < EGTM_limit_degC or \
                aircraft.r_engine.core_CR < cr_threshold or \
                aircraft.r_engine.fan_CR < cr_threshold or \
                aircraft.r_engine.lpt_CR < cr_threshold:
            if aircraft.r_engine.EGTM_degC < EGTM_limit_degC:
                print(f"{aircraft.r_engine.esn} has low EGTM")
                maint_reason = Maint_Type.EGT_RESTORATION.value
            elif aircraft.r_engine.core_CR < cr_threshold:
                print(f"{aircraft.r_engine.esn} has low core C/R")
                maint_reason = Maint_Type.CORE_REPLACEMENT.value
            elif aircraft.r_engine.fan_CR < cr_threshold:
                print(f"{aircraft.r_engine.esn} has low fan or LPC C/R")
                maint_reason = Maint_Type.FAN_REPLACEMENT.value
            elif aircraft.r_engine.lpt_CR < cr_threshold:
                print(f"{aircraft.r_engine.esn} has low LPT C/R")
                maint_reason = Maint_Type.LPT_REPLACEMENT.value
            aircraft.r_engine = Engines.create_Engine(
                esn="ESN0" + str(self.time) + "R")
            # print("R engine is being swapped, aircraft grounded")
            go_fly = False
            aog_time = engine_swap_aog_hrs

        return go_fly, aog_time, maint_reason


if __name__ == "__main__":
    schedule_filename = schedule_filename
    airport_filename = airport_filename
    num_fleet = 10
    simulation_days = 100
    policy = Policy.RANGE_MAX
    start_time = 6

    fleet = Fleet.create_Fleet(num_fleet, airport_filename, Fleet_Locations.RANDOM_LOC)
    ops = Flight_Operations(fleet=fleet,
                            time=0,
                            airport_filename=schedule_filename,
                            simulation_days=simulation_days,
                            simulation_start_time=start_time
                            )
    data =  ops.operate(
            policy=policy,
            )
    
    print(data.avg_fleet_EGTM)
    plot_avgEGTM(data)
    