import csv

Today = input("\nEnter today's date: ")

#-------------------------! ASSUMPTIONS AND ELEMENTS TO BE CHANGED !-------------------------#

ETSWeight = 480 #kg (from Geethu Chandramohan)
AirDensity = 0.225 #kg/m3
PercentageDataElimination = 0.1 #Percentage of flights removed with extreme total energy
AirlineDict = {"BA":"British Airways"}
AircraftModelDict = {"A319-100":["WV010", 62500, 76900, 0.915, 0.5842, 0.051, 122.6, 0.015],\
                     "A320-200":["WV017", 66000, 78400, 0.929, 0.5842, 0.055, 122.6, 0.015],\
                     "A320neo":["WV054", 66300, 79400, 0.926, 0.5842, 0.055, 122.6, 0.015],\
                     "A321-200":["WV011", 77800, 93900, 0.952, 0.6096, 0.069, 122.6, 0.015],\
                     "A321neo":["WV072", 79200, 97400, 0.950, 0.6096, 0.069, 122.6, 0.015]}

#------------------------------------ Program Definition ------------------------------------#

def TaxiAnalysis(airline, model, case):

    #Program setting
    Airline = str(AirlineDict[airline])
    ICAO = airline
    Date = str("01 June 2023")
    AircraftModel = model
    WeightVariant = AircraftModelDict[model][0]
    if case == "Taxi In":
        MaximumWeight = AircraftModelDict[model][1] #kg #Maximum Landing Weight (from reference 12)
    else:
        MaximumWeight = AircraftModelDict[model][2] #kg #Maximum Ramp Weight (from reference 12)
    #PercentageOnMLG = AircraftModelDict[model][3]
    MainGearTireRadius = AircraftModelDict[model][4] #m
    CoefficientOfDrag = AircraftModelDict[model][5] #(from reference 10)
    AreaOfWing = AircraftModelDict[model][6] #m2 (from reference 11)
    TotalWeight = MaximumWeight + ETSWeight
    AircraftWeightForce = TotalWeight*9.80665
    RollingFrictionCoefficient = AircraftModelDict[model][7] #(from reference 7)
    
    import csv
    FileList = []
    FlightDict = {}
    taxitimeList = []
    energyList = []
    FaultCase = 0
    FaultyFlight = []
    FlightRecorded = 0
    
    #Read FileList and read the file names
    try: 
        with open ('#'+ICAO+' '+AircraftModel+' File List.csv','r') as file:
            tempFileList = csv.reader(file)
            for row in tempFileList:
                FileList.append(row)
                
        FileList[0].append("Taxi Time (s)")
        FileList[0].append("Energy (kWh)")
        FileList[0].append("Fault?")
        FileList[0].append("Eliminated?")
        
    except:
        print("There is no " + ICAO+ " " + AircraftModel + " " + case + " files.")
        return True

    #---------------- THIS PART IS ABOUT READING EACH CSV FILES ----------------#
    #--------- CAUTION: THIS PART WILL BE LOOPED CSV NUMBER OF TIMES!! ---------#
    
    #Read every flight file
    for fileSeq in range (1,len(FileList)):
        
        DataList = []
        StartTimeStamp=int(0)
        TotalEnergy = 0
        
        #Read flight csv file and store it to DataList
        with open (FileList[fileSeq][3]+'.csv','r') as file:
            tempFile = csv.reader(file)
            for row in tempFile:
                DataList.append(row)
        
        #Set ending row
        EndRow = int(0)
        
        #Add titles
        DataList[0].append("Time from start")
        DataList[0].append("Speed (m/s)")
        DataList[0].append("Speed (km/h)")
        DataList[0].append("Acc. (m/s2)")
        DataList[0].append("Breakaway (N)")
        DataList[0].append("Acc. Trac. (N)")
        DataList[0].append("Aerod. Drag (N)")
        DataList[0].append("Total Traction (N)")
        DataList[0].append("Torque (Nm)")
        DataList[0].append("Power (kW)")
        DataList[0].append("Energy (kJ)")
        
        #Selecting appropriate data
        if case == "Taxi In":
            #Analyse Taxi In Data, mark starting row & mark ending row
            DataList.reverse()
            for i in range (len(DataList)):
                if int(DataList[i][5]) > 30:
                    EndRow = i
                    StartTimeStamp = int(DataList [i][0])
                    break
            for i in range (len(DataList)):
                if int(DataList[i][5]) <= 30:
                    DataList[i].append(int(DataList[i][0])-StartTimeStamp)
                    DataList[i].append(float(DataList[i][5])*0.514444)
                    DataList[i].append(float(DataList[i][8])*3600/1000)
                else:
                    break
            
            #Delete redundant data
            for i in range (len(DataList)-EndRow-1):
                DataList.remove(DataList[EndRow])
            DataList.reverse()
            
        elif case == "Taxi Out":
            #Analyse Taxi Out Data and mark ending row
            StartTimeStamp = int(DataList [1][0])
            for i in range (1,len(DataList)):
                if int(DataList[i][5]) <= 30:
                    DataList[i].append(int(DataList[i][0])-StartTimeStamp)
                    DataList[i].append(float(DataList[i][5])*0.514444)
                    DataList[i].append(float(DataList[i][8])*3600/1000)
                else:
                    EndRow = i
                    break
            
            #Delete redundant data
            for i in range (len(DataList)-EndRow):
                DataList.remove(DataList[EndRow])
                
        #Add double row
        tempDataList= []
        tempDataList.append(DataList[0][:])
        if len(DataList) > 2:
            tempDataList.append(DataList[1][:])
            for i in range (2,len(DataList)-1):
                tempDataList.append(DataList[i][:])
                tempDataList.append(DataList[i][:])
            tempDataList.append(DataList[len(DataList)-1][:])
            DataList = tempDataList.copy()
        
        #Analyse Acceleration & Breakaway Force
        for i in range (1,len(DataList)):
            try:
                if i%2 == 1:
                    DataList[i].append((DataList[i+1][8]-DataList[i][8])/(float(DataList[i+1][7])-float(DataList[i][7])))
                    DataList[i].append(AircraftWeightForce*RollingFrictionCoefficient)
                else:
                    DataList[i].append((DataList[i][8]-DataList[i-1][8])/(float(DataList[i][7])-float(DataList[i-1][7])))
                    DataList[i].append(AircraftWeightForce*RollingFrictionCoefficient)
            except:
                DataList[i].append(float(0))
                DataList[i].append(AircraftWeightForce*RollingFrictionCoefficient)

        
        #Analyse Accelerative Traction
        for i in range (1,len(DataList)):
            if DataList[i][10] > 0:
                DataList[i].append(TotalWeight*DataList[i][10])
            else:
                DataList[i].append(0)
        
        #Analyse Aerodynamic Resistance, Total Traction, Torque & Power
        for i in range (1,len(DataList)):
            DataList[i].append(0.5*AirDensity*CoefficientOfDrag*AreaOfWing*(DataList[i][8]**2))
            DataList[i].append(DataList[i][11]+DataList[i][12]+DataList[i][13])
            DataList[i].append(DataList[i][14]*MainGearTireRadius)
            DataList[i].append(DataList[i][14]*DataList[i][8]/1000)
            
        #Analyse Energy
        for i in range (1,len(DataList)):
            if i != len(DataList)-1:
                DataList[i].append((DataList[i+1][16]+DataList[i][16])*(float(DataList[i+1][7])-float(DataList[i][7]))/2)
                TotalEnergy += DataList[i][17]/3600
            else:
                DataList[i].append(float(0))
                DataList[i].append(TotalEnergy)
                FileList[fileSeq].append(DataList[i][7])
                FileList[fileSeq].append(TotalEnergy)
                if DataList[i][7] != 0 and TotalEnergy != 0 and DataList[i][7] < 3600:
                    taxitimeList.append(DataList[i][7])
                    energyList.append(TotalEnergy)
                    FlightDict[FileList[fileSeq][4]]=[DataList[i][7],TotalEnergy]
                else:
                    FileList[fileSeq].append("FAULTY")
                    FaultyFlight.append(FileList[fileSeq][4])

        #Create copy of csv file with analysis
        if case == "Taxi In":
            with open ('X(IN)_'+FileList[fileSeq][3]+'.csv','w+', newline='') as file:
                tempFile = csv.writer(file)
                for i in range (len(DataList)):
                    tempFile.writerow(DataList[i])
        else:
            with open ('X(OUT)_'+FileList[fileSeq][3]+'.csv','w+', newline='') as file:
                tempFile = csv.writer(file)
                for i in range (len(DataList)):
                    tempFile.writerow(DataList[i])
        
        FlightRecorded += 1
    
    #---------------------- END OF READING EACH CSV FILES ----------------------#
    
    #Check absolute blank data
    for i in range(1,len(FileList)):
        if len(FileList[i]) == 7:
            FileList[i].append(0)
            FileList[i].append(0)
            FileList[i].append("FAULTY")
            FaultyFlight.append(FileList[i][4])
        #if FileList[i][7] > 3600:
        #    FileList[i].append("FAULTY")
        #    FaultyFlight.append(FileList[i][4])
    
    #Analyse uncorrected average data (uncount fault cases)
    TotalTaxiTime = 0
    AverageTaxiTime = 0
    TotalEnergy = 0
    AverageEnergy = 0
    for i in FlightDict.keys():
        TotalTaxiTime += FlightDict[i][0]
        TotalEnergy += FlightDict[i][1]
    AverageTaxiTime = TotalTaxiTime / (len(FlightDict))
    AverageEnergy = TotalEnergy / (len(FlightDict))
    FaultCase = len(FileList) - 1 - len(FlightDict)
    #print("-------- Uncorrected Results --------")
    #print("Number of faulty flights:",FaultCase)
    #print(FaultyFlight)
    #print("Average Taxi Time is ",round(AverageTaxiTime,2),"seconds.")
    #print("Average Energy is ",round(AverageEnergy,2),"kWh.")
    
    #Analyse elimination flights
    EliminationDict = {}
    #EliminationFlightList = []
    EliminationEnergyList = []
    CorrectedFlightDict = {}
    EliminationNo = round((len(FlightDict)-1)*PercentageDataElimination)
    if EliminationNo % 2 == 1:
        EliminationNo += 1
    energyList.sort()
    for i in range(int(EliminationNo/2)):
        EliminationEnergyList.append(energyList[i])
        EliminationEnergyList.append(energyList[len(energyList)-i-1])
    for i in EliminationEnergyList:
        for j in FlightDict.keys():
            if i == FlightDict[j][1]:
    #            EliminationFlightList.append(FlightDict[j][])
                EliminationDict[j] = [FlightDict[j][0],FlightDict[j][1]]
    for i in EliminationDict.keys():
        for j in range(1,len(FileList)):
            if i == FileList[j][4]:
                FileList[j].append("")
                FileList[j].append("ELIM")
    for j in FlightDict.keys():
        if j not in EliminationDict:
            CorrectedFlightDict[j] = [FlightDict[j][0],FlightDict[j][1]]
    #print("------------ Elimination ------------")
    #print("Number of approved flights:",len(CorrectedFlightDict))
    #print("Number of elimination flights:",len(EliminationDict))
    #print("Is number of flights correct:",len(CorrectedFlightDict)+len(EliminationDict)+FaultCase == FlightRecorded)
    #print(EliminationDict)
    
    #Analyse corrected average data
    EliminationTaxiTime = 0
    EliminationEnergy = 0
    CorrectedAverageTaxiTime = 0
    CorrectedAverageEnergy = 0
    for i in EliminationDict.keys():
        EliminationTaxiTime += EliminationDict[i][0]
        EliminationEnergy += EliminationDict[i][1]  
    CorrectedAverageTaxiTime = (TotalTaxiTime - EliminationTaxiTime) / (len(CorrectedFlightDict))
    CorrectedAverageEnergy = (TotalEnergy - EliminationEnergy) / (len(CorrectedFlightDict))
    #print("--------- Corrected Results ---------")
    #print("Average Taxi Time is ",round(CorrectedAverageTaxiTime,2),"seconds.")
    #print("Average Energy is ",round(CorrectedAverageEnergy,2),"kWh.")
    
    FileList.append(["","","","","","","Average",round(AverageTaxiTime,2),round(AverageEnergy,2)])
    FileList.append(["","","","","","","Corrected Average",round(CorrectedAverageTaxiTime,2),round(CorrectedAverageEnergy,2)])
    
    FinalList.append([airline, AirlineDict[airline], model, case, round(CorrectedAverageTaxiTime,2), round(CorrectedAverageTaxiTime/60,2), round(CorrectedAverageEnergy,2)])
    if model == FinalList[len(FinalList)-2][2]:
        TimeS = round(FinalList[len(FinalList)-1][4] + FinalList[len(FinalList)-2][4],2)
        TimeM = round(FinalList[len(FinalList)-1][5] + FinalList[len(FinalList)-2][5],2)
        TotEnergy = round(FinalList[len(FinalList)-1][6] + FinalList[len(FinalList)-2][6],2)
        FinalList.append([airline, AirlineDict[airline], model, "Total Taxi", TimeS, TimeM, TotEnergy])
    
    #Create copy of FileList with analysis
    with open ('#'+ICAO+' '+AircraftModel+' '+case+' Flight List.csv','w+', newline='') as file:
        tempFileList = csv.writer(file)
        for i in range (len(FileList)):
            tempFileList.writerow(FileList[i])
    
    #Generate Report
    Report = "REPORT OF "+Airline.upper()+" "+AircraftModel.upper()+" AIRCRAFTS "+case.upper()+" CASES INVESTIGATION ON TAXI TIME AND ENERGY REQUIRED FOR "+Date+" OPERATION"+"\n\nThis is an "\
        "automatically generated report for Individual Research Project of CHI HIN PANG from Cranfield University "\
        "under the topic of Electrically Powered On-board Taxi System. All rights reserved. No part of this publication may be "\
        "reproduced, distributed, or transmitted in any form or by any means, including photocopying, recording, or other "\
        "electronic or mechanical methods, without the prior written permission of the publisher. \n\nThis report is generated on " + Today + "\n\n"\
        "------------------------- BACKGROUND STATISTICS -------------------------\n\nAirline: "+Airline+"\nICAO Code: "+ICAO+"\nAircraft Model: "+AircraftModel+\
        "\nWeight Varient: "+WeightVariant+" ("+str(MaximumWeight)+"kg)\nDatum Date of Investigation: "+Date+"\nAssumption of ETS Weight: "+str(ETSWeight)+"kg\nPercentage "\
        "of Extreme Data Elimination: "+str(PercentageDataElimination*100)+"%\n\n-------------------------- UNCORRECTED RESULT ---------------------------\n\n"\
        "Number of Recorded Flights: "+str(FlightRecorded)+"\n\nNumber of Faulty Flights: "+str(FaultCase)+"\nList of Faulty Flights: "+str(FaultyFlight)+"\n\nAverage Taxi Time: "\
        +str(round(AverageTaxiTime,2))+"secs ("+str(round(AverageTaxiTime/60,2))+"mins)\nAverage Energy: "+str(round(AverageEnergy,2))+"kWh\n\n------------------------------ ELIMINATION ------------------------------\n\nNumber of Approved Flights: "\
        +str(len(CorrectedFlightDict))+"\nList of Approved Flights: "+str(list(CorrectedFlightDict.keys()))+"\n\nNumber of Elimination Flights: "+str(len(EliminationDict))+"\nList of Elimination Flights: "\
        +str(list(EliminationDict.keys()))+"\n\nIs number of flights correct: "+str(len(CorrectedFlightDict)+len(EliminationDict)+FaultCase == FlightRecorded)+"\n\n--------------------------- CORRECTED RESULT "\
        "----------------------------\n\nCorrected Average Taxi Time: "+str(round(CorrectedAverageTaxiTime,2))+"secs ("+str(round(CorrectedAverageTaxiTime/60,2))+"mins)\nCorrected Average Energy: "+str(round(CorrectedAverageEnergy,2))+"kWh\n"\
        "Difference in Taxi Time: "+str(round((CorrectedAverageTaxiTime-AverageTaxiTime)/AverageTaxiTime*100,2))+"%\nDifference in Energy: "+str(round((CorrectedAverageEnergy-AverageEnergy)/AverageEnergy*100,2))+"%"+\
        "\n\n----------------------------- END OF REPORT -----------------------------\n\n"
    
    with open ('#'+ICAO+' '+AircraftModel+' '+case+' Report.doc','w+') as file:
        file.write(Report)
 

#------------------------------------ Program Execution ------------------------------------#

FinalList = [["ICAO", "Airline", "Aircraft Model", "Case", "Average Taxi Time (s)", "Average Taxi Time (min)", "Average Energy (kWh)"]]

for airline in AirlineDict.keys():
    for model in AircraftModelDict.keys():
        TaxiAnalysis(airline,model,"Taxi Out")
        TaxiAnalysis(airline,model,"Taxi In")
    
    #Generate Final Report
    FinalReport = "REPORT OF "+AirlineDict[airline].upper()+" AIRCRAFTS INVESTIGATION ON TAXI TIME AND ENERGY REQUIRED\n\nThis is an "\
        "automatically generated report for Individual Research Project of CHI HIN PANG from Cranfield University "\
        "under the topic of Electrically Powered On-board Taxi System. All rights reserved. No part of this publication may be "\
        "reproduced, distributed, or transmitted in any form or by any means, including photocopying, recording, or other "\
        "electronic or mechanical methods, without the prior written permission of the publisher. \n\nThis report is generated on " + Today + "\n\n"\
        "----------------------------------------------------------------------\n\n"
    
    for i in range(1,len(FinalList)):
        FinalReport += "Case: "+FinalList[i][1]+" "+FinalList[i][2]+" "+FinalList[i][3]+"\nAverage Taxi Time: "+str(FinalList[i][4])+"secs ("+str(FinalList[i][5])+\
            "mins)\nAverage Energy: "+str(FinalList[i][6])+"kWh\n\n"
        if FinalList[i][3] == "Total Taxi":
            FinalReport += "----------------------------------------------------------------------\n\n"

    with open ('#'+airline+' Final Report.doc','w+') as file:
        file.write(FinalReport)
    
    #Generate Final csv File
    with open ('#'+airline+' Final List.csv','w+', newline='') as file:
        tempFileList = csv.writer(file)
        for i in range (len(FinalList)):
            tempFileList.writerow(FinalList[i])
    
    
        

