#include <QTimeZone>
#include <QString>
#include <QDir>
#include <QDebug>
#include <QMessageBox>
#include <QCoreApplication>

#include "SleepLib/loader_plugins/yuwell_loader.h"

/*
 * Take care of BreathCare ECO and BreathCare I type machines
 * The files either contain single sessions per BYS file OR a single file with all sessions
 *
 * At this stage I don't know which machines have SD-Cards, some may not
 * and if all machines provide full logs rather than just summaries.
 * This is left in the simple state that it is until I receive data from
 * other models. Other models include:
 *
 * YH350 (BreathCare I Auto-CPAP)
 * YH360 (BreathCare I Auto-CPAP)
 * YH450 (BreathCare II Auto-CPAP)
 * YH480 (BreathCare II Auto-CPAP)
 * YH550 (BreathCare ECO Auto-CPAP, A/B Models) (Supported)
 * YH580 (BreathCare I Auto-CPAP) (Supported)
 * YH680 (BreathCare III Auto-CPAP, A/B Models)
 * YH690 (BreathCare III Auto-CPAP, A/B Models)
 * YH720 (BreathCare I Bi-PAP)
 * YH725 (BreathCare I Bi-PAP)
 * YH730 (BreathCare I Bi-PAP)
 * YH820 (BreathCare II Bi-PAP)
 * YH825 (BreathCare II Bi-PAP)
 * YH830 (BreathCare II Bi-PAP)
 * So This will probably change around here to detect each model and if the file
 * format is different, the Open() will have to change as well
 *
 * Fingers crossed the other models with CD-Cards have the same file format.
 * NARRATOR: They weren't
 *
 * NOTES
 * ======
 *
 * So far we have 2 BYS formats, the YH-550 and the YH-580
 *
 * The BYS file contains either a single session or multiple sessions
 * The SD-CARD structure for both machines are completely different
 *
 * Can identify the machine type by looking at the source of truth in the model/serial field
 *
 * The SD-CARDS have a shallow directory structure, only 1 directory deep
 * Don't need to recurse and entire directory tree looking for BYS files
 *
 * Can detect Yuwell data using SD-CARD files and directories and if the model starts with "YH"
 *
 * Can find specific machines by looking at the model/serial fields
 *
 * Can then import data depending on the format of the BYS files and directory locations
 *
 * Call them YuwellFormatA, YuwellFormatB, YuwellFormatC and so on. Iterate over each looking for a match
 *
 * 1. Match the file structure
 * 2. Look for model/serial
 *
 * Model must start with "YH"
 *
 * Do not reject data based on model number (yet)
 */

ChannelID Yuwell_Ramp;
ChannelID Yuwell_Humidity;


bool YuwellFormatA::Detect() {
    /*
     * root - RunLog.bys (0Kb in size)
     *      \ MODEL-SERIAL
     *        \ 0100001.BYS <- Data is in here
     *
     * Models seen
     * -----------
     * YH-550
     */
    QDir cardDir(m_filePath);

    if (!cardDir.exists("RunLog.bys")) {
        return false;
    }

    QStringList model_serials = GetModelSerials();

    if (model_serials.size() == 0) {
        return false;
    }

    qDebug() << "Found Yuwell format A serial number: " << model_serials;
    return true;
}

QStringList YuwellFormatA::GetModelSerials() {
    /*
     * Find a model/serial number from the inside of a file
     * instead of a directory name. Much more source-of-truth
     */
    QDir cardDir(m_filePath);

    cardDir.setFilter(QDir::NoDotAndDotDot | QDir::Dirs | QDir::NoSymLinks);
    cardDir.setSorting(QDir::Name);

    QFileInfoList dlist = cardDir.entryInfoList();

    QStringList model_serials;

    for (int i = 0; i < dlist.size(); i++) {
        // Iterate until we find our first file with a model/serial number
        QFileInfo fi = dlist.at(i);
        QString filename = fi.fileName();

        if (filename.toUpper().startsWith("YH")) {
            // Its tempting to use the directory as the model/serial, but there's a source of truth in each file
            QDir machineDir (m_filePath + "/" + filename);
            machineDir.setFilter(QDir::NoDotAndDotDot | QDir::Files | QDir::NoSymLinks);
            machineDir.setSorting(QDir::Name);

            QStringList filters;
            filters << "*.BYS";
            machineDir.setNameFilters(filters);
            QFileInfoList flist = machineDir.entryInfoList();
            if (flist.size() >= 1) { // Found data
                QFileInfo ex = flist.at(0);
                QFile bysFile(m_filePath + "/" + filename + "/" + ex.fileName());

                if (bysFile.open(QFile::ReadOnly)) {
                    QByteArray header = bysFile.read(0xA0);

                    if (header.size() == 0xA0) {
                        QDataStream in(header);
                        in.setVersion(QDataStream::Qt_4_8);

                        QByteArray raw_model_serial(16, Qt::Uninitialized);

                        in.skipRawData(0x1E);
                        in.readRawData(raw_model_serial.data(), 16);

                        QString model_serial(raw_model_serial);
                        if (model_serial.startsWith("YH") && !model_serials.contains(model_serial)) {
                            model_serials << model_serial;
                        }
                    }
                    bysFile.close();
                }
            }
        }
    }
    return model_serials;
}

int YuwellFormatA::OpenMachine(Machine *mach, const QString & serial)
{
    emit m_loader->updateMessage(QObject::tr("Getting Ready..."));
    emit m_loader->setProgressValue(0);
    QCoreApplication::processEvents();

    QString serialPath = m_filePath + "/" + serial;
    QDir dir(serialPath);

    if (!dir.exists() || (!dir.isReadable())) {
        return -1;
    }

    m_loader->backupData(mach, m_filePath);

    calc_leaks = p_profile->cpap->calculateUnintentionalLeaks();
    lpm4 = p_profile->cpap->custom4cmH2OLeaks();
    lpm20 = p_profile->cpap->custom20cmH2OLeaks();

    dir.setFilter(QDir::NoDotAndDotDot | QDir::Files | QDir::NoSymLinks);
    dir.setSorting(QDir::Name);
    QFileInfoList flist = dir.entryInfoList();

    emit m_loader->updateMessage(QObject::tr("Reading data files..."));
    QCoreApplication::processEvents();

    Sessions.clear();

    QString filename, fpath;
    emit m_loader->setProgressMax(flist.size());

    // There's only one file type with an extension .BYS containing a header and line data
    for (int i = 0; i < flist.size(); i++) {
        emit m_loader->setProgressValue(i);
        QFileInfo fi = flist.at(i);
        filename = fi.fileName();
        fpath = serialPath + "/" + filename;

        OpenSession(mach, fpath);
    }

    int c = Sessions.size();
    qDebug() << "Yuwell Format A Loader found" << c << "sessions";

    emit m_loader->updateMessage(QObject::tr("Finishing up..."));
    QCoreApplication::processEvents();

    m_loader->finish();

    return c;
}

bool YuwellFormatA::OpenSession(Machine *mach, const QString & filename)
{
    QFile file(filename);

    if (!file.open(QFile::ReadOnly)) {
        qWarning() << "Yuwell Session Couldn't open " << filename;
        return false;
    }

    // Read header of summary file, 51 bytes ending in 0xF9
    QByteArray header;
    header = file.read(0x33);

    if (header.size() != 0x33) {
        qWarning() << "Yuwell Session Short file " << filename;
        file.close();
        return false;
    }

    char terminator = 0xf9;
    if (terminator != header.at(0x32)) {
        qWarning() << "Yuwell Session Header missing 0xf9 terminator" << filename;
    }

    QDataStream in(header);
    in.setVersion(QDataStream::Qt_4_8);
    in.setByteOrder(QDataStream::LittleEndian);

    unsigned char start_year, start_month, start_day, start_hour, start_minute, start_second;
    unsigned char finish_year, finish_month, finish_day, finish_hour, finish_minute, finish_second;

    in >> start_year;
    in >> start_month;
    in >> start_day;
    in >> start_hour;
    in >> start_minute;
    in >> start_second;

    in >> finish_year;
    in >> finish_month;
    in >> finish_day;
    in >> finish_hour;
    in >> finish_minute;
    in >> finish_second;

    QDateTime start = QDateTime(QDate((int)start_year + 2000, start_month, start_day), QTime(start_hour, start_minute, start_second), QTimeZone::systemTimeZone());
    QDateTime finish = QDateTime(QDate((int)finish_year + 2000, finish_month, finish_day), QTime(finish_hour, finish_minute, finish_second), QTimeZone::systemTimeZone());

    unsigned char mode, ramp_up_time, initial_pressure, minimum_pressure, maximum_pressure;
    unsigned char humidity_settings;
    unsigned char avg_leak_volume, avg_pressure;
    QByteArray raw_model_serial(16, Qt::Uninitialized);
    short int record_count;

    in >> mode;  // 0x00=CPAP, 0x01=APAP
    in >> ramp_up_time;  // Minutes
    in >> initial_pressure;
    in >> minimum_pressure;
    in >> maximum_pressure;
    in.skipRawData(1);
    in >> humidity_settings;
    in.skipRawData(7);
    in >> avg_leak_volume;
    in.skipRawData(1);
    in >> avg_pressure;
    in.skipRawData(1);
    in.readRawData(raw_model_serial.data(), 16);
    in >> record_count;

    QString model_serial(raw_model_serial);
    mach->setModel(model_serial);

    quint32 ts;
    ts = start.toSecsSinceEpoch();  // Create timestamp from session start
    if (!mach->SessionExists(ts)) {
        Session *sess = new Session(mach, ts);
        sess->really_set_first(qint64(ts) * 1000L);
        sess->really_set_last(qint64(finish.toSecsSinceEpoch()) * 1000L);

        if (mode == YUWELL_CPAP) {
            sess->settings[CPAP_Mode] = (int)MODE_CPAP;
            sess->settings[CPAP_Pressure] = initial_pressure / 10.0;
        } else {
            sess->settings[CPAP_Mode] = (int)MODE_APAP;
            sess->settings[CPAP_PressureMin] = minimum_pressure / 10.0;
            sess->settings[CPAP_PressureMax] = maximum_pressure / 10.0;
        }

        sess->settings[Yuwell_Humidity] = humidity_settings;
        sess->settings[Yuwell_Ramp] = ramp_up_time;

        EventList *LK = sess->AddEventList(CPAP_LeakTotal, EVL_Event, 1);
        EventList *PR = sess->AddEventList(CPAP_Pressure, EVL_Event, 0.1F);
        EventList *OA = sess->AddEventList(CPAP_Obstructive, EVL_Event);
        EventList *CA = sess->AddEventList(CPAP_ClearAirway, EVL_Event);
        EventList *H =  sess->AddEventList(CPAP_Hypopnea, EVL_Event);

        qint64 ti;
        ti = qint64(ts) * 1000L;

        for (int i = 0; i < record_count; i++) {
            QCoreApplication::processEvents();
            QByteArray record;

            record = file.read(0x0a);

            if (record.size() != 0x0a) {
                qWarning() << "Yuwell Session Record Short" << filename;
                file.close();
                return false;
            }

            QDataStream in(record);
            in.setVersion(QDataStream::Qt_4_8);
            in.setByteOrder(QDataStream::LittleEndian);
            
            unsigned char pressure, oai, hi, cai, leak_volume;
            in >> pressure;
            in.skipRawData(2);
            in >> oai;
            in >> hi;
            in >> cai;
            in.skipRawData(3);
            in >> leak_volume;

            PR->AddEvent(ti + (i * 60000), pressure); // Samples are every 60 seconds

            if (oai > 0) {
                OA->AddEvent(ti + (i * 60000), 0);
            }
            if (hi > 0) {
                H->AddEvent(ti + (i * 60000), 0);
            }
            if (cai > 0) {
                CA->AddEvent(ti + (i * 60000), 0);
            }
            if (leak_volume > 0) {
                LK->AddEvent(ti + (i * 60000), leak_volume);
            }
        }
        sess->SetChanged(true);
        Sessions[ts] = sess;
        sess->UpdateSummaries();
        sess->Store(mach->getDataPath());
        mach->AddSession(sess);
    }

    return true;
}

int YuwellFormatA::Open() {
    // Start with m_filePath and do the import
    QStringList model_serials = GetModelSerials();

    Machine *m;

    int c = 0;
    for (int i = 0; i < model_serials.size(); i++) {
        MachineInfo info = m_loader->newInfo();
        info.serial = model_serials[i];
        m = p_profile->CreateMachine(info);
        try {
            if (m) {
                c += OpenMachine(m, model_serials[i]);
                m->Save();
            }
        } catch (OneTypePerDay& e) {
            Q_UNUSED(e)
            p_profile->DelMachine(m);
            QMessageBox::warning(nullptr, m_loader->tr("Import Error"),
                                 m_loader->tr("This device Record cannot be imported in this profile.")+"\n\n"+m_loader->tr("The Day records overlap with already existing content."),
                                 QMessageBox::Ok);
            delete m;
        }
    }

    return c;
}

bool YuwellFormatB::Detect() {
    /*
     * root - YHSD-NEW.BYS (64Kb in size) <- Data is in here
     *      \ YHSD-OLD.BYS <- We don't care about this
     *
     * Models seen
     * -----------
     * YH-580
     */
    QDir cardDir(m_filePath);

    if (!cardDir.exists("YHSD-NEW.BYS")) {
        return false;
    }

    QFileInfo fi = QFileInfo(m_filePath + "/YHSD-NEW.BYS");
    if (fi.size() != 0x10000) { // Needs to be 64Kb in size
        return false;
    }

    QStringList model_serials = GetModelSerials();
    if (model_serials.size() == 0) {
        return false;
    }

    qDebug() << "Found Yuwell Format B serial: " << model_serials;

    return true;
}

QStringList YuwellFormatB::GetModelSerials() {
    /*
     * Find a model/serial number from the inside of a file
     * instead of a directory name. Much more source-of-truth
     */
    QStringList model_serials;
    QFile bysFile(m_filePath + "/YHSD-NEW.BYS");
    if (bysFile.open(QFile::ReadOnly)) {
        QByteArray header = bysFile.read(0xA0);

        if (header.size() == 0xA0) {
            QDataStream in(header);
            in.setVersion(QDataStream::Qt_4_8);

            QByteArray raw_model_serial(16, Qt::Uninitialized);

            in.skipRawData(0x84);
            in.readRawData(raw_model_serial.data(), 16);

            QString model_serial(raw_model_serial);
            if (model_serial.startsWith("YH")) {
                model_serials << model_serial;
            }
        }
        bysFile.close();
    }

    return model_serials;
}

int YuwellFormatB::Open() {
    // Start with m_filePath and do the import
    QStringList model_serials = GetModelSerials();

    Machine *m;

    int c = 0;
    for (int i = 0; i < model_serials.size(); i++) {
        MachineInfo info = m_loader->newInfo();
        info.serial = model_serials[i];
        m = p_profile->CreateMachine(info);
        try {
            if (m) {
                c += OpenMachine(m, model_serials[i]);
                m->Save();
            }
        } catch (OneTypePerDay& e) {
            Q_UNUSED(e)
            p_profile->DelMachine(m);
            QMessageBox::warning(nullptr, m_loader->tr("Import Error"),
                                 m_loader->tr("This device Record cannot be imported in this profile.")+"\n\n"+m_loader->tr("The Day records overlap with already existing content."),
                                 QMessageBox::Ok);
            delete m;
        }
    }

    return c;
}

int YuwellFormatB::OpenMachine(Machine *mach, const QString & serial) {
    emit m_loader->updateMessage(QObject::tr("Getting Ready..."));
    emit m_loader->setProgressValue(0);
    QCoreApplication::processEvents();

    QString test = m_filePath + "/" + serial;
    QDir dir(m_filePath);

    if (!dir.exists() || (!dir.isReadable())) {
        return -1;
    }

    m_loader->backupData(mach, m_filePath);

    // We don't use the serial in the path, its always YHSD-NEW.BYS
    calc_leaks = p_profile->cpap->calculateUnintentionalLeaks();
    lpm4 = p_profile->cpap->custom4cmH2OLeaks();
    lpm20 = p_profile->cpap->custom20cmH2OLeaks();

    emit m_loader->updateMessage(QObject::tr("Reading data files..."));
    QCoreApplication::processEvents();

    Sessions.clear();

    QFile file(m_filePath + "/YHSD-NEW.BYS");

    if (!file.exists()) {
        qWarning() << "Yuwell Session Cannot find " << file.fileName();
        return -1;
    }

    // We just read the file contents from here...
    if (!file.open(QFile::ReadOnly)) {
        qWarning() << "Yuwell Session Couldn't open " << file.fileName();
        return -1;
    }

    QByteArray cpap_data;
    cpap_data = file.read(0x10000);

    if (cpap_data.size() != 0x10000) {
        qWarning() << "Yuwell Session Short file " << file.fileName();
        file.close();
        return -1;
    }

    QDataStream in(cpap_data);
    in.setVersion(QDataStream::Qt_4_8);
    in.setByteOrder(QDataStream::LittleEndian); // Mostly, except for record offsets

    unsigned char mode, ramp, initial_pressure, pressure_setting, maximum_pressure, minimum_pressure, humidity, fps_level;
    unsigned char oai_count, hi_count, avg_leak_vol, avg_pressure, offset_high, offset_low, session_minutes;
    short int record_count;
    short unsigned int offset;
    unsigned char start_year, start_month, start_day, start_hour, start_minute, start_second;
    unsigned char finish_year, finish_month, finish_day, finish_hour, finish_minute, finish_second;

    QByteArray raw_model_serial(16, Qt::Uninitialized);

    in.skipRawData(4); // Magic number "AAAA", but as this may be different for some machines, we don't enfore a check (yet)

    in >> mode;
    in >> ramp;
    in >> initial_pressure;
    in >> pressure_setting;
    in >> maximum_pressure;
    in >> minimum_pressure;
    in >> humidity;
    in >> fps_level;

    in.skipRawData(19);

    in >> record_count;

    in.skipRawData(99);

    in.readRawData(raw_model_serial.data(), 16);

    QString model_serial(raw_model_serial);
    mach->setModel(model_serial);

    in.skipRawData(2924); // Alarming amount of data to skip

    QList<FormatBSessionSummary> sessionSummaries;
    // record_count number of sessions from this point forward, 30 bytes per session summary
    for (int i = 0; i < record_count; i++) {
        in >> start_year;
        in >> start_month;
        in >> start_day;
        in >> start_hour;
        in >> start_minute;
        in >> start_second;

        in >> finish_year;
        in >> finish_month;
        in >> finish_day;
        in >> finish_hour;
        in >> finish_minute;
        in >> finish_second;

        QDateTime start = QDateTime(QDate((int)start_year + 2000, start_month, start_day), QTime(start_hour, start_minute, start_second), QTimeZone::systemTimeZone());
        QDateTime finish = QDateTime(QDate((int)finish_year + 2000, finish_month, finish_day), QTime(finish_hour, finish_minute, finish_second), QTimeZone::systemTimeZone());

        in >> mode;
        in >> ramp;
        in >> initial_pressure;
        in >> pressure_setting;
        in >> maximum_pressure;
        in >> minimum_pressure;
        in >> humidity;
        in >> fps_level;
        in >> oai_count;
        in >> hi_count;
        in.skipRawData(2);
        in >> avg_leak_vol;
        in >> avg_pressure;
        in >> offset_high;
        in >> offset_low;
        /*
         * The offset 0x7600 here seems fairly arbitrary but from examining the header of the file I
         * cannot find any data that could possibly be used to calculate this value. It's fairly odd
         * to have something like this in a data file at a fixed place. So if the imports fail in the
         * future, this is a likely culprit.
         * For reference, the header seems to extent to position 0x0C00 where it starts with session
         * summary information. Then there's nothing of a bunch of 0xFF's until 0x7600.
         */
        offset = ((offset_high << 0x08) + offset_low) + 0x7600;
        in.skipRawData(1);
        in >> session_minutes;

        FormatBSessionSummary sessionSummary = {
            start,
            finish,
            mode,
            ramp,
            initial_pressure,
            pressure_setting,
            maximum_pressure,
            minimum_pressure,
            humidity,
            fps_level,
            oai_count,
            hi_count,
            avg_leak_vol,
            avg_pressure,
            offset,
            session_minutes
        };
        sessionSummaries.append(sessionSummary);
    }

    unsigned char leakage, pressure, spo2, oai, hi, pulse, cai;
    emit m_loader->setProgressMax(sessionSummaries.size());
    int counter = 0;
    for (FormatBSessionSummary summary : sessionSummaries) {
        emit m_loader->setProgressValue(counter++);
        QDataStream in(cpap_data.mid(summary.offset, summary.session_minutes * 7));
        quint32 ts;
        ts = summary.start.toSecsSinceEpoch();  // Create timestamp from session start
        if (!mach->SessionExists(ts)) {
            Session *sess = new Session(mach, ts);
            sess->really_set_first(qint64(ts) * 1000L);
            sess->really_set_last(qint64(summary.finish.toSecsSinceEpoch()) * 1000L);
            if (summary.mode == YUWELL_CPAP) {
                sess->settings[CPAP_Mode] = (int)MODE_CPAP;
                sess->settings[CPAP_Pressure] = summary.initial_pressure / 10.0;
            } else {
                sess->settings[CPAP_Mode] = (int)MODE_APAP;
                sess->settings[CPAP_PressureMin] = summary.minimum_pressure / 10.0;
                sess->settings[CPAP_PressureMax] = summary.maximum_pressure / 10.0;
            }
            sess->settings[Yuwell_Humidity] = summary.humidity;
            sess->settings[Yuwell_Ramp] = summary.ramp;

            EventList *LK = sess->AddEventList(CPAP_LeakTotal, EVL_Event, 1);
            EventList *PR = sess->AddEventList(CPAP_Pressure, EVL_Event, 0.1F);
            EventList *OA = sess->AddEventList(CPAP_Obstructive, EVL_Event);
            EventList *CA = sess->AddEventList(CPAP_ClearAirway, EVL_Event);
            EventList *H =  sess->AddEventList(CPAP_Hypopnea, EVL_Event);
            EventList *P =  sess->AddEventList(OXI_Pulse, EVL_Event);
            EventList *O =  sess->AddEventList(OXI_SPO2, EVL_Event);

            qint64 ti;
            ti = qint64(ts) * 1000L;

            for (int i = 0; i < summary.session_minutes; i++) {
                QCoreApplication::processEvents();

                in >> leakage;
                in >> pressure;
                in >> spo2;
                in >> oai;
                in >> hi;
                in >> pulse;
                in >> cai;

                if (i == 0 && leakage != 0xF9) {
                    break; // Out of bounds offset, not a valid record
                }
                PR->AddEvent(ti + (i * 60000), pressure); // Samples are every 60 seconds
                if (pulse < 249) {
                    P->AddEvent(ti + (i * 60000), pulse);
                }
                O->AddEvent(ti + (i * 60000), spo2);

                if (oai > 0) {
                    OA->AddEvent(ti + (i * 60000), 0);
                }
                if (hi > 0) {
                    H->AddEvent(ti + (i * 60000), 0);
                }
                if (cai > 0) {
                    CA->AddEvent(ti + (i * 60000), 0);
                }
                if (leakage > 0 && leakage < 249) { // First record always pulls high, ignore
                    LK->AddEvent(ti + (i * 60000), leakage);
                }
            }
            sess->SetChanged(true);
            Sessions[ts] = sess;
            sess->UpdateSummaries();
            sess->Store(mach->getDataPath());
            mach->AddSession(sess);
        }
    }

    emit m_loader->updateMessage(QObject::tr("Finishing up..."));
    QCoreApplication::processEvents();

    m_loader->finish();

    return record_count;
}

YuwellLoader::YuwellLoader() {
    m_type = MT_CPAP;

    const QString YUWELL_ICON = ":/icons/yuwell.png";

    QString s = newInfo().series;
    m_pixmap_paths[s] = YUWELL_ICON;
    m_pixmaps[s] = QPixmap(YUWELL_ICON);
}

YuwellLoader::~YuwellLoader() {}

bool yuwell_initialized = false;
void YuwellLoader::Register() {
    if (yuwell_initialized)
        return;

    RegisterLoader(new YuwellLoader());

    yuwell_initialized = true;
}

YuwellFormat* YuwellLoader::YuwellFactory(const QString & givenPath) {
    // Add formats here as we find them
    QList<YuwellFormat*> candidates;
    candidates << new YuwellFormatA(*this, givenPath);
    candidates << new YuwellFormatB(*this, givenPath);
    //candidates << new YuwellFormatC(givenPath);

    YuwellFormat* match = nullptr;
    for(YuwellFormat* format : candidates) {
        if (match == nullptr && format->Detect()) {
            match = format;
        } else {
            delete format;
        }
    }

    return match;
}

bool YuwellLoader::Detect(const QString & givenPath) {
    /*
     * For each format, detect
     */
    QDir dir(givenPath);

    if (!dir.exists()) {
        return false;
    }

    YuwellFormat* formatter = YuwellFactory(givenPath);
    if (!formatter) {
        return false;
    }

    delete formatter;
    return true;
}

bool YuwellLoader::backupData (Machine * mach, const QString & path) {
    QDir ipath(path);
    QDir bpath(mach->getBackupPath());

    // Compare QDirs rather than QStrings because separators may be different, especially on Windows.
    if (ipath == bpath) {
        // Don't create backups if importing from backup folder
        rebuild_from_backups = true;
        create_backups = false;
    } else {
        rebuild_from_backups = false;
        create_backups = p_profile->session->backupCardData();
    }

    if (rebuild_from_backups || !create_backups)
        return true;

    // Copy input data to backup location
    copyPath(ipath.absolutePath(), bpath.absolutePath(), true);

    return true;
}

int YuwellLoader::Open(const QString & dirPath) {
    // Do the import
    YuwellFormat* formatter = YuwellFactory(dirPath);
    if (formatter) {
        formatter->Open();
        return 1;
    }
    return 0;

}

void YuwellLoader::finish() {
    finishAddingSessions();
}
