// ignore_for_file: non_constant_identifier_names

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:jiffy/jiffy.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
//import 'package:supabase_flutter/supabase_flutter.dart';

import "package:tp5/core/utils.dart";
import "package:tp5/csv/csv_data.dart";

class CrewFilter {
  String? college;
  String? ac;
  String? tlc;
  String? fname;
  String? lname;
  String? searchname;
  CrewFilter({
    this.college,
    this.ac,
    this.tlc,
    this.fname,
    this.lname,
    this.searchname,
  });

  CrewFilter copyWith({
    ValueGetter<String?>? college,
    ValueGetter<String?>? ac,
    ValueGetter<String?>? tlc,
    ValueGetter<String?>? fname,
    ValueGetter<String?>? lname,
    ValueGetter<String?>? searchname,
  }) {
    return CrewFilter(
      college: college != null ? college() : this.college,
      ac: ac != null ? ac() : this.ac,
      tlc: tlc != null ? tlc() : this.tlc,
      fname: fname != null ? fname() : this.fname,
      lname: lname != null ? lname() : this.lname,
      searchname: searchname != null ? searchname() : this.searchname,
    );
  }

  Map<String, dynamic> toMap() {
    return {
      'college': college,
      'ac': ac,
      'tlc': tlc,
      'fname': fname,
      'lname': lname,
      'searchname': searchname,
    };
  }

  factory CrewFilter.fromMap(Map<String, dynamic> map) {
    return CrewFilter(
      college: map['college'],
      ac: map['ac'],
      tlc: map['tlc'],
      fname: map['fname'],
      lname: map['lname'],
      searchname: map['searchname'],
    );
  }

  String toJson() => json.encode(toMap());

  factory CrewFilter.fromJson(String source) =>
      CrewFilter.fromMap(json.decode(source));

  @override
  String toString() {
    return 'CrewFilter(college: $college, ac: $ac, tlc: $tlc, fname: $fname, lname: $lname, searchname: $searchname)';
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;

    return other is CrewFilter &&
        other.college == college &&
        other.ac == ac &&
        other.tlc == tlc &&
        other.fname == fname &&
        other.lname == lname &&
        other.searchname == searchname;
  }

  @override
  int get hashCode {
    return college.hashCode ^
        ac.hashCode ^
        tlc.hashCode ^
        fname.hashCode ^
        lname.hashCode ^
        searchname.hashCode;
  }
}

class FlightFilter {
  String? al;
  String? fnum;
  String? date;
  String? dep;
  String? arr;
  Jiffy? jdep;
  Jiffy? jarr;
  String? tlc;
  String? reg;
  FlightFilter(
      {this.al,
      this.fnum,
      this.date,
      this.dep,
      this.arr,
      this.jdep,
      this.jarr,
      this.tlc,
      this.reg});
  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;

    return other is FlightFilter &&
        other.al == al &&
        other.fnum == fnum &&
        other.date == date &&
        other.dep == dep &&
        other.arr == arr &&
        other.jdep == jdep &&
        other.jarr == jarr &&
        other.tlc == tlc &&
        other.reg == reg;
  }

  FlightFilter copyWith({
    ValueGetter<String?>? al,
    ValueGetter<String?>? fnum,
    ValueGetter<String?>? date,
    ValueGetter<String?>? dep,
    ValueGetter<String?>? arr,
    ValueGetter<Jiffy?>? jdep,
    ValueGetter<Jiffy?>? jarr,
    ValueGetter<String?>? tlc,
    ValueGetter<String?>? reg,
  }) {
    return FlightFilter(
      al: al != null ? al() : this.al,
      fnum: fnum != null ? fnum() : this.fnum,
      date: date != null ? date() : this.date,
      dep: dep != null ? dep() : this.dep,
      arr: arr != null ? arr() : this.arr,
      jdep: jdep != null ? jdep() : this.jdep,
      jarr: jarr != null ? jarr() : this.jarr,
      tlc: tlc != null ? tlc() : this.tlc,
      reg: reg != null ? reg() : this.reg,
    );
  }

  Map<String, dynamic> toMap() {
    return {
      'al': al,
      'fnum': fnum,
      'date': date,
      'dep': dep,
      'arr': arr,
      'jdep': jdep?.toString(),
      'jarr': jarr?.toString(),
      'tlc': tlc,
      'reg': reg,
    };
  }

  factory FlightFilter.fromMap(Map<String, dynamic> map) {
    return FlightFilter(
      al: map['al'],
      fnum: map['fnum'],
      date: map['date'],
      dep: map['dep'],
      arr: map['arr'],
      jdep: map['jdep'] != null ? Jiffy.parse(map['jdep']) : null,
      jarr: map['jarr'] != null ? Jiffy.parse(map['jarr']) : null,
      tlc: map['tlc'],
      reg: map['reg'],
    );
  }

  String toJson() => json.encode(toMap());

  factory FlightFilter.fromJson(String source) =>
      FlightFilter.fromMap(json.decode(source));

  @override
  String toString() {
    return 'FlightFilter(al: $al, fnum: $fnum, date: $date, dep: $dep, arr: $arr, jdep: $jdep, jarr: $jarr, tlc: $tlc, reg: $reg)';
  }

  @override
  int get hashCode {
    return al.hashCode ^
        fnum.hashCode ^
        date.hashCode ^
        dep.hashCode ^
        arr.hashCode ^
        jdep.hashCode ^
        jarr.hashCode ^
        tlc.hashCode ^
        reg.hashCode;
  }
}

final pnlegByTlcProvider =
    Provider. /*autoDispose.*/ family<List<Pnleg>, String>((ref, tlc) => ref
        .watch(dataProvider)
        .pnleg
        .where((leg) => leg.tlc == tlc)
        .sortedBy((e) =>
            e.jdep?.dateTime ??
            Jiffy.parse(e.depdate ?? "01/01/2001",
                    pattern: "dd/MM/yyyy", isUtc: true)
                .dateTime)
        .toList());

final pnlegProvider = Provider. /*autoDispose.*/ family<List<Pnleg>, FlightFilter>(
    (ref, flightFilter) => ref
        .watch(dataProvider)
        .pnleg
        .where((leg) =>
            (leg.al == (flightFilter.al ?? leg.al)) &&
            (leg.fnum == (flightFilter.fnum ?? leg.fnum)) &&
            // (leg.jdep?.yMd ==
            //     (flightFilter.date ?? leg.jdep?.yMd)) && //format yyyy-MM-dd
            (leg.dep == (flightFilter.dep ?? leg.dep)) &&
            (leg.arr == (flightFilter.arr ?? leg.arr)) &&
            (flightFilter.jdep == null ||
                (leg.jdep != null &&
                    flightFilter.jdep != null &&
                    flightFilter.jdep!.isBetween(leg.jdep!.subtract(hours: 5),
                        leg.jdep!.add(hours: 5)))) &&
            (flightFilter.jarr == null ||
                (leg.jarr != null &&
                    flightFilter.jarr != null &&
                    flightFilter.jarr!.isBetween(leg.jarr!.subtract(hours: 5),
                        leg.jarr!.add(hours: 5)))) &&
            (leg.tlc == (flightFilter.tlc ?? leg.tlc)))
        .toList());

/*
final pnlegInterval = Provider((ref) {
  final pnleg = ref.watch(dataProvider).pnleg;
  return pnleg.where((leg) => leg.jdep != null && leg.jarr != null).toList();
});


final pnlegDates = Provider((ref) {
  final pnleg = ref.watch(dataProvider).pnleg;
  return pnleg.fold(
      <Jiffy?>{},
      (t, e) => [...t, e.jdep?.startOf(Unit.day)]
          .nonNulls
          .sortedBy((k) => k.millisecondsSinceEpoch.toString())
          .toSet());
});

final pnlegTlcs = Provider((ref) {
  final data = ref.watch(dataProvider);
  final pnleg = data.pnleg;
  final qualif = data.qualif;

  return pnleg.fold(<String?>{}, (t, e) => {...t, e.tlc}).sortedBy((k) =>
      qualif.firstWhereOrNull((q) => q.tlc == k)?.lname ?? "zzzzzzzzzzzzz");
});
*/

final aclegProvider = Provider.autoDispose.family<List<Acleg>, FlightFilter>(
    (ref, flightFilter) => ref
        .watch(dataProvider)
        .acleg
        .where((leg) =>
            (leg.FN_CARRIER == (flightFilter.al ?? leg.FN_CARRIER)) &&
            (leg.FN_NUMBER == (flightFilter.fnum ?? leg.FN_NUMBER)) &&
//            (leg.jdep?.yMd ==
//                (flightFilter.date ?? leg.jdep?.yMd)) && //format yyyy-MM-dd
            (leg.DEP_AP_ACTUAL == (flightFilter.dep ?? leg.DEP_AP_ACTUAL)) &&
            (leg.DEP_AP_SCHED == (flightFilter.dep ?? leg.DEP_AP_SCHED)) &&
            (leg.ARR_AP_ACTUAL == (flightFilter.arr ?? leg.ARR_AP_ACTUAL)) &&
            (leg.ARR_AP_SCHED == (flightFilter.arr ?? leg.ARR_AP_SCHED)) &&
            (flightFilter.jdep == null ||
                (leg.jdep != null &&
                    flightFilter.jdep != null &&
                    flightFilter.jdep!.isBetween(leg.jdep!.subtract(hours: 5),
                        leg.jdep!.add(hours: 5)))) &&
            (flightFilter.jarr == null ||
                (leg.jarr != null &&
                    flightFilter.jarr != null &&
                    flightFilter.jarr!.isBetween(leg.jarr!.subtract(hours: 5),
                        leg.jarr!.add(hours: 5)))) &&
            (leg.AC_REGISTRATION == (flightFilter.reg ?? leg.AC_REGISTRATION)))
        .toList());

final qualifProvider = Provider.family<List<Qualif>, CrewFilter>(
    (ref, crewFilter) => ref
        .watch(dataProvider)
        .qualif
        .where((x) => (((crewFilter.tlc ?? x.tlc) == x.tlc &&
            (crewFilter.college ?? x.college) == x.college &&
            (crewFilter.ac ?? x.ac) == x.ac &&
            (crewFilter.lname ?? x.lname ?? "").capitalize() ==
                (x.lname ?? "").capitalize() &&
            (crewFilter.fname ?? x.fname ?? "").capitalize() ==
                (x.fname ?? "").capitalize() &&
            //search like
            (crewFilter.searchname == null ||
                (x.fname ?? "")
                    .capitalize()
                    .contains(crewFilter.searchname!.capitalize())) &&
            (crewFilter.searchname == null ||
                (x.lname ?? "")
                    .capitalize()
                    .contains(crewFilter.searchname!.capitalize())))))
        .toList());

final dataProvider = StateNotifierProvider<DataNotifier, DataState>((ref) {
  return DataNotifier();
});

class DataState {
  List<Qualif> qualif;

  List<Pnleg> pnleg;

  List<Acleg> acleg;

  Jiffy? qualifupdate;
  Jiffy? pnlegupdate;
  Jiffy? aclegupdate;

  List<Jiffy> get pnleg_dates => pnleg.fold(
      <Jiffy>{},
      (t, e) => {
            ...t,
            e.jdep?.startOf(Unit.day) ??
                Jiffy.parse(e.date ?? "01/01/1970",
                    pattern: "dd/MM/yyyy", isUtc: true)
          }).sortedBy((e) => e.dateTime);

  List<String> get pnleg_tlcs =>
      pnleg.fold(<String>{}, (t, e) => {...t, e.tlc ?? ""}).sortedBy((e) {
        final pn = qualif.firstWhereOrNull((q) => q.tlc == e);
        return "${pn?.lname} ${pn?.fname}";
      });

  DataState({
    this.qualif = const [],
    this.pnleg = const [],
    this.acleg = const [],
    this.qualifupdate,
    this.pnlegupdate,
    this.aclegupdate,
  });

  DataState copyWith({
    List<Qualif>? qualif,
    List<Pnleg>? pnleg,
    List<Acleg>? acleg,
    Jiffy? qualifupdate,
    Jiffy? pnlegupdate,
    Jiffy? aclegupdate,
  }) {
    return DataState(
      qualif: qualif ?? this.qualif,
      pnleg: pnleg ?? this.pnleg,
      acleg: acleg ?? this.acleg,
      qualifupdate: qualifupdate ?? this.qualifupdate,
      pnlegupdate: pnlegupdate ?? this.pnlegupdate,
      aclegupdate: aclegupdate ?? this.aclegupdate,
    );
  }

  @override
  String toString() {
    return 'DataState(qualif: $qualif, pnleg: $pnleg, acleg: $acleg, qualifupdate: $qualifupdate, pnlegupdate: $pnlegupdate, aclegupdate: $aclegupdate)';
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;

    return other is DataState &&
        listEquals(other.qualif, qualif) &&
        listEquals(other.pnleg, pnleg) &&
        listEquals(other.acleg, acleg) &&
        other.qualifupdate == qualifupdate &&
        other.pnlegupdate == pnlegupdate &&
        other.aclegupdate == aclegupdate;
  }

  @override
  int get hashCode {
    return qualif.hashCode ^
        pnleg.hashCode ^
        acleg.hashCode ^
        qualifupdate.hashCode ^
        pnlegupdate.hashCode ^
        aclegupdate.hashCode;
  }
}

class DataNotifier extends StateNotifier<DataState> {
  RealtimeChannel? realtimeCsvupdates;
  Timer? _timer;

  _startTimer() {
    _timer = Timer.periodic(Duration(seconds: 60), (timer) async {
      // print("data: datastate: timer: activated ${Jiffy.now().Hms}");
      if (Jiffy.now().dateTime.difference(lastupdate.dateTime).inSeconds >
          (5 * 60)) {
        print(
            "data: datanotifier: last update ${Jiffy.now().dateTime.difference(lastupdate.dateTime).inMinutes} minutes, trying download");
        await downloadModifiedFiles();
      }
    });
  }

  Jiffy get lastupdate => pnleg3time
      .max(pnlegmoistime ?? Jiffy.now().subtract(years: 1))
      .max(aclegtime ?? Jiffy.now().subtract(years: 1));

  DataNotifier() : super(DataState()) {
    Future.delayed(Duration.zero).then((x) async {
      await loadAll();
      // await downloadModifiedFiles();
      _startTimer();
      final csvhich = Supabase.instance.client.channel('csvhichstorage');
      csvhich.subscribe((st, ob) {
        print("Data: datanotifier: Subscription status: $st | object: $ob");
      });
      csvhich.onBroadcast(
          event: 'upload',
          callback: (payload) async {
            final pay = payload; //["payload"] ?? payload;
            print("data: datanotifier: realtime: $pay");
            if (pay is Map && pay.keys.contains("filename")) {
              await downloadFile(pay["filename"] ?? "unknown.file");
            }
          });
    });
  }

  @override
  void dispose() {
    if (_timer != null) _timer!.cancel();

    if (realtimeCsvupdates != null) realtimeCsvupdates!.unsubscribe();
    super.dispose();
  }

  Future<void> downloadFile(String fileName) async {
    final supabaseClient = Supabase.instance.client;
    final localFile = File(PathTo().downloadFile(fileName));

    final fileBytes =
        await supabaseClient.storage.from('csv').download(fileName);
    if (fileBytes.isNotEmpty) {
      await localFile.writeAsBytes(fileBytes);
      print('data: downloadfile: Downloaded  file: $fileName');
      await localFile.copy(PathTo().csvFile(fileName));
      await loadAll();
    }
  }

  Future<void> downloadModifiedFiles() async {
    final supabaseClient = Supabase.instance.client;
    //print("data: downloadmodifiedfiles");
    // Get the local download directory
    final String downloadPath = PathTo().subd("download").path;

    // Create the download directory if it doesn't exist
    final Directory directory = Directory(downloadPath);
    if (!await directory.exists()) {
      await directory.create(recursive: true);
    }
    try {
      // List files in the "csv" bucket
      final filelist = await supabaseClient.storage.from('csv').list();
      // Iterate through the files and check for modifications
      for (final file in filelist) {
        final fileName = file.name;
        final modifiedAt = file.updatedAt;

        // Check if the file exists locally
        final localFile = File(PathTo().downloadFile(fileName));
        DateTime? localModifiedAt;

        if (await localFile.exists()) {
          localModifiedAt = await localFile.lastModified();
        }

        // Download the file if it has been modified
        if (localModifiedAt == null ||
            (modifiedAt != null &&
                DateTime.parse(modifiedAt).isAfter(localModifiedAt))) {
          final fileBytes =
              await supabaseClient.storage.from('csv').download(fileName);
          if (fileBytes.isNotEmpty) {
            await localFile.writeAsBytes(fileBytes);
            print(
                'data: downloadmodified file: Downloaded modified file: $fileName');
            await localFile.copy(PathTo().csvFile(fileName));
            await loadAll();
          } else {
            print('Failed to download file: $fileName');
          }
        } else {
          //print('File $fileName is up to date.');
        }
      }
    } catch (e) {
      print(
          "data: downloadmodified files: unable to list files on supabase bucket");
    }
  }

  loadAll() async {
    try {
      if (qualiftime != qualifupdate) await loadQualif();
      if (aclegtime != aclegupdate) await loadAclegs();
      if (pnleg3time != pnleg3update) await loadPnlegs3();
      if (pnlegmoistime != pnlegmoisupdate) await loadPnlegsmois();
    } catch (e) {
      print("data: loadall: exception: $e");
    }
  }

  Jiffy? qualifupdate;
  String get qualiffile => PathTo().csvFile("exportlicence.txt");
  Jiffy? get qualiftime => File(qualiffile).existsSync()
      ? Jiffy.parseFromDateTime(
          File(PathTo().csvFile("exportlicence.txt")).lastModifiedSync())
      : null;
  List<Qualif> qualif = [];
  loadQualif() async {
    print("data: qualif update ${qualiftime?.dateTime}");
    final txt = await File(qualiffile).readAsString();
    qualifupdate = qualiftime;

    qualif =
        await compute(csvqualiftolist, txt, debugLabel: "convert qualif csv");

    state = state.copyWith(qualifupdate: qualifupdate, qualif: qualif);
  }

  static List<Qualif> csvqualiftolist(txt) {
    return CsvData.csv2list(txt).map((e) => Qualif.fromList(e)).toList();
  }

  Jiffy? aclegupdate;
  String get aclegfile => PathTo().csvFile("secondprgtype.zip");
  Jiffy? get aclegtime => File(aclegfile).existsSync()
      ? Jiffy.parseFromDateTime(
          File(PathTo().csvFile("secondprgtype.zip")).lastModifiedSync())
      : null;
  List<Acleg> acleg = [];
  loadAclegs() async {
    print("data: acleg update ${aclegtime?.dateTime}");

    final txt =
        await CsvData.extractTextFileFromZip(aclegfile, "secondprgtype.txt");
    aclegupdate = aclegtime;
    acleg =
        await compute(csvaclegtolist, txt, debugLabel: "convert aclegs csv");

    state = state.copyWith(aclegupdate: aclegupdate, acleg: acleg);

    //print(acleg);
  }

  static List<Acleg> csvaclegtolist(txt) {
    return CsvData.csv2list(txt)
        .map((e) => Acleg.fromList(e))
        .where((leg) =>
            //only flights of Tunisair
            (leg.FN_CARRIER == "TU" ||
                leg.EMPLOYER_CABIN == "TU" ||
                leg.EMPLOYER_COCKPIT == "TU" ||
                leg.AC_OWNER == "TU"))
        .sortedBy((e) => e.jdep!.dateTime)
        .toList();
  }

  List<Pnleg> pnleg = [];

  static List<Pnleg> calculpnleg(Map data) {
    //print(data.keys);
    final pnlegmoistime = data["pnlegmoistime"] as Jiffy?;
    final pnleg3time = data["pnleg3time"] as Jiffy?;
    final pnlegmois = data["pnlegmois"] as List<Pnleg>;
    final pnleg3 = data["pnleg3"] as List<Pnleg>;
    if (pnlegmoistime != null &&
        pnleg3time != null &&
        pnlegmoistime.isSameOrAfter(pnleg3time)) {
      return pnlegmois;
    } else if (pnlegmoistime != null &&
        pnleg3time != null &&
        pnlegmoistime.isBefore(pnleg3time)) {
      final dates3tlc3 =
          pnleg3.fold(<String>{}, (p, e) => {...p, "${e.date},${e.tlc}"});
      return (pnlegmois
          .where((x) => !(dates3tlc3.contains("${x.date},${x.tlc}")))
          .toList())
        ..addAll(pnleg3); //pnleg3 dabord
    } else if (pnlegmoistime == null && pnleg3time != null) {
      return pnleg3;
    } else if (pnlegmoistime != null && pnleg3time == null) {
      return pnlegmois;
    } else {
      return <Pnleg>[];
    }
  }

  Jiffy? pnlegmoisupdate;
  static String get pnlegmoisfile => PathTo().csvFile("ExportPGRGPNmois.zip");
  Jiffy? get pnlegmoistime => File(pnlegmoisfile).existsSync()
      ? Jiffy.parseFromDateTime(
          File(PathTo().csvFile("ExportPGRGPNmois.zip")).lastModifiedSync())
      : null;
  List<Pnleg> pnlegmois = [];
  loadPnlegsmois() async {
    print("data: pnlegmois update ${pnlegmoistime?.dateTime}");

    final txt = await CsvData.extractTextFileFromZip(
        pnlegmoisfile, "ExportPGRGPNmois.txt");
    // pnlegmois = CsvData.csv2list(txt).map((e) => Pnleg.fromList(e)).toList();
    pnlegmoisupdate = pnlegmoistime;

    pnlegmois =
        await compute(csvpnlegtolist, txt, debugLabel: "convert pnlegmois csv");
    pnleg = await compute(
        calculpnleg,
        {
          "pnlegmois": pnlegmois,
          "pnlegmoistime": pnlegmoistime,
          "pnleg3": pnleg3,
          "pnleg3time": pnleg3time,
        },
        debugLabel: "recalculate pnleg");
    //calculPnlegMeta();
    state = state.copyWith(pnlegupdate: pnlegmoisupdate, pnleg: pnleg);
  }

  static List<Pnleg> csvpnlegtolist(txt) {
    return CsvData.csv2list(txt).map((e) => Pnleg.fromList(e)).toList();
  }

  Jiffy? pnleg3update;
  String get pnleg3file => PathTo().csvFile("exportPGRGPN.zip");
  Jiffy? get pnleg3time => File(pnleg3file).existsSync()
      ? Jiffy.parseFromDateTime(
          File(PathTo().csvFile("exportPGRGPN.zip")).lastModifiedSync())
      : null;
  List<Pnleg> pnleg3 = [];
  loadPnlegs3() async {
    print("data: pnleg3 update ${pnleg3time?.dateTime}");

    final txt =
        await CsvData.extractTextFileFromZip(pnleg3file, "exportPGRGPN.txt");
    pnleg3 =
        await compute(csvpnlegtolist, txt, debugLabel: "convert pnleg3 csv");
    pnleg3update = pnleg3time;
    pnleg = await compute(
        calculpnleg,
        {
          "pnlegmois": pnlegmois,
          "pnlegmoistime": pnlegmoistime,
          "pnleg3": pnleg3,
          "pnleg3time": pnleg3time,
        },
        debugLabel: "recalculating pnleg");

    state = state.copyWith(pnlegupdate: pnleg3update, pnleg: pnleg);

    //print(pnleg3);
  }
}

class Qualif {
  String? tlc;
  String? lname;
  String? mname;
  String? fname;
  String? date;
  String? ac;
  String? college;
  String? base;
  Qualif({
    this.tlc,
    this.lname,
    this.mname,
    this.fname,
    this.date,
    this.ac,
    this.college,
    this.base,
  });

  Qualif copyWith({
    String? tlc,
    String? lname,
    String? mname,
    String? fname,
    String? date,
    String? ac,
    String? college,
    String? base,
  }) {
    return Qualif(
      tlc: tlc ?? this.tlc,
      lname: lname ?? this.lname,
      mname: mname ?? this.mname,
      fname: fname ?? this.fname,
      date: date ?? this.date,
      ac: ac ?? this.ac,
      college: college ?? this.college,
      base: base ?? this.base,
    );
  }

  Map<String, dynamic> toMap() {
    return <String, dynamic>{
      'tlc': tlc,
      'lname': lname,
      'mname': mname,
      'fname': fname,
      'date': date,
      'ac': ac,
      'college': college,
      'base': base,
    };
  }

  factory Qualif.fromList(List datalist) {
    return Qualif(
        tlc: datalist[0],
        lname: datalist[1],
        mname: datalist[2],
        fname: datalist[3],
        date: datalist[4],
        ac: datalist[5],
        college: datalist[6],
        base: datalist[7]);
  }

  factory Qualif.fromMap(Map<String, dynamic> map) {
    return Qualif(
      tlc: map['tlc'] != null ? map['tlc'] as String : null,
      lname: map['lname'] != null ? map['lname'] as String : null,
      mname: map['mname'] != null ? map['mname'] as String : null,
      fname: map['fname'] != null ? map['fname'] as String : null,
      date: map['date'] != null ? map['date'] as String : null,
      ac: map['ac'] != null ? map['ac'] as String : null,
      college: map['college'] != null ? map['college'] as String : null,
      base: map['base'] != null ? map['base'] as String : null,
    );
  }

  String toJson() => json.encode(toMap());

  factory Qualif.fromJson(String source) =>
      Qualif.fromMap(json.decode(source) as Map<String, dynamic>);

  @override
  String toString() {
    return 'Qualif(tlc: $tlc, lname: $lname, mname: $mname, fname: $fname, date: $date, ac: $ac, college: $college, base: $base)';
  }

  @override
  bool operator ==(covariant Qualif other) {
    if (identical(this, other)) return true;

    return other.tlc == tlc &&
        other.lname == lname &&
        other.mname == mname &&
        other.fname == fname &&
        other.date == date &&
        other.ac == ac &&
        other.college == college &&
        other.base == base;
  }

  @override
  int get hashCode {
    return tlc.hashCode ^
        lname.hashCode ^
        mname.hashCode ^
        fname.hashCode ^
        date.hashCode ^
        ac.hashCode ^
        college.hashCode ^
        base.hashCode;
  }
}

class Pnleg {
  String? date;
  Jiffy? get jdate => date != null
      ? Jiffy.parse(date ?? "01/01/1970", pattern: "dd/MM/yyyy", isUtc: true)
      : null;

  String? tlc;
  String? actype;
  String? al;
  String? fnum;
  String? depdate;
  String? deptime;
  String? arrdate;
  String? arrtime;
  String? dep;
  String? arr;
  String? label;
  String? type;
  Jiffy? get jdep =>
      deptime != null ? "$depdate $deptime".parseddmmyyyyhhmm() : null;
  Jiffy? get jarr =>
      arrtime != null ? "$arrdate $arrtime".parseddmmyyyyhhmm() : null;

  String get dutytype {
    if (type == "L") {
      return "flight";
    } else if ((type == "G")) {
      return "dhlimo";
    } else if ((type == "F") || ((dep ?? "") != "" && (arr ?? "") != "")) {
      return "dhflight";
    } else if ((label?.startsWith("SBY") ?? false) || (label == "R0")) {
      return "standby";
    } else if ((!["OFF", "CM", "CA", "PP"].contains(label)) &&
        (jarr != null &&
            jdep != null &&
            DTInterval(jdep!, jarr!).duration.inHours < 18)) {
      return "ground";
    } else {
      return "day";
    }
  }

  Pnleg({
    this.date,
    this.tlc,
    this.actype,
    this.al,
    this.fnum,
    this.depdate,
    this.deptime,
    this.arrdate,
    this.arrtime,
    this.dep,
    this.arr,
    this.label,
    this.type,
  });

  Pnleg copyWith({
    String? date,
    String? tlc,
    String? actype,
    String? al,
    String? fnum,
    String? depdate,
    String? deptime,
    String? arrdate,
    String? arrtime,
    String? dep,
    String? arr,
    String? label,
    String? type,
  }) {
    return Pnleg(
      date: date ?? this.date,
      tlc: tlc ?? this.tlc,
      actype: actype ?? this.actype,
      al: al ?? this.al,
      fnum: fnum ?? this.fnum,
      depdate: depdate ?? this.depdate,
      deptime: deptime ?? this.deptime,
      arrdate: arrdate ?? this.arrdate,
      arrtime: arrtime ?? this.arrtime,
      dep: dep ?? this.dep,
      arr: arr ?? this.arr,
      label: label ?? this.label,
      type: type ?? this.type,
    );
  }

  Map<String, dynamic> toMap() {
    return <String, dynamic>{
      'date': date,
      'tlc': tlc,
      'actype': actype,
      'al': al,
      'fnum': fnum,
      'depdate': depdate,
      'deptime': deptime,
      'arrdate': arrdate,
      'arrtime': arrtime,
      'dep': dep,
      'arr': arr,
      'label': label,
      'type': type,
    };
  }

  factory Pnleg.fromList(List datalist) {
    return Pnleg(
        date: datalist[0],
        tlc: datalist[1],
        actype: datalist[2],
        al: datalist[3],
        fnum: datalist[4],
        depdate: datalist[5],
        deptime: datalist[6],
        arrdate: datalist[7],
        arrtime: datalist[8],
        dep: datalist[9],
        arr: datalist[10],
        label: datalist[11],
        type: datalist[12]);
  }

  factory Pnleg.fromMap(Map<String, dynamic> map) {
    return Pnleg(
      date: map['date'] != null ? map['date'] as String : null,
      tlc: map['tlc'] != null ? map['tlc'] as String : null,
      actype: map['actype'] != null ? map['actype'] as String : null,
      al: map['al'] != null ? map['al'] as String : null,
      fnum: map['fnum'] != null ? map['fnum'] as String : null,
      depdate: map['depdate'] != null ? map['depdate'] as String : null,
      deptime: map['deptime'] != null ? map['deptime'] as String : null,
      arrdate: map['arrdate'] != null ? map['arrdate'] as String : null,
      arrtime: map['arrtime'] != null ? map['arrtime'] as String : null,
      dep: map['dep'] != null ? map['dep'] as String : null,
      arr: map['arr'] != null ? map['arr'] as String : null,
      label: map['label'] != null ? map['label'] as String : null,
      type: map['type'] != null ? map['type'] as String : null,
    );
  }

  String toJson() => json.encode(toMap());

  factory Pnleg.fromJson(String source) =>
      Pnleg.fromMap(json.decode(source) as Map<String, dynamic>);

  @override
  String toString() {
    return 'Pnleg(date: $date, tlc: $tlc, actype: $actype, al: $al, fnum: $fnum, depdate: $depdate, deptime: $deptime, arrdate: $arrdate, arrtime: $arrtime, dep: $dep, arr: $arr, label: $label, type: $type)';
  }

  @override
  bool operator ==(covariant Pnleg other) {
    if (identical(this, other)) return true;

    return other.date == date &&
        other.tlc == tlc &&
        other.actype == actype &&
        other.al == al &&
        other.fnum == fnum &&
        other.depdate == depdate &&
        other.deptime == deptime &&
        other.arrdate == arrdate &&
        other.arrtime == arrtime &&
        other.dep == dep &&
        other.arr == arr &&
        other.label == label &&
        other.type == type;
  }

  @override
  int get hashCode {
    return date.hashCode ^
        tlc.hashCode ^
        actype.hashCode ^
        al.hashCode ^
        fnum.hashCode ^
        depdate.hashCode ^
        deptime.hashCode ^
        arrdate.hashCode ^
        arrtime.hashCode ^
        dep.hashCode ^
        arr.hashCode ^
        label.hashCode ^
        type.hashCode;
  }
}

//enum flt_status { sched, delayed, taxiout, enroute, landed, arrived }

class Acleg {
  String? LEG_NO;
  String? FN_CARRIER;
  String? FN_NUMBER;
  String? FN_SUFFIX;
  String? DAY_OF_ORIGIN;
  String? AC_OWNER;
  String? AC_SUBTYPE;
  String? AC_VERSION;
  String? AC_REGISTRATION;
  String? DEP_AP_ACTUAL;
  String? DEP_AP_SCHED;
  String? DEP_DT_EST;
  Jiffy? get jdepest =>
      (DEP_DT_EST == null) ? null : DEP_DT_EST!.parseyyyymmddhhmm();
  String? DEP_SCHED_DT;
  Jiffy? get jdepsched =>
      (DEP_SCHED_DT == null) ? null : DEP_SCHED_DT!.parseyyyymmddhhmm();
  String? ARR_AP_ACTUAL;
  String? ARR_AP_SCHED;
  String? ARR_DT_EST;
  Jiffy? get jarrest =>
      (ARR_DT_EST == null) ? null : ARR_DT_EST!.parseyyyymmddhhmm();
  String? ARR_SCHED_DT;
  Jiffy? get jarrsched =>
      (ARR_SCHED_DT == null) ? null : ARR_SCHED_DT!.parseyyyymmddhhmm();
  String? SLOT_TIME_ACTUAL;
  Jiffy? get slot =>
      (SLOT_TIME_ACTUAL == null) ? null : SLOT_TIME_ACTUAL!.parseyyyymmddhhmm();
  String? LEG_TYPE;
  String? STATUS;
  String? EMPLOYER_COCKPIT;
  String? EMPLOYER_CABIN;
  String? CYCLES;
  String? DELAY_CODE_01;
  String? DELAY_CODE_02;
  String? DELAY_CODE_03;
  String? DELAY_CODE_04;
  String? DELAY_TIME_01;
  String? DELAY_TIME_02;
  String? DELAY_TIME_03;
  String? DELAY_TIME_04;
  String? SUBDELAY_CODE_01;
  String? SUBDELAY_CODE_02;
  String? SUBDELAY_CODE_03;
  String? SUBDELAY_CODE_04;
  List<List> get dla => [
        [
          SUBDELAY_CODE_01 ?? DELAY_CODE_01,
          DELAY_TIME_01 == null
              ? null
              : Duration(minutes: int.parse(DELAY_TIME_01!))
        ],
        [
          SUBDELAY_CODE_02 ?? DELAY_CODE_02,
          DELAY_TIME_02 == null
              ? null
              : Duration(minutes: int.parse(DELAY_TIME_02!))
        ],
        [
          SUBDELAY_CODE_03 ?? DELAY_CODE_03,
          DELAY_TIME_03 == null
              ? null
              : Duration(minutes: int.parse(DELAY_TIME_03!))
        ],
        [
          SUBDELAY_CODE_04 ?? DELAY_CODE_04,
          DELAY_TIME_04 == null
              ? null
              : Duration(minutes: int.parse(DELAY_TIME_04!))
        ],
      ].where((e) => e.every((f) => f != null)).toList();
  List<String> get delaycode => [
        DELAY_CODE_01,
        DELAY_CODE_02,
        DELAY_CODE_03,
        DELAY_CODE_04
      ].nonNulls.toList();
  List<String> get delaysubcode => [
        SUBDELAY_CODE_01,
        SUBDELAY_CODE_02,
        SUBDELAY_CODE_03,
        SUBDELAY_CODE_04
      ].nonNulls.toList();
  List<Duration?> get delaytime => [
        DELAY_TIME_01 == null
            ? null
            : Duration(minutes: int.parse(DELAY_TIME_01!)),
        DELAY_TIME_02 == null
            ? null
            : Duration(minutes: int.parse(DELAY_TIME_02!)),
        DELAY_TIME_03 == null
            ? null
            : Duration(minutes: int.parse(DELAY_TIME_03!)),
        DELAY_TIME_04 == null
            ? null
            : Duration(minutes: int.parse(DELAY_TIME_04!))
      ].nonNulls.toList();

  String? PAX_BOOKED_C;
  String? PAX_BOOKED_Y;
  String? get pax_booked => (PAX_BOOKED_C != null || PAX_BOOKED_Y != null)
      ? "${(AC_VERSION != null && AC_VERSION!.contains("C")) ? "C${PAX_BOOKED_C ?? 0}." : ""}Y${PAX_BOOKED_Y ?? 0}"
      : null;
  String? PAX_BOOKED_TRS_C;
  String? PAX_BOOKED_TRS_Y;
  String? get pax_trs => (PAX_BOOKED_TRS_C != null || PAX_BOOKED_TRS_Y != null)
      ? "C${PAX_BOOKED_TRS_C ?? 0}/Y${PAX_BOOKED_TRS_Y ?? 0}"
      : null;
  String? PAD_BOOKED_C;
  String? PAD_BOOKED_Y;
  String? get pad_booked => (PAD_BOOKED_C != null || PAD_BOOKED_Y != null)
      ? "C${PAD_BOOKED_C ?? 0}/Y${PAD_BOOKED_Y ?? 0}"
      : null;
  String? OFFBLOCK_DT_A;
  String? AIRBORNE_DT_A;
  String? LANDING_DT_A;
  String? ONBLOCK_DT_A;
  List<Jiffy?> get blocks_a => [
        (OFFBLOCK_DT_A ?? "").parseyyyymmddhhmm(),
        (AIRBORNE_DT_A ?? "").parseyyyymmddhhmm(),
        (LANDING_DT_A ?? "").parseyyyymmddhhmm(),
        (ONBLOCK_DT_A ?? "").parseyyyymmddhhmm()
      ];
  String get flt_status {
    if (blocks[3] != null) {
      return "Arrived";
    } else if (blocks[2] != null) {
      return "Landed";
    } else if (blocks[1] != null) {
      return "Inflight";
    } else if (blocks[0] != null) {
      return "Taxiout";
    } else if (jdepest != null &&
        jarrsched != null &&
        jdepest!.isAfter(jdepsched!)) {
      return "Delayed";
    } else {
      return "Sched";
    }
  }

  String? OFFBLOCK_DT_F;
  String? AIRBORNE_DT_F;
  String? LANDING_DT_F;
  String? ONBLOCK_DT_F;
  List<Jiffy?> get blocks_f => [
        (OFFBLOCK_DT_F ?? "").parseyyyymmddhhmm(),
        (AIRBORNE_DT_F ?? "").parseyyyymmddhhmm(),
        (LANDING_DT_F ?? "").parseyyyymmddhhmm(),
        (ONBLOCK_DT_F ?? "").parseyyyymmddhhmm()
      ];

  String? OFFBLOCK_DT_M;
  String? AIRBORNE_DT_M;
  String? LANDING_DT_M;
  String? ONBLOCK_DT_M;
  List<Jiffy?> get blocks_m => [
        (OFFBLOCK_DT_M ?? "").parseyyyymmddhhmm(),
        (AIRBORNE_DT_M ?? "").parseyyyymmddhhmm(),
        (LANDING_DT_M ?? "").parseyyyymmddhhmm(),
        (ONBLOCK_DT_M ?? "").parseyyyymmddhhmm()
      ];
  List<Jiffy?> get blocks => [
        blocks_m[0] ?? blocks_a[0] ?? blocks_f[0],
        blocks_m[1] ?? blocks_a[1] ?? blocks_f[1],
        blocks_m[2] ?? blocks_a[2] ?? blocks_f[2],
        blocks_m[3] ?? blocks_a[3] ?? blocks_f[3]
      ];
  Jiffy? get jdep => blocks[0] ?? jdepest ?? jdepsched;
  Jiffy? get jarr =>
      blocks[3] ??
      blocks[2]?.add(minutes: 5) ??
      (eet == null ? null : blocks[1]?.addDuration(eet!).add(minutes: 8)) ??
      (eet == null
          ? null
          : blocks[0]?.add(minutes: 5).addDuration(eet!).add(minutes: 8)) ??
      (eet == null
          ? null
          : jdep?.addDuration(eet!).add(minutes: 8).add(minutes: 5)) ??
      jarrest ??
      jarrsched;
  String? EET;
  Duration? get eet => EET == null ? null : Duration(minutes: int.parse(EET!));
  Acleg({
    this.LEG_NO,
    this.FN_CARRIER,
    this.FN_NUMBER,
    this.FN_SUFFIX,
    this.DAY_OF_ORIGIN,
    this.AC_OWNER,
    this.AC_SUBTYPE,
    this.AC_VERSION,
    this.AC_REGISTRATION,
    this.DEP_AP_ACTUAL,
    this.DEP_AP_SCHED,
    this.DEP_DT_EST,
    this.DEP_SCHED_DT,
    this.ARR_AP_ACTUAL,
    this.ARR_AP_SCHED,
    this.ARR_DT_EST,
    this.ARR_SCHED_DT,
    this.SLOT_TIME_ACTUAL,
    this.LEG_TYPE,
    this.STATUS,
    this.EMPLOYER_COCKPIT,
    this.EMPLOYER_CABIN,
    this.CYCLES,
    this.DELAY_CODE_01,
    this.DELAY_CODE_02,
    this.DELAY_CODE_03,
    this.DELAY_CODE_04,
    this.DELAY_TIME_01,
    this.DELAY_TIME_02,
    this.DELAY_TIME_03,
    this.DELAY_TIME_04,
    this.SUBDELAY_CODE_01,
    this.SUBDELAY_CODE_02,
    this.SUBDELAY_CODE_03,
    this.SUBDELAY_CODE_04,
    this.PAX_BOOKED_C,
    this.PAX_BOOKED_Y,
    this.PAX_BOOKED_TRS_C,
    this.PAX_BOOKED_TRS_Y,
    this.PAD_BOOKED_C,
    this.PAD_BOOKED_Y,
    this.OFFBLOCK_DT_A,
    this.AIRBORNE_DT_A,
    this.LANDING_DT_A,
    this.ONBLOCK_DT_A,
    this.OFFBLOCK_DT_F,
    this.AIRBORNE_DT_F,
    this.LANDING_DT_F,
    this.ONBLOCK_DT_F,
    this.OFFBLOCK_DT_M,
    this.AIRBORNE_DT_M,
    this.LANDING_DT_M,
    this.ONBLOCK_DT_M,
    this.EET,
  });

  Acleg copyWith({
    String? LEG_NO,
    String? FN_CARRIER,
    String? FN_NUMBER,
    String? FN_SUFFIX,
    String? DAY_OF_ORIGIN,
    String? AC_OWNER,
    String? AC_SUBTYPE,
    String? AC_VERSION,
    String? AC_REGISTRATION,
    String? DEP_AP_ACTUAL,
    String? DEP_AP_SCHED,
    String? DEP_DT_EST,
    String? DEP_SCHED_DT,
    String? ARR_AP_ACTUAL,
    String? ARR_AP_SCHED,
    String? ARR_DT_EST,
    String? ARR_SCHED_DT,
    String? SLOT_TIME_ACTUAL,
    String? LEG_TYPE,
    String? STATUS,
    String? EMPLOYER_COCKPIT,
    String? EMPLOYER_CABIN,
    String? CYCLES,
    String? DELAY_CODE_01,
    String? DELAY_CODE_02,
    String? DELAY_CODE_03,
    String? DELAY_CODE_04,
    String? DELAY_TIME_01,
    String? DELAY_TIME_02,
    String? DELAY_TIME_03,
    String? DELAY_TIME_04,
    String? SUBDELAY_CODE_01,
    String? SUBDELAY_CODE_02,
    String? SUBDELAY_CODE_03,
    String? SUBDELAY_CODE_04,
    String? PAX_BOOKED_C,
    String? PAX_BOOKED_Y,
    String? PAX_BOOKED_TRS_C,
    String? PAX_BOOKED_TRS_Y,
    String? PAD_BOOKED_C,
    String? PAD_BOOKED_Y,
    String? OFFBLOCK_DT_A,
    String? AIRBORNE_DT_A,
    String? LANDING_DT_A,
    String? ONBLOCK_DT_A,
    String? OFFBLOCK_DT_F,
    String? AIRBORNE_DT_F,
    String? LANDING_DT_F,
    String? ONBLOCK_DT_F,
    String? OFFBLOCK_DT_M,
    String? AIRBORNE_DT_M,
    String? LANDING_DT_M,
    String? ONBLOCK_DT_M,
    String? EET,
  }) {
    return Acleg(
      LEG_NO: LEG_NO ?? this.LEG_NO,
      FN_CARRIER: FN_CARRIER ?? this.FN_CARRIER,
      FN_NUMBER: FN_NUMBER ?? this.FN_NUMBER,
      FN_SUFFIX: FN_SUFFIX ?? this.FN_SUFFIX,
      DAY_OF_ORIGIN: DAY_OF_ORIGIN ?? this.DAY_OF_ORIGIN,
      AC_OWNER: AC_OWNER ?? this.AC_OWNER,
      AC_SUBTYPE: AC_SUBTYPE ?? this.AC_SUBTYPE,
      AC_VERSION: AC_VERSION ?? this.AC_VERSION,
      AC_REGISTRATION: AC_REGISTRATION ?? this.AC_REGISTRATION,
      DEP_AP_ACTUAL: DEP_AP_ACTUAL ?? this.DEP_AP_ACTUAL,
      DEP_AP_SCHED: DEP_AP_SCHED ?? this.DEP_AP_SCHED,
      DEP_DT_EST: DEP_DT_EST ?? this.DEP_DT_EST,
      DEP_SCHED_DT: DEP_SCHED_DT ?? this.DEP_SCHED_DT,
      ARR_AP_ACTUAL: ARR_AP_ACTUAL ?? this.ARR_AP_ACTUAL,
      ARR_AP_SCHED: ARR_AP_SCHED ?? this.ARR_AP_SCHED,
      ARR_DT_EST: ARR_DT_EST ?? this.ARR_DT_EST,
      ARR_SCHED_DT: ARR_SCHED_DT ?? this.ARR_SCHED_DT,
      SLOT_TIME_ACTUAL: SLOT_TIME_ACTUAL ?? this.SLOT_TIME_ACTUAL,
      LEG_TYPE: LEG_TYPE ?? this.LEG_TYPE,
      STATUS: STATUS ?? this.STATUS,
      EMPLOYER_COCKPIT: EMPLOYER_COCKPIT ?? this.EMPLOYER_COCKPIT,
      EMPLOYER_CABIN: EMPLOYER_CABIN ?? this.EMPLOYER_CABIN,
      CYCLES: CYCLES ?? this.CYCLES,
      DELAY_CODE_01: DELAY_CODE_01 ?? this.DELAY_CODE_01,
      DELAY_CODE_02: DELAY_CODE_02 ?? this.DELAY_CODE_02,
      DELAY_CODE_03: DELAY_CODE_03 ?? this.DELAY_CODE_03,
      DELAY_CODE_04: DELAY_CODE_04 ?? this.DELAY_CODE_04,
      DELAY_TIME_01: DELAY_TIME_01 ?? this.DELAY_TIME_01,
      DELAY_TIME_02: DELAY_TIME_02 ?? this.DELAY_TIME_02,
      DELAY_TIME_03: DELAY_TIME_03 ?? this.DELAY_TIME_03,
      DELAY_TIME_04: DELAY_TIME_04 ?? this.DELAY_TIME_04,
      SUBDELAY_CODE_01: SUBDELAY_CODE_01 ?? this.SUBDELAY_CODE_01,
      SUBDELAY_CODE_02: SUBDELAY_CODE_02 ?? this.SUBDELAY_CODE_02,
      SUBDELAY_CODE_03: SUBDELAY_CODE_03 ?? this.SUBDELAY_CODE_03,
      SUBDELAY_CODE_04: SUBDELAY_CODE_04 ?? this.SUBDELAY_CODE_04,
      PAX_BOOKED_C: PAX_BOOKED_C ?? this.PAX_BOOKED_C,
      PAX_BOOKED_Y: PAX_BOOKED_Y ?? this.PAX_BOOKED_Y,
      PAX_BOOKED_TRS_C: PAX_BOOKED_TRS_C ?? this.PAX_BOOKED_TRS_C,
      PAX_BOOKED_TRS_Y: PAX_BOOKED_TRS_Y ?? this.PAX_BOOKED_TRS_Y,
      PAD_BOOKED_C: PAD_BOOKED_C ?? this.PAD_BOOKED_C,
      PAD_BOOKED_Y: PAD_BOOKED_Y ?? this.PAD_BOOKED_Y,
      OFFBLOCK_DT_A: OFFBLOCK_DT_A ?? this.OFFBLOCK_DT_A,
      AIRBORNE_DT_A: AIRBORNE_DT_A ?? this.AIRBORNE_DT_A,
      LANDING_DT_A: LANDING_DT_A ?? this.LANDING_DT_A,
      ONBLOCK_DT_A: ONBLOCK_DT_A ?? this.ONBLOCK_DT_A,
      OFFBLOCK_DT_F: OFFBLOCK_DT_F ?? this.OFFBLOCK_DT_F,
      AIRBORNE_DT_F: AIRBORNE_DT_F ?? this.AIRBORNE_DT_F,
      LANDING_DT_F: LANDING_DT_F ?? this.LANDING_DT_F,
      ONBLOCK_DT_F: ONBLOCK_DT_F ?? this.ONBLOCK_DT_F,
      OFFBLOCK_DT_M: OFFBLOCK_DT_M ?? this.OFFBLOCK_DT_M,
      AIRBORNE_DT_M: AIRBORNE_DT_M ?? this.AIRBORNE_DT_M,
      LANDING_DT_M: LANDING_DT_M ?? this.LANDING_DT_M,
      ONBLOCK_DT_M: ONBLOCK_DT_M ?? this.ONBLOCK_DT_M,
      EET: EET ?? this.EET,
    );
  }

  Map<String, dynamic> toMap() {
    return <String, dynamic>{
      'LEG_NO': LEG_NO,
      'FN_CARRIER': FN_CARRIER,
      'FN_NUMBER': FN_NUMBER,
      'FN_SUFFIX': FN_SUFFIX,
      'DAY_OF_ORIGIN': DAY_OF_ORIGIN,
      'AC_OWNER': AC_OWNER,
      'AC_SUBTYPE': AC_SUBTYPE,
      'AC_VERSION': AC_VERSION,
      'AC_REGISTRATION': AC_REGISTRATION,
      'DEP_AP_ACTUAL': DEP_AP_ACTUAL,
      'DEP_AP_SCHED': DEP_AP_SCHED,
      'DEP_DT_EST': DEP_DT_EST,
      'DEP_SCHED_DT': DEP_SCHED_DT,
      'ARR_AP_ACTUAL': ARR_AP_ACTUAL,
      'ARR_AP_SCHED': ARR_AP_SCHED,
      'ARR_DT_EST': ARR_DT_EST,
      'ARR_SCHED_DT': ARR_SCHED_DT,
      'SLOT_TIME_ACTUAL': SLOT_TIME_ACTUAL,
      'LEG_TYPE': LEG_TYPE,
      'STATUS': STATUS,
      'EMPLOYER_COCKPIT': EMPLOYER_COCKPIT,
      'EMPLOYER_CABIN': EMPLOYER_CABIN,
      'CYCLES': CYCLES,
      'DELAY_CODE_01': DELAY_CODE_01,
      'DELAY_CODE_02': DELAY_CODE_02,
      'DELAY_CODE_03': DELAY_CODE_03,
      'DELAY_CODE_04': DELAY_CODE_04,
      'DELAY_TIME_01': DELAY_TIME_01,
      'DELAY_TIME_02': DELAY_TIME_02,
      'DELAY_TIME_03': DELAY_TIME_03,
      'DELAY_TIME_04': DELAY_TIME_04,
      'SUBDELAY_CODE_01': SUBDELAY_CODE_01,
      'SUBDELAY_CODE_02': SUBDELAY_CODE_02,
      'SUBDELAY_CODE_03': SUBDELAY_CODE_03,
      'SUBDELAY_CODE_04': SUBDELAY_CODE_04,
      'PAX_BOOKED_C': PAX_BOOKED_C,
      'PAX_BOOKED_Y': PAX_BOOKED_Y,
      'PAX_BOOKED_TRS_C': PAX_BOOKED_TRS_C,
      'PAX_BOOKED_TRS_Y': PAX_BOOKED_TRS_Y,
      'PAD_BOOKED_C': PAD_BOOKED_C,
      'PAD_BOOKED_Y': PAD_BOOKED_Y,
      'OFFBLOCK_DT_A': OFFBLOCK_DT_A,
      'AIRBORNE_DT_A': AIRBORNE_DT_A,
      'LANDING_DT_A': LANDING_DT_A,
      'ONBLOCK_DT_A': ONBLOCK_DT_A,
      'OFFBLOCK_DT_F': OFFBLOCK_DT_F,
      'AIRBORNE_DT_F': AIRBORNE_DT_F,
      'LANDING_DT_F': LANDING_DT_F,
      'ONBLOCK_DT_F': ONBLOCK_DT_F,
      'OFFBLOCK_DT_M': OFFBLOCK_DT_M,
      'AIRBORNE_DT_M': AIRBORNE_DT_M,
      'LANDING_DT_M': LANDING_DT_M,
      'ONBLOCK_DT_M': ONBLOCK_DT_M,
      'EET': EET,
    };
  }

  factory Acleg.fromList(List datalist) {
    //print(datalist);
    if (datalist.length >= 54) {
      return Acleg(
          LEG_NO: datalist[0],
          FN_CARRIER: datalist[1],
          FN_NUMBER: datalist[2],
          FN_SUFFIX: datalist[3],
          DAY_OF_ORIGIN: datalist[4],
          AC_OWNER: datalist[5],
          AC_SUBTYPE: datalist[6],
          AC_VERSION: datalist[7],
          AC_REGISTRATION: datalist[8],
          DEP_AP_ACTUAL: datalist[9],
          DEP_AP_SCHED: datalist[10],
          DEP_DT_EST: datalist[11],
          DEP_SCHED_DT: datalist[12],
          ARR_AP_ACTUAL: datalist[13],
          ARR_AP_SCHED: datalist[14],
          ARR_DT_EST: datalist[15],
          ARR_SCHED_DT: datalist[16],
          SLOT_TIME_ACTUAL: datalist[17],
          LEG_TYPE: datalist[18],
          STATUS: datalist[19],
          EMPLOYER_COCKPIT: datalist[20],
          EMPLOYER_CABIN: datalist[21],
          CYCLES: datalist[22],
          DELAY_CODE_01: datalist[23],
          DELAY_CODE_02: datalist[24],
          DELAY_CODE_03: datalist[25],
          DELAY_CODE_04: datalist[26],
          DELAY_TIME_01: datalist[27],
          DELAY_TIME_02: datalist[28],
          DELAY_TIME_03: datalist[29],
          DELAY_TIME_04: datalist[30],
          SUBDELAY_CODE_01: datalist[31],
          SUBDELAY_CODE_02: datalist[32],
          SUBDELAY_CODE_03: datalist[33],
          SUBDELAY_CODE_04: datalist[34],
          PAX_BOOKED_C: datalist[35],
          PAX_BOOKED_Y: datalist[36],
          PAX_BOOKED_TRS_C: datalist[37],
          PAX_BOOKED_TRS_Y: datalist[38],
          PAD_BOOKED_C: datalist[39],
          PAD_BOOKED_Y: datalist[40],
          OFFBLOCK_DT_A: datalist[41],
          AIRBORNE_DT_A: datalist[42],
          LANDING_DT_A: datalist[43],
          ONBLOCK_DT_A: datalist[44],
          OFFBLOCK_DT_F: datalist[45],
          AIRBORNE_DT_F: datalist[46],
          LANDING_DT_F: datalist[47],
          ONBLOCK_DT_F: datalist[48],
          OFFBLOCK_DT_M: datalist[49],
          AIRBORNE_DT_M: datalist[50],
          LANDING_DT_M: datalist[51],
          ONBLOCK_DT_M: datalist[52],
          EET: datalist[53]);
    } else {
      return Acleg();
    }
  }

  factory Acleg.fromMap(Map<String, dynamic> map) {
    return Acleg(
      LEG_NO: map['LEG_NO'] != null ? map['LEG_NO'] as String : null,
      FN_CARRIER:
          map['FN_CARRIER'] != null ? map['FN_CARRIER'] as String : null,
      FN_NUMBER: map['FN_NUMBER'] != null ? map['FN_NUMBER'] as String : null,
      FN_SUFFIX: map['FN_SUFFIX'] != null ? map['FN_SUFFIX'] as String : null,
      DAY_OF_ORIGIN:
          map['DAY_OF_ORIGIN'] != null ? map['DAY_OF_ORIGIN'] as String : null,
      AC_OWNER: map['AC_OWNER'] != null ? map['AC_OWNER'] as String : null,
      AC_SUBTYPE:
          map['AC_SUBTYPE'] != null ? map['AC_SUBTYPE'] as String : null,
      AC_VERSION:
          map['AC_VERSION'] != null ? map['AC_VERSION'] as String : null,
      AC_REGISTRATION: map['AC_REGISTRATION'] != null
          ? map['AC_REGISTRATION'] as String
          : null,
      DEP_AP_ACTUAL:
          map['DEP_AP_ACTUAL'] != null ? map['DEP_AP_ACTUAL'] as String : null,
      DEP_AP_SCHED:
          map['DEP_AP_SCHED'] != null ? map['DEP_AP_SCHED'] as String : null,
      DEP_DT_EST:
          map['DEP_DT_EST'] != null ? map['DEP_DT_EST'] as String : null,
      DEP_SCHED_DT:
          map['DEP_SCHED_DT'] != null ? map['DEP_SCHED_DT'] as String : null,
      ARR_AP_ACTUAL:
          map['ARR_AP_ACTUAL'] != null ? map['ARR_AP_ACTUAL'] as String : null,
      ARR_AP_SCHED:
          map['ARR_AP_SCHED'] != null ? map['ARR_AP_SCHED'] as String : null,
      ARR_DT_EST:
          map['ARR_DT_EST'] != null ? map['ARR_DT_EST'] as String : null,
      ARR_SCHED_DT:
          map['ARR_SCHED_DT'] != null ? map['ARR_SCHED_DT'] as String : null,
      SLOT_TIME_ACTUAL: map['SLOT_TIME_ACTUAL'] != null
          ? map['SLOT_TIME_ACTUAL'] as String
          : null,
      LEG_TYPE: map['LEG_TYPE'] != null ? map['LEG_TYPE'] as String : null,
      EMPLOYER_COCKPIT: map['EMPLOYER_COCKPIT'] != null
          ? map['EMPLOYER_COCKPIT'] as String
          : null,
      EMPLOYER_CABIN: map['EMPLOYER_CABIN'] != null
          ? map['EMPLOYER_CABIN'] as String
          : null,
//      CYCLES: map['CYCLES'] != null ? map['CYCLES'] as String : null,
      DELAY_CODE_01:
          map['DELAY_CODE_01'] != null ? map['DELAY_CODE_01'] as String : null,
      DELAY_CODE_02:
          map['DELAY_CODE_02'] != null ? map['DELAY_CODE_02'] as String : null,
      DELAY_CODE_03:
          map['DELAY_CODE_03'] != null ? map['DELAY_CODE_03'] as String : null,
      DELAY_CODE_04:
          map['DELAY_CODE_04'] != null ? map['DELAY_CODE_04'] as String : null,
      DELAY_TIME_01:
          map['DELAY_TIME_01'] != null ? map['DELAY_TIME_01'] as String : null,
      DELAY_TIME_02:
          map['DELAY_TIME_02'] != null ? map['DELAY_TIME_02'] as String : null,
      DELAY_TIME_03:
          map['DELAY_TIME_03'] != null ? map['DELAY_TIME_03'] as String : null,
      DELAY_TIME_04:
          map['DELAY_TIME_04'] != null ? map['DELAY_TIME_04'] as String : null,
      SUBDELAY_CODE_01: map['SUBDELAY_CODE_01'] != null
          ? map['SUBDELAY_CODE_01'] as String
          : null,
      SUBDELAY_CODE_02: map['SUBDELAY_CODE_02'] != null
          ? map['SUBDELAY_CODE_02'] as String
          : null,
      SUBDELAY_CODE_03: map['SUBDELAY_CODE_03'] != null
          ? map['SUBDELAY_CODE_03'] as String
          : null,
      SUBDELAY_CODE_04: map['SUBDELAY_CODE_04'] != null
          ? map['SUBDELAY_CODE_04'] as String
          : null,
      PAX_BOOKED_C:
          map['PAX_BOOKED_C'] != null ? map['PAX_BOOKED_C'] as String : null,
      PAX_BOOKED_Y:
          map['PAX_BOOKED_Y'] != null ? map['PAX_BOOKED_Y'] as String : null,
      PAX_BOOKED_TRS_C: map['PAX_BOOKED_TRS_C'] != null
          ? map['PAX_BOOKED_TRS_C'] as String
          : null,
      PAX_BOOKED_TRS_Y: map['PAX_BOOKED_TRS_Y'] != null
          ? map['PAX_BOOKED_TRS_Y'] as String
          : null,
      PAD_BOOKED_C:
          map['PAD_BOOKED_C'] != null ? map['PAD_BOOKED_C'] as String : null,
      PAD_BOOKED_Y:
          map['PAD_BOOKED_Y'] != null ? map['PAD_BOOKED_Y'] as String : null,
      OFFBLOCK_DT_A:
          map['OFFBLOCK_DT_A'] != null ? map['OFFBLOCK_DT_A'] as String : null,
      AIRBORNE_DT_A:
          map['AIRBORNE_DT_A'] != null ? map['AIRBORNE_DT_A'] as String : null,
      LANDING_DT_A:
          map['LANDING_DT_A'] != null ? map['LANDING_DT_A'] as String : null,
      ONBLOCK_DT_A:
          map['ONBLOCK_DT_A'] != null ? map['ONBLOCK_DT_A'] as String : null,
      OFFBLOCK_DT_F:
          map['OFFBLOCK_DT_F'] != null ? map['OFFBLOCK_DT_F'] as String : null,
      AIRBORNE_DT_F:
          map['AIRBORNE_DT_F'] != null ? map['AIRBORNE_DT_F'] as String : null,
      LANDING_DT_F:
          map['LANDING_DT_F'] != null ? map['LANDING_DT_F'] as String : null,
      ONBLOCK_DT_F:
          map['ONBLOCK_DT_F'] != null ? map['ONBLOCK_DT_F'] as String : null,
      OFFBLOCK_DT_M:
          map['OFFBLOCK_DT_M'] != null ? map['OFFBLOCK_DT_M'] as String : null,
      AIRBORNE_DT_M:
          map['AIRBORNE_DT_M'] != null ? map['AIRBORNE_DT_M'] as String : null,
      LANDING_DT_M:
          map['LANDING_DT_M'] != null ? map['LANDING_DT_M'] as String : null,
      ONBLOCK_DT_M:
          map['ONBLOCK_DT_M'] != null ? map['ONBLOCK_DT_M'] as String : null,
      EET: map['EET'] != null ? map['EET'] as String : null,
    );
  }

  String toJson() => json.encode(toMap());

  factory Acleg.fromJson(String source) =>
      Acleg.fromMap(json.decode(source) as Map<String, dynamic>);

  @override
  String toString() {
    return 'Acleg(LEG_NO: $LEG_NO, FN_CARRIER: $FN_CARRIER, FN_NUMBER: $FN_NUMBER, FN_SUFFIX: $FN_SUFFIX, DAY_OF_ORIGIN: $DAY_OF_ORIGIN, AC_OWNER: $AC_OWNER, AC_SUBTYPE: $AC_SUBTYPE, AC_VERSION: $AC_VERSION, AC_REGISTRATION: $AC_REGISTRATION, DEP_AP_ACTUAL: $DEP_AP_ACTUAL, DEP_AP_SCHED: $DEP_AP_SCHED, DEP_DT_EST: $DEP_DT_EST, DEP_SCHED_DT: $DEP_SCHED_DT, ARR_AP_ACTUAL: $ARR_AP_ACTUAL, ARR_AP_SCHED: $ARR_AP_SCHED, ARR_DT_EST: $ARR_DT_EST, ARR_SCHED_DT: $ARR_SCHED_DT, SLOT_TIME_ACTUAL: $SLOT_TIME_ACTUAL, LEG_TYPE: $LEG_TYPE, EMPLOYER_COCKPIT: $EMPLOYER_COCKPIT, EMPLOYER_CABIN: $EMPLOYER_CABIN, CYCLES: $CYCLES, DELAY_CODE_01: $DELAY_CODE_01, DELAY_CODE_02: $DELAY_CODE_02, DELAY_CODE_03: $DELAY_CODE_03, DELAY_CODE_04: $DELAY_CODE_04, DELAY_TIME_01: $DELAY_TIME_01, DELAY_TIME_02: $DELAY_TIME_02, DELAY_TIME_03: $DELAY_TIME_03, DELAY_TIME_04: $DELAY_TIME_04, SUBDELAY_CODE_01: $SUBDELAY_CODE_01, SUBDELAY_CODE_02: $SUBDELAY_CODE_02, SUBDELAY_CODE_03: $SUBDELAY_CODE_03, SUBDELAY_CODE_04: $SUBDELAY_CODE_04, PAX_BOOKED_C: $PAX_BOOKED_C, PAX_BOOKED_Y: $PAX_BOOKED_Y, PAX_BOOKED_TRS_C: $PAX_BOOKED_TRS_C, PAX_BOOKED_TRS_Y: $PAX_BOOKED_TRS_Y, PAD_BOOKED_C: $PAD_BOOKED_C, PAD_BOOKED_Y: $PAD_BOOKED_Y, OFFBLOCK_DT_A: $OFFBLOCK_DT_A, AIRBORNE_DT_A: $AIRBORNE_DT_A, LANDING_DT_A: $LANDING_DT_A, ONBLOCK_DT_A: $ONBLOCK_DT_A, OFFBLOCK_DT_F: $OFFBLOCK_DT_F, AIRBORNE_DT_F: $AIRBORNE_DT_F, LANDING_DT_F: $LANDING_DT_F, ONBLOCK_DT_F: $ONBLOCK_DT_F, OFFBLOCK_DT_M: $OFFBLOCK_DT_M, AIRBORNE_DT_M: $AIRBORNE_DT_M, LANDING_DT_M: $LANDING_DT_M, ONBLOCK_DT_M: $ONBLOCK_DT_M, EET: $EET)';
  }

  @override
  bool operator ==(covariant Acleg other) {
    if (identical(this, other)) return true;

    return other.LEG_NO == LEG_NO &&
        other.FN_CARRIER == FN_CARRIER &&
        other.FN_NUMBER == FN_NUMBER &&
        other.FN_SUFFIX == FN_SUFFIX &&
        other.DAY_OF_ORIGIN == DAY_OF_ORIGIN &&
        other.AC_OWNER == AC_OWNER &&
        other.AC_SUBTYPE == AC_SUBTYPE &&
        other.AC_VERSION == AC_VERSION &&
        other.AC_REGISTRATION == AC_REGISTRATION &&
        other.DEP_AP_ACTUAL == DEP_AP_ACTUAL &&
        other.DEP_AP_SCHED == DEP_AP_SCHED &&
        other.DEP_DT_EST == DEP_DT_EST &&
        other.DEP_SCHED_DT == DEP_SCHED_DT &&
        other.ARR_AP_ACTUAL == ARR_AP_ACTUAL &&
        other.ARR_AP_SCHED == ARR_AP_SCHED &&
        other.ARR_DT_EST == ARR_DT_EST &&
        other.ARR_SCHED_DT == ARR_SCHED_DT &&
        other.SLOT_TIME_ACTUAL == SLOT_TIME_ACTUAL &&
        other.LEG_TYPE == LEG_TYPE &&
        other.EMPLOYER_COCKPIT == EMPLOYER_COCKPIT &&
        other.EMPLOYER_CABIN == EMPLOYER_CABIN &&
        other.CYCLES == CYCLES &&
        other.DELAY_CODE_01 == DELAY_CODE_01 &&
        other.DELAY_CODE_02 == DELAY_CODE_02 &&
        other.DELAY_CODE_03 == DELAY_CODE_03 &&
        other.DELAY_CODE_04 == DELAY_CODE_04 &&
        other.DELAY_TIME_01 == DELAY_TIME_01 &&
        other.DELAY_TIME_02 == DELAY_TIME_02 &&
        other.DELAY_TIME_03 == DELAY_TIME_03 &&
        other.DELAY_TIME_04 == DELAY_TIME_04 &&
        other.SUBDELAY_CODE_01 == SUBDELAY_CODE_01 &&
        other.SUBDELAY_CODE_02 == SUBDELAY_CODE_02 &&
        other.SUBDELAY_CODE_03 == SUBDELAY_CODE_03 &&
        other.SUBDELAY_CODE_04 == SUBDELAY_CODE_04 &&
        other.PAX_BOOKED_C == PAX_BOOKED_C &&
        other.PAX_BOOKED_Y == PAX_BOOKED_Y &&
        other.PAX_BOOKED_TRS_C == PAX_BOOKED_TRS_C &&
        other.PAX_BOOKED_TRS_Y == PAX_BOOKED_TRS_Y &&
        other.PAD_BOOKED_C == PAD_BOOKED_C &&
        other.PAD_BOOKED_Y == PAD_BOOKED_Y &&
        other.OFFBLOCK_DT_A == OFFBLOCK_DT_A &&
        other.AIRBORNE_DT_A == AIRBORNE_DT_A &&
        other.LANDING_DT_A == LANDING_DT_A &&
        other.ONBLOCK_DT_A == ONBLOCK_DT_A &&
        other.OFFBLOCK_DT_F == OFFBLOCK_DT_F &&
        other.AIRBORNE_DT_F == AIRBORNE_DT_F &&
        other.LANDING_DT_F == LANDING_DT_F &&
        other.ONBLOCK_DT_F == ONBLOCK_DT_F &&
        other.OFFBLOCK_DT_M == OFFBLOCK_DT_M &&
        other.AIRBORNE_DT_M == AIRBORNE_DT_M &&
        other.LANDING_DT_M == LANDING_DT_M &&
        other.ONBLOCK_DT_M == ONBLOCK_DT_M &&
        other.EET == EET;
  }

  @override
  int get hashCode {
    return LEG_NO.hashCode ^
        FN_CARRIER.hashCode ^
        FN_NUMBER.hashCode ^
        FN_SUFFIX.hashCode ^
        DAY_OF_ORIGIN.hashCode ^
        AC_OWNER.hashCode ^
        AC_SUBTYPE.hashCode ^
        AC_VERSION.hashCode ^
        AC_REGISTRATION.hashCode ^
        DEP_AP_ACTUAL.hashCode ^
        DEP_AP_SCHED.hashCode ^
        DEP_DT_EST.hashCode ^
        DEP_SCHED_DT.hashCode ^
        ARR_AP_ACTUAL.hashCode ^
        ARR_AP_SCHED.hashCode ^
        ARR_DT_EST.hashCode ^
        ARR_SCHED_DT.hashCode ^
        SLOT_TIME_ACTUAL.hashCode ^
        LEG_TYPE.hashCode ^
        EMPLOYER_COCKPIT.hashCode ^
        EMPLOYER_CABIN.hashCode ^
        CYCLES.hashCode ^
        DELAY_CODE_01.hashCode ^
        DELAY_CODE_02.hashCode ^
        DELAY_CODE_03.hashCode ^
        DELAY_CODE_04.hashCode ^
        DELAY_TIME_01.hashCode ^
        DELAY_TIME_02.hashCode ^
        DELAY_TIME_03.hashCode ^
        DELAY_TIME_04.hashCode ^
        SUBDELAY_CODE_01.hashCode ^
        SUBDELAY_CODE_02.hashCode ^
        SUBDELAY_CODE_03.hashCode ^
        SUBDELAY_CODE_04.hashCode ^
        PAX_BOOKED_C.hashCode ^
        PAX_BOOKED_Y.hashCode ^
        PAX_BOOKED_TRS_C.hashCode ^
        PAX_BOOKED_TRS_Y.hashCode ^
        PAD_BOOKED_C.hashCode ^
        PAD_BOOKED_Y.hashCode ^
        OFFBLOCK_DT_A.hashCode ^
        AIRBORNE_DT_A.hashCode ^
        LANDING_DT_A.hashCode ^
        ONBLOCK_DT_A.hashCode ^
        OFFBLOCK_DT_F.hashCode ^
        AIRBORNE_DT_F.hashCode ^
        LANDING_DT_F.hashCode ^
        ONBLOCK_DT_F.hashCode ^
        OFFBLOCK_DT_M.hashCode ^
        AIRBORNE_DT_M.hashCode ^
        LANDING_DT_M.hashCode ^
        ONBLOCK_DT_M.hashCode ^
        EET.hashCode;
  }
}
