/*
 *openPilot Log - A FOSS Pilot Logbook Application
 *Copyright (C) 2020  Felix Turowsky
 *
 *This program is free software: you can redistribute it and/or modify
 *it under the terms of the GNU General Public License as published by
 *the Free Software Foundation, either version 3 of the License, or
 *(at your option) any later version.
 *
 *This program is distributed in the hope that it will be useful,
 *but WITHOUT ANY WARRANTY; without even the implied warranty of
 *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *GNU General Public License for more details.
 *
 *You should have received a copy of the GNU General Public License
 *along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
#include "newflightdialog.h"
#include "ui_newflight.h"
#include "src/gui/dialogues/newtaildialog.h"
#include "src/gui/dialogues/newpilotdialog.h"
#include "src/functions/acalc.h"
#include "src/testing/atimer.h"
#include "src/database/adatabase.h"


#include "src/testing/adebug.h"

/////////////////////////////////////// DEBUG /////////////////////////////////////////////////////
void NewFlightDialog::onInputRejected()
{
    auto sender_object = sender();
    auto line_edit = this->findChild<QLineEdit*>(sender_object->objectName());
    DEB << line_edit->objectName() << "Input Rejected - " << line_edit->text();
}
/////////////////////////////////////// DEBUG /////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////
///                                         constants                                           ///
///////////////////////////////////////////////////////////////////////////////////////////////////

static const auto TIME_FORMAT = QLatin1String("hh:mm");
static const auto NAME_RX     = QLatin1String("((\\p{L}+)?('|\\-|,)?(\\p{L}+)?)");
static const auto ADD_NAME_RX = QLatin1String("(\\s?(\\p{L}+('|\\-|,)?\\p{L}+?))?");

static const auto TIME_VALID_RGX       = QRegularExpression(
            "([01]?[0-9]|2[0-3]):?[0-5][0-9]?");
static const auto LOC_VALID_RGX        = QRegularExpression(
            "[a-zA-Z0-9]{1,4}");
static const auto AIRCRAFT_VALID_RGX   = QRegularExpression(
            "\\w+\\-?(\\w+)?");
static const auto NAME_VALID_RGX       = QRegularExpression(
            NAME_RX + ADD_NAME_RX + ADD_NAME_RX + ADD_NAME_RX + ",?\\s?" // up to 4 first names
            + NAME_RX + ADD_NAME_RX + ADD_NAME_RX + ADD_NAME_RX );         // up to 4 last names
static const auto DATE_VALID_RGX       = QRegularExpression(
            "^([1-9][0-9]{3}).?(1[0-2]|0[1-9]).?(3[01]|0[1-9]|[12][0-9])?$");
static const auto SELF_RX              = QRegularExpression(
            "self", QRegularExpression::CaseInsensitiveOption);

static const auto MANDATORY_LINE_EDITS_DISPLAY_NAMES = QMap<int, QLatin1String> {
    {0, QLatin1String("Date of Flight")}, {1, QLatin1String("Departure Airport")},
    {2, QLatin1String("Destination Airport")}, {3, QLatin1String("Time Off Blocks")},
    {4, QLatin1String("Time on Blocks")}, {5, QLatin1String("PIC Name")},
    {6, QLatin1String("Aircraft Registration")}
};





///////////////////////////////////////////////////////////////////////////////////////////////////
///                                      Construction                                           ///
///////////////////////////////////////////////////////////////////////////////////////////////////

/// [F] The general idea for this dialog is this:
/// - Most line edits have validators and completers.
/// - Validators are based on regular expressions, serving as raw input validation
/// - The Completers are based off the database and provide auto-completion
/// - mandatory line edits only emit editing finished if their content has passed
///   raw input validation or focus is lost.
/// - Editing finished triggers validating inputs by mapping them to Database values
///   where required and results in either pass or fail.
/// - A QBitArray is mainained containing the state of validity of the mandatory line edits
/// - The deducted entries are automatically filled if all mandatory entries
/// are valid.
/// - Comitting an entry to the database is only allowed if all mandatory inputs are valid.
///
/// if the user presses "OK", check if all mandatory inputs are valid,
/// check if optional user inputs are valid and commit.
///
/// For the completion and mapping, I have settled on a more low-level approach using
/// Completers based on QStringLists and mapping with QMaps.
///
/// I implemented the Completers and mapping based on a QSqlTableModel which would
/// have been quite nice, since it would keep all data in one place, providing both completion
/// and mapping in one model.
/// But as we have seen before with the more high-level qt classes, they are quite slow on execution
/// when used for tasks they were probably not designed to do.
/// Mapping a registration to an ID for example took around 300ms, which is very
/// noticeable in the UI and not an acceptable user experience. Using QStringLists and QMaps
/// this goes down to around 5ms.

NewFlightDialog::NewFlightDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::NewFlight)
{
    ui->setupUi(this);
    flightEntry = AFlightEntry();
    setup();
    if (ASettings::read(ASettings::NewFlight::FunctionComboBox).toString() == "SIC") {
        ui->picNameLineEdit->setText(DB_NULL);
        ui->secondPilotNameLineEdit->setText("self");
    }
    if(ASettings::read(ASettings::NewFlight::FunctionComboBox).toString() == "PIC"){
        ui->picNameLineEdit->setText("self");
        emit ui->picNameLineEdit->editingFinished();
    }
}

NewFlightDialog::NewFlightDialog(int row_id, QWidget *parent) :
    QDialog(parent),
    ui(new Ui::NewFlight)
{
    ui->setupUi(this);
    flightEntry = aDB()->getFlightEntry(row_id);
    setup();
    formFiller();
}

NewFlightDialog::~NewFlightDialog()
{
    delete ui;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
///                        Methods - setup and maintenance of dialog                            ///
///////////////////////////////////////////////////////////////////////////////////////////////////

void NewFlightDialog::setup()
{
    updateEnabled = true;
    setupButtonGroups();
    setupRawInputValidation();
    setupSignalsAndSlots();
    readSettings();

    // Visually mark mandatory fields
    ui->deptLocLineEdit->setStyleSheet("border: 0.1ex solid #3daee9");
    ui->destLocLineEdit->setStyleSheet("border: 0.1ex solid #3daee9");
    ui->tofbTimeLineEdit->setStyleSheet("border: 0.1ex solid #3daee9");
    ui->tonbTimeLineEdit->setStyleSheet("border: 0.1ex solid #3daee9");
    ui->picNameLineEdit->setStyleSheet("border: 0.1ex solid #3daee9");
    ui->acftLineEdit->setStyleSheet("border: 0.1ex solid #3daee9");

    ui->doftLineEdit->setText(QDate::currentDate().toString(Qt::ISODate));
    emit ui->doftLineEdit->editingFinished();
    ui->deptLocLineEdit->setFocus();
}
void NewFlightDialog::readSettings()
{
    DEB << "Reading Settings...";
    QSettings settings;
    ui->FunctionComboBox->setCurrentText(ASettings::read(ASettings::FlightLogging::Function).toString());
    ui->ApproachComboBox->setCurrentText(ASettings::read(ASettings::FlightLogging::Approach).toString());

    ASettings::read(ASettings::FlightLogging::PilotFlying).toBool() ? ui->PilotFlyingCheckBox->setChecked(true)
                                                          : ui->PilotMonitoringCheckBox->setChecked(true);

    ui->TakeoffSpinBox->setValue(ASettings::read(ASettings::FlightLogging::NumberTakeoffs).toInt());
    ui->TakeoffSpinBox->value() > 0 ? ui->TakeoffCheckBox->setChecked(true)
                                    : ui->TakeoffCheckBox->setChecked(false);
    ui->LandingSpinBox->setValue(ASettings::read(ASettings::FlightLogging::NumberLandings).toInt());
    ui->LandingSpinBox->value() > 0 ? ui->LandingCheckBox->setChecked(true)
                                    : ui->LandingCheckBox->setChecked(false);
    if (ASettings::read(ASettings::FlightLogging::LogIFR).toBool()) {
        ui->IfrCheckBox->setChecked(true);
    } else {
        ui->VfrCheckBox->setChecked(true);
    }

    ui->FlightNumberLineEdit->setText(ASettings::read(ASettings::FlightLogging::FlightNumberPrefix).toString());
    ui->calendarCheckBox->setChecked(ASettings::read(ASettings::FlightLogging::PopupCalendar).toBool());

}

void NewFlightDialog::writeSettings()
{
    DEB << "Writing Settings...";

    ASettings::write(ASettings::FlightLogging::Function, ui->FunctionComboBox->currentText());
    ASettings::write(ASettings::FlightLogging::Approach, ui->ApproachComboBox->currentText());
    ASettings::write(ASettings::FlightLogging::PilotFlying, ui->PilotFlyingCheckBox->isChecked());
    ASettings::write(ASettings::FlightLogging::NumberTakeoffs, ui->TakeoffSpinBox->value());
    ASettings::write(ASettings::FlightLogging::NumberLandings, ui->LandingSpinBox->value());
    ASettings::write(ASettings::FlightLogging::LogIFR, ui->IfrCheckBox->isChecked());
    ASettings::write(ASettings::FlightLogging::PopupCalendar, ui->calendarCheckBox->isChecked());
}

void NewFlightDialog::setupButtonGroups()
{
    QButtonGroup *FlightRulesGroup = new QButtonGroup(this);
    FlightRulesGroup->addButton(ui->IfrCheckBox);
    FlightRulesGroup->addButton(ui->VfrCheckBox);

    QButtonGroup *PilotTaskGroup = new QButtonGroup(this);
    PilotTaskGroup->addButton(ui->PilotFlyingCheckBox);
    PilotTaskGroup->addButton(ui->PilotMonitoringCheckBox);
}

void NewFlightDialog::setupRawInputValidation()
{
    // get Maps
    pilotsIdMap      = aDB()->getIdMap(ADatabaseTarget::pilots);
    tailsIdMap       = aDB()->getIdMap(ADatabaseTarget::tails);
    airportIcaoIdMap = aDB()->getIdMap(ADatabaseTarget::airport_identifier_icao);
    airportIataIdMap = aDB()->getIdMap(ADatabaseTarget::airport_identifier_iata);
    airportNameIdMap = aDB()->getIdMap(ADatabaseTarget::airport_names);
    //get Completer Lists
    pilotList   = aDB()->getCompletionList(ADatabaseTarget::pilots);
    tailsList   = aDB()->getCompletionList(ADatabaseTarget::registrations);
    airportList = aDB()->getCompletionList(ADatabaseTarget::airport_identifier_all);
    auto tempList = QStringList();
    // define tuples
    const std::tuple<QString, QStringList*, QRegularExpression>
            location_line_edit_settings = {"Loc", &airportList, LOC_VALID_RGX};
    const std::tuple<QString, QStringList*, QRegularExpression>
            name_line_edit_settings = {"Name", &pilotList, NAME_VALID_RGX};
    const std::tuple<QString, QStringList*, QRegularExpression>
            acft_line_edit_settings = {"acft", &tailsList, AIRCRAFT_VALID_RGX};
    const std::tuple<QString, QStringList*, QRegularExpression>
            time_line_edit_settings = {"Time", &tempList, TIME_VALID_RGX};
    const QList<std::tuple<QString, QStringList*, QRegularExpression>> line_edit_settings = {
        location_line_edit_settings,
        name_line_edit_settings,
        acft_line_edit_settings,
        time_line_edit_settings
    };
    //get line edits, set up completers and validators
    auto line_edits = ui->flightDataTab->findChildren<QLineEdit*>();

    for (const auto &item : line_edit_settings) {
        for (const auto &line_edit : line_edits) {
            if(line_edit->objectName().contains(std::get<0>(item))) {
                DEB << "Setting up: " << line_edit->objectName();
                // Set Validator
                auto validator = new QRegularExpressionValidator(std::get<2>(item), line_edit);
                line_edit->setValidator(validator);
                // Set Completer
                auto completer = new QCompleter(*std::get<1>(item), line_edit);
                completer->setCaseSensitivity(Qt::CaseInsensitive);
                completer->setCompletionMode(QCompleter::PopupCompletion);
                completer->setFilterMode(Qt::MatchContains);
                line_edit->setCompleter(completer);
            }
        }
    }
    // populate Mandatory Line Edits list and prepare QBitArray
    mandatoryLineEdits = {
        ui->doftLineEdit,
        ui->deptLocLineEdit,
        ui->destLocLineEdit,
        ui->tofbTimeLineEdit,
        ui->tonbTimeLineEdit,
        ui->picNameLineEdit,
        ui->acftLineEdit,
    };
    primaryTimeLineEdits = {
        ui->tofbTimeLineEdit,
        ui->tonbTimeLineEdit
    };
    pilotsLineEdits = {
        ui->picNameLineEdit,
        ui->secondPilotNameLineEdit,
        ui->thirdPilotNameLineEdit
    };

    mandatoryLineEditsGood.resize(mandatoryLineEdits.size());
}

void NewFlightDialog::setupSignalsAndSlots()
{
    auto line_edits = this->findChildren<QLineEdit*>();

    for (const auto &line_edit : line_edits){
        /////////////////////////////////////// DEBUG /////////////////////////////////////////////////////
        QObject::connect(line_edit, &QLineEdit::inputRejected,
                         this, &NewFlightDialog::onInputRejected);
        /////////////////////////////////////// DEBUG /////////////////////////////////////////////////////
        line_edit->installEventFilter(this);
        if(line_edit->objectName().contains("Loc")){
            QObject::connect(line_edit, &QLineEdit::textChanged,
                             this, &NewFlightDialog::onToUpperTriggered_textChanged);
        }
        if(line_edit->objectName().contains("acft")){
            QObject::connect(line_edit, &QLineEdit::textChanged,
                             this, &NewFlightDialog::onToUpperTriggered_textChanged);
        }
        if(line_edit->objectName().contains("Name")){
            QObject::connect(line_edit, &QLineEdit::editingFinished,
                             this, &NewFlightDialog::onPilotNameLineEdit_editingFinished);
        }
        if(line_edit->objectName().contains("Time")){
            QObject::connect(line_edit, &QLineEdit::editingFinished,
                             this, &NewFlightDialog::onTimeLineEdit_editingFinished);
        }
    }
    for (const auto &line_edit : mandatoryLineEdits) {
        if(line_edit->objectName().contains("doft"))
            break;
        QObject::connect(line_edit->completer(), QOverload<const QString &>::of(&QCompleter::highlighted),
                         this, &NewFlightDialog::onCompleter_highlighted);
        QObject::connect(line_edit->completer(), QOverload<const QString &>::of(&QCompleter::activated),
                         this, &NewFlightDialog::onCompleter_activated);
    }
}


void NewFlightDialog::setPopUpCalendarEnabled(bool state)
{
    ui->flightDataTabWidget->setCurrentIndex(0);
    ui->flightDataTabWidget->removeTab(2); // hide calendar widget

    if (state) {
        DEB << "Enabling pop-up calendar widget...";
        ui->calendarWidget->installEventFilter(this);
        ui->placeLabel1->installEventFilter(this);
        ui->doftLineEdit->installEventFilter(this);

        QObject::connect(ui->calendarWidget, &QCalendarWidget::clicked,
                         this, &NewFlightDialog::onCalendarWidget_clicked);
        QObject::connect(ui->calendarWidget, &QCalendarWidget::activated,
                         this, &NewFlightDialog::onCalendarWidget_selected);
    } else {
        DEB << "Disabling pop-up calendar widget...";
        ui->calendarWidget->removeEventFilter(this);
        ui->placeLabel1->removeEventFilter(this);
        ui->doftLineEdit->removeEventFilter(this);
        QObject::disconnect(ui->calendarWidget, &QCalendarWidget::clicked,
                            this, &NewFlightDialog::onCalendarWidget_clicked);
        QObject::disconnect(ui->calendarWidget, &QCalendarWidget::activated,
                            this, &NewFlightDialog::onCalendarWidget_selected);
    }
}

bool NewFlightDialog::eventFilter(QObject* object, QEvent* event)
{
    if (object == ui->doftLineEdit && event->type() == QEvent::MouseButtonPress) {
        onDoftLineEdit_entered();
        return false; // let the event continue to the edit
    }

    auto line_edit = qobject_cast<QLineEdit*>(object);
    if (line_edit != nullptr) {
        if (mandatoryLineEdits.contains(line_edit) && event->type() == QEvent::FocusIn) {
            mandatoryLineEditsGood.setBit(mandatoryLineEdits.indexOf(line_edit), false);
            DEB << "Editing " << line_edit->objectName();
            // set verification bit to false when entering a mandatory line edit
            return false;
        }
        if (mandatoryLineEdits.contains(line_edit) && event->type() == QEvent::KeyPress) {
            // show completion menu when pressing down arrow
            QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
            if (keyEvent->key() == Qt::Key_Down) {
                DEB << "Key down event.";
                line_edit->completer()->complete();
            }
            return false;
        }
        if (line_edit->objectName().contains("Name") && event->type() == QEvent::KeyPress) {
            // show completion menu when pressing down arrow
            QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
            if (keyEvent->key() == Qt::Key_Down) {
                DEB << "Key down event.";
                line_edit->completer()->complete();
            }
            return false;
        }
    }
    return false;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///                                 Methods - Input Processing                                  ///
///////////////////////////////////////////////////////////////////////////////////////////////////
/*!
 * \brief Fills the deductable items in the Dialog based on mandatory data ui selections.
 */
void NewFlightDialog::fillDeductibleData()
{
    // check if mandatory line edits are valid
    if (mandatoryLineEditsGood.count(true) != 7) {
        return;
    }

    //zero out labels and line edits to delete previous calculations
    QList<QLineEdit*>   LE = {ui->tSPSETimeLineEdit, ui->tSPMETimeLineEdit, ui->tMPTimeLineEdit,    ui->tIFRTimeLineEdit,
                              ui->tNIGHTTimeLineEdit,ui->tPICTimeLineEdit,  ui->tPICUSTimeLineEdit, ui->tSICTimeLineEdit,
                              ui->tDUALTimeLineEdit, ui->tFITimeLineEdit,};
    QList<QLabel*>      LB = {ui->tSPSELabel, ui->tSPMELabel,  ui->tMPLabel,  ui->tIFRLabel,  ui->tNIGHTLabel,
                              ui->tPICLabel,  ui->tPICUSLabel, ui->tSICLabel, ui->tDUALLabel, ui->tFILabel};
    for(const auto& widget : LE) {widget->setText(DB_NULL);}
    for(const auto& widget : LB) {widget->setText(DB_NULL_hhmm);}
    //Calculate block time
    auto tofb = QTime::fromString(ui->tofbTimeLineEdit->text(), TIME_FORMAT);
    auto tonb = QTime::fromString(ui->tonbTimeLineEdit->text(), TIME_FORMAT);
    QString block_time = ACalc::blocktime(tofb, tonb).toString(TIME_FORMAT);
    QString block_minutes = QString::number(ACalc::stringToMinutes(block_time));
    ui->tblkTimeLineEdit->setText(block_time);
    // get acft data and fill deductible entries
    auto acft = aDB()->getTailEntry(tailsIdMap.value(ui->acftLineEdit->text()));
    if (acft.getData().isEmpty())
        DEB << "Error: No valid aircraft object available, unable to deterime auto times.";


    // SP SE
    if(acft.getData().value(DB_TAILS_MULTIPILOT).toInt() == 0
            && acft.getData().value(DB_TAILS_MULTIENGINE).toInt() == 0){
        ui->tSPSETimeLineEdit->setText(block_time);
        ui->tSPSELabel->setText(block_time);
    }
    // SP ME
    if(acft.getData().value(DB_TAILS_MULTIPILOT).toInt() == 0
            && acft.getData().value(DB_TAILS_MULTIENGINE).toInt() == 1){
        ui->tSPMETimeLineEdit->setText(block_time);
        ui->tSPMELabel->setText(block_time);
    }
    // MP
    if(acft.getData().value(DB_TAILS_MULTIPILOT).toInt() == 1){
        ui->tMPTimeLineEdit->setText(block_time);
        ui->tMPLabel->setText(block_time);
    }
    // TOTAL
    ui->tblkLabel->setText("<b>" + block_time + "</b>");
    ui->tblkLabel->setStyleSheet("color: green;");
    ui->submissonReadyLabel->setText("Ready for Submission");
    ui->submissonReadyLabel->setStyleSheet("color: green;");
    // IFR
    if(ui->IfrCheckBox->isChecked()){
        ui->tIFRTimeLineEdit->setText(block_time);
        ui->tIFRLabel->setText(block_time);
    }
    // Night
    QString dept_date = ui->doftLineEdit->text() + 'T' + tofb.toString(TIME_FORMAT);
    QDateTime dept_date_time = QDateTime::fromString(dept_date,"yyyy-MM-ddThh:mm");
    int tblk = block_minutes.toInt();
    const int night_angle = ASettings::read(ASettings::FlightLogging::NightAngle).toInt();
    QString night_minutes = QString::number(
                ACalc::calculateNightTime(
                    ui->deptLocLineEdit->text(),
                    ui->destLocLineEdit->text(),
                    dept_date_time,
                    tblk,
                    night_angle));

    ui->tNIGHTTimeLineEdit->setText(ACalc::minutesToString(night_minutes));
    ui->tNIGHTLabel->setText(ACalc::minutesToString(night_minutes));
    // Function times
    switch (ui->FunctionComboBox->currentIndex()) {
    case 0://PIC
        ui->tPICTimeLineEdit->setText(block_time);
        ui->tPICLabel->setText(block_time);
        break;
    case 1://PICus
        ui->tPICUSTimeLineEdit->setText(block_time);
        ui->tPICUSLabel->setText(block_time);
        break;
    case 2://Co-Pilot
        ui->tSICTimeLineEdit->setText(block_time);
        ui->tSICLabel->setText(block_time);
        break;
    case 3://Dual
        ui->tDUALTimeLineEdit->setText(block_time);
        ui->tDUALLabel->setText(block_time);
        break;
    case 4://Instructor
        ui->tFITimeLineEdit->setText(block_time);
        ui->tFILabel->setText(block_time);
        ui->tPICTimeLineEdit->setText(block_time);
        ui->tPICLabel->setText(block_time);
    }
}
/*!
 * \brief Collect input and create a Data map for the entry object.
 *
 * This function should only be called if input validation has been passed, since
 * no input validation is done in this step and input data is assumed to be valid.
 * \return
 */
RowData NewFlightDialog::collectInput()
{
    RowData newData;
    DEB << "Collecting Input...";
    // Mandatory data
    newData.insert(DB_FLIGHTS_DOFT, ui->doftLineEdit->text());
    newData.insert(DB_FLIGHTS_DEPT, ui->deptLocLineEdit->text());
    newData.insert(DB_FLIGHTS_TOFB, ACalc::stringToMinutes(ui->tofbTimeLineEdit->text()));
    newData.insert(DB_FLIGHTS_DEST, ui->destLocLineEdit->text());
    newData.insert(DB_FLIGHTS_TONB, ACalc::stringToMinutes(ui->tonbTimeLineEdit->text()));
    //Block Time
    const auto tofb = QTime::fromString(ui->tofbTimeLineEdit->text(), TIME_FORMAT);
    const auto tonb = QTime::fromString(ui->tonbTimeLineEdit->text(), TIME_FORMAT);
    const QString block_time = ACalc::blocktime(tofb, tonb).toString(TIME_FORMAT);
    const int block_minutes = ACalc::stringToMinutes(block_time);

    newData.insert(DB_FLIGHTS_TBLK, block_minutes);
    // Aircraft
    newData.insert(DB_FLIGHTS_ACFT, tailsIdMap.value(ui->acftLineEdit->text()));
    // Pilots
    newData.insert(DB_FLIGHTS_PIC, pilotsIdMap.value(ui->picNameLineEdit->text()));
    newData.insert(DB_FLIGHTS_SECONDPILOT, pilotsIdMap.value(ui->secondPilotNameLineEdit->text()));
    newData.insert(DB_FLIGHTS_THIRDPILOT, pilotsIdMap.value(ui->thirdPilotNameLineEdit->text()));

    // Extra Times
    ui->tSPSETimeLineEdit->text().isEmpty() ?
                newData.insert(DB_FLIGHTS_TSPSE, DB_NULL)
              : newData.insert(DB_FLIGHTS_TSPSE, ACalc::stringToMinutes(ui->tSPSETimeLineEdit->text()));

    ui->tSPMETimeLineEdit->text().isEmpty() ?
                newData.insert(DB_FLIGHTS_TSPME, DB_NULL)
              : newData.insert(DB_FLIGHTS_TSPME, ACalc::stringToMinutes(ui->tSPMETimeLineEdit->text()));
    ui->tMPTimeLineEdit->text().isEmpty() ?
                newData.insert(DB_FLIGHTS_TMP, DB_NULL)
              : newData.insert(DB_FLIGHTS_TMP, ACalc::stringToMinutes(ui->tMPTimeLineEdit->text()));

    if (ui->IfrCheckBox->isChecked()) {
        newData.insert(DB_FLIGHTS_TIFR, block_minutes);
    } else {
        newData.insert(DB_FLIGHTS_TIFR, DB_NULL);
    }
    // Night
    const auto dept_date = ui->doftLineEdit->text() + QStringLiteral("T") + tofb.toString(TIME_FORMAT);
    const auto dept_date_time = QDateTime::fromString(dept_date, QStringLiteral("yyyy-MM-ddThh:mm"));

    const auto night_angle = ASettings::read(ASettings::FlightLogging::NightAngle).toInt();
    const auto night_minutes = ACalc::calculateNightTime(
                    ui->deptLocLineEdit->text(),
                    ui->destLocLineEdit->text(),
                    dept_date_time,
                    block_minutes,
                    night_angle);
    newData.insert(DB_FLIGHTS_TNIGHT, night_minutes);

    // Function times - This is a little explicit but these are mutually exclusive so its better to be safe than sorry here.
    switch (ui->FunctionComboBox->currentIndex()) {
    case 0://PIC
        newData.insert(DB_FLIGHTS_TPIC, block_minutes);
        newData.insert(DB_FLIGHTS_TPICUS, DB_NULL);
        newData.insert(DB_FLIGHTS_TSIC, DB_NULL);
        newData.insert(DB_FLIGHTS_TDUAL, DB_NULL);
        newData.insert(DB_FLIGHTS_TFI, DB_NULL);
        break;
    case 1://PICUS
        newData.insert(DB_FLIGHTS_TPIC, DB_NULL);
        newData.insert(DB_FLIGHTS_TPICUS, block_minutes);
        newData.insert(DB_FLIGHTS_TSIC, DB_NULL);
        newData.insert(DB_FLIGHTS_TDUAL, DB_NULL);
        newData.insert(DB_FLIGHTS_TFI, DB_NULL);
        break;
    case 2://Co-Pilot
        newData.insert(DB_FLIGHTS_TPIC, DB_NULL);
        newData.insert(DB_FLIGHTS_TPICUS, DB_NULL);
        newData.insert(DB_FLIGHTS_TSIC, block_minutes);
        newData.insert(DB_FLIGHTS_TDUAL, DB_NULL);
        newData.insert(DB_FLIGHTS_TFI, DB_NULL);
        break;
    case 3://Dual
        newData.insert(DB_FLIGHTS_TPIC, DB_NULL);
        newData.insert(DB_FLIGHTS_TPICUS, DB_NULL);
        newData.insert(DB_FLIGHTS_TSIC, DB_NULL);
        newData.insert(DB_FLIGHTS_TDUAL, block_minutes);
        newData.insert(DB_FLIGHTS_TFI, DB_NULL);
        break;
    case 4://Instructor
        newData.insert(DB_FLIGHTS_TPIC, block_minutes);
        newData.insert(DB_FLIGHTS_TPICUS, DB_NULL);
        newData.insert(DB_FLIGHTS_TSIC, DB_NULL);
        newData.insert(DB_FLIGHTS_TDUAL, DB_NULL);
        newData.insert(DB_FLIGHTS_TFI, block_minutes);
    }
    // Pilot Flying
    newData.insert(DB_FLIGHTS_PILOTFLYING, ui->PilotFlyingCheckBox->isChecked());
    // TO and LDG - again a bit explicit, but we  need to check for both night to day as well as day to night transitions.
    if (ui->TakeoffCheckBox->isChecked()) {
        if (night_minutes == 0) { // all day
            newData.insert(DB_FLIGHTS_TODAY, ui->TakeoffSpinBox->value());
            newData.insert(DB_FLIGHTS_TONIGHT, 0);
        } else if (night_minutes == block_minutes) { // all night
            newData.insert(DB_FLIGHTS_TODAY, 0);
            newData.insert(DB_FLIGHTS_TONIGHT, ui->TakeoffSpinBox->value());
        } else {
            if(ACalc::isNight(ui->deptLocLineEdit->text(), dept_date_time,  night_angle)) {
                newData.insert(DB_FLIGHTS_TODAY, 0);
                newData.insert(DB_FLIGHTS_TONIGHT, ui->TakeoffSpinBox->value());
            } else {
                newData.insert(DB_FLIGHTS_TODAY, ui->TakeoffSpinBox->value());
                newData.insert(DB_FLIGHTS_TONIGHT, 0);
            }
        }
    } else {
        newData.insert(DB_FLIGHTS_TODAY, 0);
        newData.insert(DB_FLIGHTS_TONIGHT, 0);
    }

    if (ui->LandingCheckBox->isChecked()) {
        if (night_minutes == 0) { // all day
            newData.insert(DB_FLIGHTS_LDGDAY, ui->LandingSpinBox->value());
            newData.insert(DB_FLIGHTS_LDGNIGHT, 0);
        } else if (night_minutes == block_minutes) { // all night
            newData.insert(DB_FLIGHTS_LDGDAY, 0);
            newData.insert(DB_FLIGHTS_LDGNIGHT, ui->LandingSpinBox->value());
        } else { //check
            const auto dest_date = ui->doftLineEdit->text() + QStringLiteral("T") + tonb.toString(TIME_FORMAT);
            const auto dest_date_time = QDateTime::fromString(dest_date, QStringLiteral("yyyy-MM-ddThh:mm"));
            if (ACalc::isNight(ui->destLocLineEdit->text(), dest_date_time,  night_angle)) {
                newData.insert(DB_FLIGHTS_LDGDAY, 0);
                newData.insert(DB_FLIGHTS_LDGNIGHT, ui->LandingSpinBox->value());
            } else {
                newData.insert(DB_FLIGHTS_LDGDAY, ui->LandingSpinBox->value());
                newData.insert(DB_FLIGHTS_LDGNIGHT, 0);
            }
        }
    } else {
        newData.insert(DB_FLIGHTS_LDGDAY, 0);
        newData.insert(DB_FLIGHTS_LDGNIGHT, 0);
    }

    newData.insert(DB_FLIGHTS_AUTOLAND, ui->AutolandSpinBox->value());
    newData.insert(DB_FLIGHTS_APPROACHTYPE, ui->ApproachComboBox->currentText());
    newData.insert(DB_FLIGHTS_FLIGHTNUMBER, ui->FlightNumberLineEdit->text());
    newData.insert(DB_FLIGHTS_REMARKS, ui->RemarksLineEdit->text());

    DEB << "New Flight Data: " << newData;

    return newData;
}

void NewFlightDialog::formFiller()
{
    DEB << "Filling Line Edits...";
    // get Line Edits
    auto line_edits = this->findChildren<QLineEdit *>();
    QStringList line_edits_names;
    for (const auto& le : line_edits) {
        line_edits_names << le->objectName();
    }

    ui->acftLineEdit->setText(flightEntry.getRegistration());
    line_edits_names.removeOne("acftLineEdit");

    for (const auto& data_key : flightEntry.getData().keys()) {
        auto rx = QRegularExpression(data_key + "LineEdit");//acftLineEdit
        for(const auto& leName : line_edits_names){
            if(rx.match(leName).hasMatch())  {
                //DEB << "Loc Match found: " << key << " - " << leName);
                auto line_edit = this->findChild<QLineEdit *>(leName);
                if(line_edit != nullptr){
                    line_edit->setText(flightEntry.getData().value(data_key).toString());
                    line_edits_names.removeOne(leName);
                }
                break;
            }
        }
        rx = QRegularExpression(data_key + "Loc\\w+?");
        for(const auto& leName : line_edits_names){
            if(rx.match(leName).hasMatch())  {
                //DEB << "Loc Match found: " << key << " - " << leName);
                auto line_edit = this->findChild<QLineEdit *>(leName);
                if(line_edit != nullptr){
                    line_edit->setText(flightEntry.getData().value(data_key).toString());
                    line_edits_names.removeOne(leName);
                }
                break;
            }
        }
        rx = QRegularExpression(data_key + "Time\\w+?");
        for(const auto& leName : line_edits_names){
            if(rx.match(leName).hasMatch())  {
                //DEB << "Time Match found: " << key << " - " << leName);
                auto line_edits = this->findChild<QLineEdit *>(leName);
                if(line_edits != nullptr){
                    DEB << "Setting " << line_edits->objectName() << " to " << ACalc::minutesToString(flightEntry.getData().value(data_key).toInt());
                    line_edits->setText(ACalc::minutesToString(
                                            flightEntry.getData().value(data_key).toInt()));
                    line_edits_names.removeOne(leName);
                }
                break;
            }
        }
        rx = QRegularExpression(data_key + "Name\\w+?");
        for(const auto& leName : line_edits_names){
            if(rx.match(leName).hasMatch())  {
                auto line_edits = this->findChild<QLineEdit *>(leName);
                if(line_edits != nullptr){
                    DEB << pilotsIdMap.key(1);
                    line_edits->setText(pilotsIdMap.key(flightEntry.getData().value(data_key).toInt()));
                    line_edits_names.removeOne(leName);
                }
                break;
            }
        }
    }
    //FunctionComboBox
    QList<QLineEdit*> function_combo_boxes = {ui->tPICTimeLineEdit, ui->tPICUSTimeLineEdit,
                                              ui->tSICTimeLineEdit, ui->tDUALTimeLineEdit,
                                              ui->tFITimeLineEdit};
    for(const auto& line_edit : function_combo_boxes){
        if(line_edit->text() != "00:00"){
            QString name = line_edit->objectName();
            name.chop(12);
            name.remove(0,1);
            ui->FunctionComboBox->setCurrentText(name);
        }
    }
    // Approach Combo Box
    const QString& app = flightEntry.getData().value(DB_FLIGHTS_APPROACHTYPE).toString();
    if(app != DB_NULL){
        ui->ApproachComboBox->setCurrentText(app);
    }
    // Task and Rules
    qint8 PF = flightEntry.getData().value(DB_FLIGHTS_PILOTFLYING).toInt();
    if (PF > 0) {
        ui->PilotFlyingCheckBox->setChecked(true);
    } else {
        ui->PilotMonitoringCheckBox->setChecked(true);
    }
    qint8 FR = flightEntry.getData().value(DB_FLIGHTS_TIFR).toInt();
    if (FR > 0) {
        ui->IfrCheckBox->setChecked(true);
    } else {
        ui->tIFRTimeLineEdit->setText(DB_NULL);
        ui->VfrCheckBox->setChecked(true);
    }
    // Take Off and Landing
    qint8 TO = flightEntry.getData().value(DB_FLIGHTS_TODAY).toInt()
            + flightEntry.getData().value(DB_FLIGHTS_TONIGHT).toInt();
    qint8 LDG = flightEntry.getData().value(DB_FLIGHTS_LDGDAY).toInt()
            + flightEntry.getData().value(DB_FLIGHTS_LDGNIGHT).toInt();
    if(TO > 0) {
        ui->TakeoffCheckBox->setChecked(true);
        ui->TakeoffSpinBox->setValue(TO);
    } else {
        ui->TakeoffCheckBox->setChecked(false);
        ui->TakeoffSpinBox->setValue(0);
    }
    if(LDG > 0) {
        ui->LandingCheckBox->setChecked(true);
        ui->LandingSpinBox->setValue(LDG);
    } else {
        ui->LandingCheckBox->setChecked(false);
        ui->LandingSpinBox->setValue(0);
    }
    qint8 AL = flightEntry.getData().value(DB_FLIGHTS_AUTOLAND).toInt();
    if(AL > 0) {
        ui->AutolandCheckBox->setChecked(true);
        ui->AutolandSpinBox->setValue(AL);
    }

    for(const auto& le : mandatoryLineEdits){
        emit le->editingFinished();
    }
}

bool NewFlightDialog::isLessOrEqualThanBlockTime(const QString time_string)
{
    if (mandatoryLineEditsGood.count(true) != 7){
        auto message_box = QMessageBox(this);
        message_box.setText("Unable to determine total block time.\n"
                            "Please fill out Departure and Arrival Time\n"
                            "before manually editing these times.");
        message_box.exec();
        return false;
    }

    auto extra_time = QTime::fromString(time_string,TIME_FORMAT);
    auto block_time = ACalc::blocktime(QTime::fromString(
                                           ui->tofbTimeLineEdit->text(),TIME_FORMAT),
                                       QTime::fromString(
                                           ui->tonbTimeLineEdit->text(), TIME_FORMAT));
    if (extra_time <= block_time) {
        return true;
    } else {
        auto message_box = QMessageBox(this);
        message_box.setWindowTitle("Error");
        message_box.setText("The flight time you have entered is longer than the total blocktime:<br><center><b>"
                            + block_time.toString(TIME_FORMAT)
                            + "</b></center>");
        message_box.exec();
        return false;
    }
}

/*!
 * \brief NewFlight::addNewTailMessageBox If the user input is not in the aircraftList, the user
 * is prompted if he wants to add a new entry to the database
 */
void NewFlightDialog::addNewTail(QLineEdit *parent_line_edit)
{
    QMessageBox::StandardButton reply;
    reply = QMessageBox::question(this, "No Aircraft found",
                                  "No aircraft with this registration found.<br>"
                                  "If this is the first time you log a flight with this aircraft, you have to "
                                  "add the registration to the database first.<br><br>Would you like to add a new aircraft to the database?",
                                  QMessageBox::Yes|QMessageBox::No);
    if (reply == QMessageBox::Yes) {
        DEB << "Add new aircraft selected";
        // create and open new aircraft dialog
        auto na = NewTailDialog(ui->acftLineEdit->text(), this);
        na.exec();
        // update map and list, set line edit
        tailsIdMap  = aDB()->getIdMap(ADatabaseTarget::tails);
        tailsList   = aDB()->getCompletionList(ADatabaseTarget::registrations);

        DEB << "New Entry added. Id:" << aDB()->getLastEntry(ADatabaseTarget::tails);
        DEB << "AC Map: " << tailsIdMap;

        parent_line_edit->setText(tailsIdMap.key(aDB()->getLastEntry(ADatabaseTarget::tails)));
        emit parent_line_edit->editingFinished();
    } else {
        parent_line_edit->setText(DB_NULL);
    }
}

/*!
 * \brief NewFlight::addNewPilot If the user input is not in the pilotNameList, the user
 * is prompted if he wants to add a new entry to the database
 */
void NewFlightDialog::addNewPilot(QLineEdit *parent_line_edit)
{
    QMessageBox::StandardButton reply;
    reply = QMessageBox::question(this, "No Pilot found",
                                  "No pilot found.<br>Please enter the Name as"
                                  "<br><br><center><b>Lastname, Firstname</b></center><br><br>"
                                  "If this is the first time you log a flight with this pilot, you have to "
                                  "add the name to the database first.<br><br>Would you like to add a new pilot to the database?",
                                  QMessageBox::Yes|QMessageBox::No);
    if (reply == QMessageBox::Yes) {
        DEB << "Add new pilot selected";
        // create and open new pilot dialog
        auto np = NewPilotDialog(this);
        np.exec();
        // update map and list, set line edit
        pilotsIdMap  = aDB()->getIdMap(ADatabaseTarget::pilots);
        pilotList    = aDB()->getCompletionList(ADatabaseTarget::pilots);
        DEB << "Setting new entry: " << pilotsIdMap.key(aDB()->getLastEntry(ADatabaseTarget::pilots));
        parent_line_edit->setText(pilotsIdMap.key(aDB()->getLastEntry(ADatabaseTarget::pilots)));
        emit parent_line_edit->editingFinished();
    } else {
        parent_line_edit->setText(DB_NULL);
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
///                               Flight Data Tab Slots                                         ///
///////////////////////////////////////////////////////////////////////////////////////////////////

void NewFlightDialog::on_cancelButton_clicked()
{
    DEB << "Cancel Button clicked.";
    reject();
}

void NewFlightDialog::on_submitButton_clicked()
{
    for (const auto &line_edit : mandatoryLineEdits) {
        emit line_edit->editingFinished();
    }
    DEB << "editing finished emitted. good count: " << mandatoryLineEditsGood.count(true);
    if (mandatoryLineEditsGood.count(true) != 7) {
        QString error_message = "Not all mandatory entries are valid.<br>The following"
                                " item(s) are empty or missing:<br><br><center><b>";
        for (int i=0; i < mandatoryLineEditsGood.size(); i++) {
            if (!mandatoryLineEditsGood[i]){
                error_message.append(MANDATORY_LINE_EDITS_DISPLAY_NAMES.value(i) + "<br>");
                mandatoryLineEdits[i]->setStyleSheet("border: 1px solid red");
            }
        }
        error_message.append("</b></center><br>Please go back and fill in the required data.");

        auto message_box = QMessageBox(this);
        message_box.setText(error_message);
        message_box.exec();
        return;
    }

    DEB << "Submit Button clicked. Mandatory good (out of 7): " << mandatoryLineEditsGood.count(true);
    auto newData = collectInput();
    DEB << "Setting Data for flightEntry...";
    flightEntry.setData(newData);
    DEB << "Committing...";
    if (!aDB()->commit(flightEntry)) {
        auto message_box = QMessageBox(this);
        message_box.setText("The following error has ocurred:\n\n"
                            + aDB()->lastError.text()
                            + "\n\nYour entry has not been saved.");
        message_box.setIcon(QMessageBox::Warning);
        message_box.exec();
        return;
    } else {
        QDialog::accept();
    }
}



/*
 * Shared Slots
 */

void NewFlightDialog::onGoodInputReceived(QLineEdit *line_edit)
{
    DEB << line_edit->objectName() << " - Good input received - " << line_edit->text();
    line_edit->setStyleSheet("");

    if (mandatoryLineEdits.contains(line_edit))
        mandatoryLineEditsGood.setBit(mandatoryLineEdits.indexOf(line_edit), true);

    if (mandatoryLineEditsGood.count(true) == 7)
        onMandatoryLineEditsFilled();

    DEB << "Mandatory good: " << mandatoryLineEditsGood.count(true)
        << " (out of 7) " << mandatoryLineEditsGood;

}

void NewFlightDialog::onBadInputReceived(QLineEdit *line_edit)
{
    DEB << line_edit->objectName() << " - Bad input received - " << line_edit->text();
    line_edit->setStyleSheet("border: 1px solid red");

    DEB << "Mandatory Good: " << mandatoryLineEditsGood.count(true) << " out of "
        << mandatoryLineEditsGood.size() << ". Array: " << mandatoryLineEditsGood;
    ui->submissonReadyLabel->setText("INCOMPLETE");
    ui->submissonReadyLabel->setStyleSheet("color: rgb(237, 212, 0);");
}

// capitalize input for dept, dest and registration input
void NewFlightDialog::onToUpperTriggered_textChanged(const QString &text)
{
    auto sender_object = sender();
    auto line_edit = this->findChild<QLineEdit*>(sender_object->objectName());
    DEB << "Text changed - " << line_edit->objectName() << line_edit->text();
    {
        const QSignalBlocker blocker(line_edit);
        line_edit->setText(text.toUpper());
    }
}

// update is disabled if the user chose to manually edit extra times
void NewFlightDialog::onMandatoryLineEditsFilled()
{
    if (!(mandatoryLineEditsGood.count(true) == 7)) {
        DEB << "erroneously called.";
        return;
    };

    if (updateEnabled)
        fillDeductibleData();
    DEB << mandatoryLineEditsGood;
}

// make sure that when using keyboard to scroll through completer sugggestions, line edit is up to date
void NewFlightDialog::onCompleter_highlighted(const QString &completion)
{
    auto sender_object = sender();
    auto line_edit = this->findChild<QLineEdit*>(sender_object->objectName());
    DEB << "Completer highlighted - " << line_edit->objectName() << completion;
    line_edit->setText(completion);
}

void NewFlightDialog::onCompleter_activated(const QString &)
{
    auto sender_object = sender();
    auto line_edit = this->findChild<QLineEdit*>(sender_object->objectName());
    DEB << "Line edit " << line_edit->objectName() << "completer activated.";
    emit line_edit->editingFinished();
}

/*
 *  Date of Flight
 */

void NewFlightDialog::on_doftLineEdit_editingFinished()
{
    auto line_edit = ui->doftLineEdit;
    auto text = ui->doftLineEdit->text();
    auto label = ui->doftDisplayLabel;
    DEB << line_edit->objectName() << "Editing finished - " << text;

    auto date = QDate::fromString(text, Qt::ISODate);
    if (date.isValid()) {
        label->setText(date.toString(Qt::TextDate));
        onGoodInputReceived(line_edit);
        return;
    }

    //try to correct input if only numbers are entered, eg 20200101
    if(text.length() == 8) {
        DEB << "Trying to fix input...";
        text.insert(4,'-');
        text.insert(7,'-');
        date = QDate::fromString(text, Qt::ISODate);
        if (date.isValid()) {
            line_edit->setText(date.toString(Qt::ISODate));
            label->setText(date.toString(Qt::TextDate));
            onGoodInputReceived(line_edit);
            return;
        }
    }
    label->setText("Invalid Date.");
    onBadInputReceived(line_edit);
}

void NewFlightDialog::onCalendarWidget_clicked(const QDate &date)
{
    const auto& le = ui->doftLineEdit;
    le->blockSignals(false);
    ui->calendarWidget->hide();
    ui->placeLabel1->resize(ui->placeLabel2->size());
    le->setText(date.toString(Qt::ISODate));
    le->setFocus();
}

void NewFlightDialog::onCalendarWidget_selected(const QDate &date)
{
    ui->calendarWidget->hide();
    ui->placeLabel1->resize(ui->placeLabel2->size());
    ui->doftDisplayLabel->setText(date.toString(Qt::TextDate));
    const auto& le = ui->doftLineEdit;
    le->setText(date.toString(Qt::ISODate));
    le->setFocus();
    le->blockSignals(false);
}

void NewFlightDialog::onDoftLineEdit_entered()
{
    const auto& cw = ui->calendarWidget;
    const auto& le = ui->doftLineEdit;
    const auto& anchor = ui->placeLabel1;

    if(cw->isVisible()){
        le->blockSignals(false);
        cw->hide();
        anchor->resize(ui->placeLabel2->size());
        le->setFocus();
    } else {
        le->blockSignals(true);
        // Determine size based on layout coordinates
        int c1 = anchor->pos().rx();
        int c2 = le->pos().rx();
        int z  = le->size().rwidth();
        int x = (c2 - c1)+ z;
        c1 = anchor->pos().ry();
        c2 = ui->acftLineEdit->pos().ry();
        z  = ui->acftLineEdit->size().height();
        int y = (c2 - c1) + z;
        //Re-size calendar and parent label accordingly
        anchor->resize(x, y);
        cw->setParent(ui->placeLabel1);
        cw->setGeometry(QRect(0, 0, x, y));
        cw->show();
        cw->setFocus();
    }
}

void NewFlightDialog::on_calendarCheckBox_stateChanged(int arg1)
{
    ASettings::write(ASettings::NewFlight::CalendarCheckBox, ui->calendarCheckBox->isChecked());
    DEB << "Calendar check box state changed.";
    switch (arg1) {
    case 0: // unchecked
        setPopUpCalendarEnabled(false);
        break;
    case 2: // checked
        setPopUpCalendarEnabled(true);
        break;
    default:
        break;
    }
}

/*
 * Location Line Edits
 */

void NewFlightDialog::on_deptLocLineEdit_editingFinished()
{
    onLocationEditingFinished(ui->deptLocLineEdit, ui->deptNameLabel);
}

void NewFlightDialog::on_destLocLineEdit_editingFinished()
{
    onLocationEditingFinished(ui->destLocLineEdit, ui->destNameLabel);
}

void NewFlightDialog::onLocationEditingFinished(QLineEdit *line_edit, QLabel *name_label)
{
    const auto &text = line_edit->text();
    //DEB << line_edit->objectName() << " Editing finished. " << text);
    int airport_id = 0;

    // try to map iata or icao code to airport id;
    if (text.length() == 3) {
        airport_id = airportIataIdMap.value(text);
    } else {
        airport_id = airportIcaoIdMap.value(text);
    }
    // check result
    if (airport_id == 0) {
        // to do: prompt user how to handle unknown airport
        name_label->setText("Unknown airport identifier");
        onBadInputReceived(line_edit);
        return;
    }
    line_edit->setText(airportIcaoIdMap.key(airport_id));
    name_label->setText(airportNameIdMap.key(airport_id));
    onGoodInputReceived(line_edit);
}

/*
 * Time Line Edits
 */

void NewFlightDialog::onTimeLineEdit_editingFinished()
{
    auto sender_object = sender();
    auto line_edit = this->findChild<QLineEdit*>(sender_object->objectName());
    DEB << line_edit->objectName() << "Editing Finished -" << line_edit->text();

    line_edit->setText(ACalc::formatTimeInput(line_edit->text()));
    const auto time = QTime::fromString(line_edit->text(),TIME_FORMAT);
    if(time.isValid()){
        if(primaryTimeLineEdits.contains(line_edit)) {
            onGoodInputReceived(line_edit);
        } else { // is extra time line edit
            isLessOrEqualThanBlockTime(line_edit->text());
            line_edit->setText(DB_NULL);
            line_edit->setFocus();
            return;
        }
    } else {
        if (!line_edit->text().isEmpty())
            onBadInputReceived(line_edit);
    }
}

/*
 * Aircraft Line Edit
 */

void NewFlightDialog::on_acftLineEdit_editingFinished()
{
    auto line_edit = ui->acftLineEdit;
    //DEB << line_edit->objectName() << "Editing Finished!" << line_edit->text());

    if (tailsIdMap.value(line_edit->text()) != 0) {
        DEB << "Mapped: " << line_edit->text() << tailsIdMap.value(line_edit->text());
        auto acft = aDB()->getTailEntry(tailsIdMap.value(line_edit->text()));
        ui->acftTypeLabel->setText(acft.type());
        onGoodInputReceived(line_edit);
        return;
    }

    // try to fix input
    if (!line_edit->completer()->currentCompletion().isEmpty()
            && !line_edit->text().isEmpty()) {
        DEB << "Trying to fix input...";
        line_edit->setText(line_edit->completer()->currentCompletion());
        emit line_edit->editingFinished();
        return;
    }

    // to do: promp user to add new
    onBadInputReceived(line_edit);
    ui->acftTypeLabel->setText("Unknown Registration.");
    addNewTail(line_edit);
}

/*
 * Pilot Line Edits
 */

void NewFlightDialog::onPilotNameLineEdit_editingFinished()
{
    auto sender_object = sender();
    auto line_edit = this->findChild<QLineEdit*>(sender_object->objectName());
    //DEB << line_edit->objectName() << "Editing Finished -" << line_edit->text());

    if(line_edit->text().contains(SELF_RX)) {
        DEB << "self recognized.";
        line_edit->setText(pilotsIdMap.key(1));
        auto pilot = aDB()->getPilotEntry(1);
        ui->picCompanyLabel->setText(pilot.getData().value(DB_TAILS_COMPANY).toString());
        onGoodInputReceived(line_edit);
        return;
    }

    if(pilotsIdMap.value(line_edit->text()) != 0) {
        DEB << "Mapped: " << line_edit->text() << pilotsIdMap.value(line_edit->text());
        auto pilot = aDB()->getPilotEntry(pilotsIdMap.value(line_edit->text()));
        ui->picCompanyLabel->setText(pilot.getData().value(DB_TAILS_COMPANY).toString());
        onGoodInputReceived(line_edit);
        return;
    }

    if (line_edit->text().isEmpty()) {
        return;
    }

    if (!line_edit->completer()->currentCompletion().isEmpty()) {
        DEB << "Trying to fix input...";
        line_edit->setText(line_edit->completer()->currentCompletion());
        emit line_edit->editingFinished();
        return;
    }

    // to do: prompt user to add new
    onBadInputReceived(line_edit);
    addNewPilot(line_edit);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
///                               Auto Logging Tab Slots                                        ///
///////////////////////////////////////////////////////////////////////////////////////////////////

void NewFlightDialog::on_setAsDefaultButton_clicked()
{
    writeSettings();
}

void NewFlightDialog::on_restoreDefaultButton_clicked()
{
    readSettings();
}

void NewFlightDialog::on_PilotFlyingCheckBox_stateChanged(int)
{
    DEB << "PF checkbox state changed.";
    if(ui->PilotFlyingCheckBox->isChecked()){
        ui->TakeoffSpinBox->setValue(1);
        ui->TakeoffCheckBox->setCheckState(Qt::Checked);
        ui->LandingSpinBox->setValue(1);
        ui->LandingCheckBox->setCheckState(Qt::Checked);

    }else if(!ui->PilotFlyingCheckBox->isChecked()){
        ui->TakeoffSpinBox->setValue(0);
        ui->TakeoffCheckBox->setCheckState(Qt::Unchecked);
        ui->LandingSpinBox->setValue(0);
        ui->LandingCheckBox->setCheckState(Qt::Unchecked);
    }
}

void NewFlightDialog::on_IfrCheckBox_stateChanged(int)
{
    if (mandatoryLineEditsGood.count(true) == 7 && updateEnabled)
        onMandatoryLineEditsFilled();
}

void NewFlightDialog::on_manualEditingCheckBox_stateChanged(int arg1)
{
    if (!(mandatoryLineEditsGood.count(true) == 7) && ui->manualEditingCheckBox->isChecked()) {
        auto message_box = QMessageBox(this);
        message_box.setText("Before editing times manually, please fill out the required fields in the flight data tab,"
                            " so that total time can be calculated.");
        message_box.exec();
        ui->manualEditingCheckBox->setChecked(false);
        return;
    }
    QList<QLineEdit*>   LE = {ui->tSPSETimeLineEdit, ui->tSPMETimeLineEdit, ui->tMPTimeLineEdit,    ui->tIFRTimeLineEdit,
                              ui->tNIGHTTimeLineEdit,ui->tPICTimeLineEdit,  ui->tPICUSTimeLineEdit, ui->tSICTimeLineEdit,
                              ui->tDUALTimeLineEdit, ui->tFITimeLineEdit};
    switch (arg1) {
    case 0:
        for(const auto& le : LE){
            le->setFocusPolicy(Qt::NoFocus);
            le->setStyleSheet("");
        }
        updateEnabled = true;
        if (mandatoryLineEditsGood.count(true) == 7 && updateEnabled)
            onMandatoryLineEditsFilled();
        break;
    case 2:
        for(const auto& le : LE){
            le->setFocusPolicy(Qt::StrongFocus);
        }
        updateEnabled = false;
        break;
    default:
        break;
    }
}

void NewFlightDialog::on_ApproachComboBox_currentTextChanged(const QString &arg1)
{
    if(arg1 == "ILS CAT III"){  //for a CAT III approach an Autoland is mandatory, so we can preselect it.
        ui->AutolandCheckBox->setCheckState(Qt::Checked);
        ui->AutolandSpinBox->setValue(1);
    }else{
        ui->AutolandCheckBox->setCheckState(Qt::Unchecked);
        ui->AutolandSpinBox->setValue(0);
    }
}

void NewFlightDialog::on_FunctionComboBox_currentIndexChanged(int)
{
    if (updateEnabled)
        fillDeductibleData();
}
