/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 *
 * MainWindow Implementation
 *
 * Copyright (c) 2011-2014 Mark Watkins <jedimark@users.sourceforge.net>
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License. See the file COPYING in the main directory of the Linux
 * distribution for more details. */

#include <QGLFormat>
#include <QFileDialog>
#include <QMessageBox>
#include <QResource>
#include <QProgressBar>
#include <QWebHistory>
#include <QWebFrame>
#include <QNetworkRequest>
#include <QDesktopServices>
#include <QNetworkReply>
#include <QTimer>
#include <QSettings>
#include <QPixmap>
#include <QDesktopWidget>
#include <QListView>
#include <QPrinter>
#include <QPrintDialog>
#include <QPainter>
#include <QProcess>
#include <QFontMetrics>
#include <QTextDocument>
#include <QTranslator>
#include <QPushButton>
#include <QCalendarWidget>

#include "common_gui.h"
#include "version.h"


#include <cmath>

// Custom loaders that don't autoscan..
#include <SleepLib/loader_plugins/zeo_loader.h>
#include <SleepLib/loader_plugins/somnopose_loader.h>

#ifndef REMSTAR_M_SUPPORT
#include <SleepLib/loader_plugins/mseries_loader.h>
#endif

#include "logger.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "newprofile.h"
#include "exportcsv.h"
#include "SleepLib/schema.h"
#include "Graphs/glcommon.h"
#include "UpdaterWindow.h"
#include "SleepLib/calcs.h"
#include "version.h"

#include "reports.h"
#include "statistics.h"

QProgressBar *qprogress;
QLabel *qstatus;
QLabel *qstatus2;
QStatusBar *qstatusbar;

extern Profile *profile;

QString getOpenGLVersionString()
{
    static QString glversion;

    if (glversion.isEmpty()) {
        QGLWidget w;
        w.makeCurrent();

        glversion = QString(QLatin1String(reinterpret_cast<const char*>(glGetString(GL_VERSION))));
        qDebug() << "OpenGL Version:" << glversion;
    }
    return glversion;
}

float getOpenGLVersion()
{
    QString glversion = getOpenGLVersionString();
    glversion = glversion.section(" ",0,0);
    bool ok;
    float v = glversion.toFloat(&ok);

    if (!ok) {
        QString tmp = glversion.section(".",0,1);
        v = tmp.toFloat(&ok);
        if (!ok) {
            // just look at major, we are only interested in whether we have OpenGL 2.0 anyway
            tmp = glversion.section(".",0,0);
            v = tmp.toFloat(&ok);
        }
    }
    return v;
}

QString getGraphicsEngine()
{
    QString gfxEngine = QString();
#ifdef BROKEN_OPENGL_BUILD
    gfxEngine = CSTR_GFX_BrokenGL;
#else
    QString glversion = getOpenGLVersionString();
    if (glversion.contains(CSTR_GFX_ANGLE)) {
        gfxEngine = CSTR_GFX_ANGLE;
    } else {
        gfxEngine = CSTR_GFX_OpenGL;
    }
#endif
    return gfxEngine;
}

void MainWindow::logMessage(QString msg)
{
    ui->logText->appendPlainText(msg);
}

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    Q_ASSERT(p_profile != nullptr);

    ui->setupUi(this);

    if (logger) {
        connect(logger, SIGNAL(outputLog(QString)), this, SLOT(logMessage(QString)));
    }

    QString version = VersionString;

#ifdef TEST_BUILD
    version += QString(STR_TestBuild);
#else
    ui->warningLabel->hide();
#endif

    version += " "+getGraphicsEngine();

    if (QString(GIT_BRANCH) != "master") { version += " [" + QString(GIT_BRANCH)+" branch]"; }


    this->setWindowTitle(STR_TR_SleepyHead + QString(" v%1 (" + tr("Profile") + ": %2)").arg(version).arg(PREF[STR_GEN_Profile].toString()));

    qDebug() << STR_TR_SleepyHead << VersionString << "built with Qt" << QT_VERSION_STR << "on" << __DATE__ << __TIME__;

#ifdef BROKEN_OPENGL_BUILD
    qDebug() << "This build has been created especially for computers with older graphics hardware.\n";
#endif

    //ui->tabWidget->setCurrentIndex(1);

#ifdef Q_OS_MAC

    ui->action_About->setMenuRole(QAction::ApplicationSpecificRole);
    ui->action_Preferences->setMenuRole(QAction::ApplicationSpecificRole);
    ui->action_Exit->setMenuRole(QAction::ApplicationSpecificRole);
#if(QT_VERSION<QT_VERSION_CHECK(5,0,0))
    // Disable Screenshot on Mac Platform,as it doesn't work in Qt4, and the system provides this functionality anyway.
    ui->action_Screenshot->setEnabled(false);
#endif
#endif



    QList<Machine *> machines = p_profile->GetMachines(MT_CPAP);
    for (QList<Machine *>::iterator it = machines.begin(); it != machines.end(); ++it) {
        QString mclass=(*it)->GetClass();
        if (mclass == STR_MACH_ResMed) {
            qDebug() << "ResMed machine found.. locking Session splitting capabilities";

            // Have to sacrifice these features to get access to summary data.
            p_profile->session->setCombineCloseSessions(0);
            p_profile->session->setDaySplitTime(QTime(12,0,0));
            p_profile->session->setIgnoreShortSessions(false);
            break;
        }
    }


    overview = nullptr;
    daily = nullptr;
    prefdialog = nullptr;

    m_inRecalculation = false;
    m_restartRequired = false;
    // Initialize Status Bar objects
    qstatusbar = ui->statusbar;
    qprogress = new QProgressBar(this);
    qprogress->setMaximum(100);
    qstatus2 = new QLabel(tr("Welcome"), this);
    qstatus2->setFrameStyle(QFrame::Raised);
    qstatus2->setFrameShadow(QFrame::Sunken);
    qstatus2->setFrameShape(QFrame::Box);
    //qstatus2->setMinimumWidth(100);
    qstatus2->setMaximumWidth(100);
    qstatus2->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
    qstatus = new QLabel("", this);
    qprogress->hide();
    ui->statusbar->setMinimumWidth(200);
    ui->statusbar->addPermanentWidget(qstatus, 0);
    ui->statusbar->addPermanentWidget(qprogress, 1);
    ui->statusbar->addPermanentWidget(qstatus2, 0);

    ui->actionDebug->setChecked(p_profile->general->showDebug());

    QTextCharFormat format = ui->statStartDate->calendarWidget()->weekdayTextFormat(Qt::Saturday);
    format.setForeground(QBrush(Qt::black, Qt::SolidPattern));
    Qt::DayOfWeek dow=firstDayOfWeekFromLocale();
    ui->statStartDate->calendarWidget()->setWeekdayTextFormat(Qt::Saturday, format);
    ui->statStartDate->calendarWidget()->setWeekdayTextFormat(Qt::Sunday, format);
    ui->statStartDate->calendarWidget()->setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader);
    ui->statStartDate->calendarWidget()->setFirstDayOfWeek(dow);
    ui->statEndDate->calendarWidget()->setWeekdayTextFormat(Qt::Saturday, format);
    ui->statEndDate->calendarWidget()->setWeekdayTextFormat(Qt::Sunday, format);
    ui->statEndDate->calendarWidget()->setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader);
    ui->statEndDate->calendarWidget()->setFirstDayOfWeek(dow);

    ui->statEndDate->setVisible(false);
    ui->statStartDate->setVisible(false);

    ui->reportModeRange->setVisible(false);
    switch(p_profile->general->statReportMode()) {
        case 0:
            ui->reportModeStandard->setChecked(true);
            break;
        case 1:
            ui->reportModeMonthly->setChecked(true);
            break;
        case 2:
            ui->reportModeRange->setChecked(true);
            ui->statEndDate->setVisible(true);
            ui->statStartDate->setVisible(true);
            break;
        default:
            p_profile->general->setStatReportMode(0);
    }
    if (!p_profile->general->showDebug()) {
        ui->logText->hide();
    }

#ifdef Q_OS_MAC
    p_profile->appearance->setAntiAliasing(false);
#endif
    ui->action_Link_Graph_Groups->setChecked(p_profile->general->linkGroups());

    first_load = true;

    // Using the dirty registry here. :(
    QSettings settings(getDeveloperName(), getAppName());

    // Load previous Window geometry (this is currently broken on Mac as of Qt5.2.1)
    restoreGeometry(settings.value("MainWindow/geometry").toByteArray());

    daily = new Daily(ui->tabWidget, nullptr);
    ui->tabWidget->insertTab(1, daily, STR_TR_Daily);


    // Start with the Summary Tab
    ui->tabWidget->setCurrentWidget(ui->statisticsTab); // setting this to daily shows the cube during loading..

    // Nifty Notification popups in System Tray (uses Growl on Mac)
    if (QSystemTrayIcon::isSystemTrayAvailable() && QSystemTrayIcon::supportsMessages()) {
        systray = new QSystemTrayIcon(QIcon(":/icons/bob-v3.0.png"), this);
        systray->show();
        systraymenu = new QMenu(this);
        systray->setContextMenu(systraymenu);
        QAction *a = systraymenu->addAction(STR_TR_SleepyHead + " v" + VersionString);
        a->setEnabled(false);
        systraymenu->addSeparator();
        systraymenu->addAction(tr("&About"), this, SLOT(on_action_About_triggered()));
        systraymenu->addAction(tr("Check for &Updates"), this,
                               SLOT(on_actionCheck_for_Updates_triggered()));
        systraymenu->addSeparator();
        systraymenu->addAction(tr("E&xit"), this, SLOT(close()));
    } else { // if not available, the messages will popup in the taskbar
        systray = nullptr;
        systraymenu = nullptr;
    }

    ui->toolBox->setCurrentIndex(0);
    daily->graphView()->redraw();

    if (p_profile->cpap->AHIWindow() < 30.0) {
        p_profile->cpap->setAHIWindow(60.0);
    }

    ui->recordsBox->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
    ui->statisticsView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
    ui->webView->page()->setLinkDelegationPolicy(QWebPage::DelegateExternalLinks);
    ui->bookmarkView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);

    QString loadingtxt =
        "<HTML><body style='text-align: center; vertical-align: center'><table width='100%' height='100%'><tr><td align=center><h1>"
        + tr("Loading...") + "</h1></td></tr></table></body></HTML>";
    ui->statisticsView->setHtml(loadingtxt);
    on_tabWidget_currentChanged(0);

#ifndef REMSTAR_M_SUPPORT
    ui->actionImport_RemStar_MSeries_Data->setVisible(false);
#endif

    on_homeButton_clicked();

    qsrand(QDateTime::currentDateTime().toTime_t());

    // Translators, these are only temporary messages, don't bother unless you really want to..

    warnmsg.push_back(tr("<b>Warning:</b> This pre-release build is meant for beta testers only. Please do <b>NOT</b> share outside the SleepyHead Testing Forum."));
    warnmsg.push_back(tr("Please report bugs for this build to the SleepyHead Testing Forum, but first, check the release thread to ensure you are running the latest version."));
    warnmsg.push_back(tr("When reporting bugs, please make sure to supply the SleepyHead version number, operating system details and CPAP machine model."));
    warnmsg.push_back(tr("<b>Warning:</b> This reports this software generates are not fit for compliance or medical diagnostic purposes."));
    warnmsg.push_back(tr(""));
    warnmsg.push_back(tr("These messages are only a temporary feature. Some people thought they were an error."));

    wtimer.setParent(this);
    warnidx = 0;
    wtimer.singleShot(0, this, SLOT(on_changeWarningMessage()));
}

void MainWindow::on_changeWarningMessage()
{
    int i=warnidx++ % warnmsg.size();
    QString warning = "<html><head/><body><p>"+warnmsg[i]+"</p></body></html>";
    ui->warningLabel->setText(warning);
    wtimer.singleShot(10000, this, SLOT(on_changeWarningMessage()));
}

void MainWindow::closeEvent(QCloseEvent * event)
{
    if (daily) {
        daily->close();
        daily->deleteLater();
    }

    if (overview) {
        overview->close();
        overview->deleteLater();
    }

    // Shutdown and Save the current User profile
    Profiles::Done();

    // Save current window position
    QSettings settings(getDeveloperName(), getAppName());
    settings.setValue("MainWindow/geometry", saveGeometry());

    QMainWindow::closeEvent(event);
}

extern MainWindow *mainwin;
MainWindow::~MainWindow()
{
//    if (systraymenu) { delete systraymenu; }

//    if (systray) { delete systray; }

    // Trash anything allocated by the Graph objects
    DestroyGraphGlobals();

    disconnect(logger, SIGNAL(outputLog(QString)), this, SLOT(logMessage(QString)));
    shutdownLogger();

    mainwin = nullptr;
    delete ui;
}


void MainWindow::Notify(QString s, QString title, int ms)
{
    if (title.isEmpty()) {
        title = "SleepyHead v" + VersionString;
    }
    if (systray) {
        // GNOME3's systray hides the last line of the displayed Qt message.
        // As a workaround, add an extra line to bump the message back
        // into the visible area.
        QString msg = s;

#ifdef Q_OS_UNIX
        char *desktop = getenv("DESKTOP_SESSION");

        if (desktop && !strncmp(desktop, "gnome", 5)) {
            msg += "\n";
        }
#endif

        systray->showMessage(title, msg, QSystemTrayIcon::Information, ms);
    } else {
        ui->statusbar->showMessage(s, ms);
    }
}

void MainWindow::PopulatePurgeMenu()
{
    QList<QAction *> actions = ui->menu_Purge_CPAP_Data->actions();

    ui->menu_Purge_CPAP_Data->disconnect(ui->menu_Purge_CPAP_Data, SIGNAL(triggered(QAction*)), this, SLOT(on_actionPurgeMachine(QAction *)));

    ui->menu_Purge_CPAP_Data->clear();

    QList<Machine *> machines = p_profile->GetMachines(MT_CPAP);
    for (int i=0; i < machines.size(); ++i) {
        Machine *mach = machines.at(i);
        QString name =  mach->properties[STR_PROP_Brand]+" "+
                        mach->properties[STR_PROP_Model]+" "+
                        mach->properties[STR_PROP_Serial];

        QAction * action = new QAction(name.replace("&","&&"), ui->menu_Purge_CPAP_Data);
        action->setData(mach->GetClass()+":"+mach->properties[STR_PROP_Serial]);
        ui->menu_Purge_CPAP_Data->addAction(action);
    }
    ui->menu_Purge_CPAP_Data->connect(ui->menu_Purge_CPAP_Data, SIGNAL(triggered(QAction*)), this, SLOT(on_actionPurgeMachine(QAction*)));
}

void MainWindow::Startup()
{
    qstatus->setText(tr("Loading Data"));
    qprogress->show();
    //qstatusbar->showMessage(tr("Loading Data"),0);

    // profile is a global variable set in main after login
    p_profile->LoadMachineData();

    PopulatePurgeMenu();

    SnapshotGraph = new gGraphView(this, daily->graphView());

    // Snapshot graphs mess up with pixmap cache
    SnapshotGraph->setUsePixmapCache(false);

   // SnapshotGraph->setFormat(daily->graphView()->format());
    //SnapshotGraph->setMaximumSize(1024,512);
    //SnapshotGraph->setMinimumSize(1024,512);
    SnapshotGraph->hide();

    overview = new Overview(ui->tabWidget, daily->graphView());
    ui->tabWidget->insertTab(2, overview, STR_TR_Overview);

    GenerateStatistics();
    ui->tabWidget->setCurrentWidget(ui->statisticsTab);

    ui->statStartDate->setDate(p_profile->FirstDay());
    ui->statEndDate->setDate(p_profile->LastDay());

    if (daily) { daily->ReloadGraphs(); }

    if (overview) { overview->ReloadGraphs(); }

    qprogress->hide();
    qstatus->setText("");

    if (p_profile->p_preferences[STR_PREF_ReimportBackup].toBool()) {
        importCPAPBackups();
        p_profile->p_preferences[STR_PREF_ReimportBackup]=false;
    }
}

int MainWindow::importCPAP(const QString &path, const QString &message)
{
    QDialog popup(this, Qt::SplashScreen);
    QLabel waitmsg(message);
    QVBoxLayout waitlayout(&popup);
    waitlayout.addWidget(&waitmsg,1,Qt::AlignCenter);
    waitlayout.addWidget(qprogress,1);
    qprogress->setVisible(true);
    popup.show();
    int c=p_profile->Import(path);
    popup.hide();
    ui->statusbar->insertWidget(2,qprogress,1);
    qprogress->setVisible(false);

    return c;
}

void MainWindow::finishCPAPImport()
{
    p_profile->Save();
    GenerateStatistics();

    if (overview) { overview->ReloadGraphs(); }
    if (daily) { daily->ReloadGraphs(); }
}

void MainWindow::importCPAPBackups()
{

    // Get BackupPaths for all CPAP machines
    QList<Machine *> machlist = p_profile->GetMachines(MT_CPAP);
    QStringList paths;
    Q_FOREACH(Machine *m, machlist) {
        if (m->properties.contains(STR_PROP_BackupPath)) {
            paths.push_back(p_profile->Get(m->properties[STR_PROP_BackupPath]));
        }
    }

    if (paths.size() > 0) {
        if (QMessageBox::question(
                    this,
                    STR_MessageBox_Question,
                    tr("CPAP data was recently purged and needs to be re-imported.")+"\n\n"+
                    tr("Would you like this done automatically from the Backup Folder?")+"\n\n"+
                    QDir::toNativeSeparators(paths.join("\n")),
                    QMessageBox::Yes | QMessageBox::No,
                    QMessageBox::Yes) == QMessageBox::Yes)
        {
            int c=0;
            Q_FOREACH(QString path, paths) {
                c+=importCPAP(path,tr("Please wait, importing from backup folder(s)..."));
            }
            if (c>0) {
                QString str=tr("Data successfully imported from the following locations:")+"\n\n"+
                        QDir::toNativeSeparators(paths.join("\n"));
                mainwin->Notify(str);
                finishCPAPImport();
            } else {
                mainwin->Notify(tr("Couldn't find any new Machine Data at the locations given."),tr("Import Problem"));
            }
        }
    }
}

#ifdef Q_OS_UNIX
# include <stdio.h>
# include <unistd.h>

# if defined(Q_OS_MAC) || defined(Q_OS_BSD4)
#  include <sys/mount.h>
# else
#  include <sys/statfs.h>
#  include <mntent.h>
# endif // Q_OS_MAC/BSD

#endif // Q_OS_UNIX

//! \brief Returns a list of drive mountpoints
QStringList getDriveList()
{
    QStringList drivelist;

#if defined(Q_OS_MAC) || defined(Q_OS_BSD4)
    struct statfs *mounts;
    int num_mounts = getmntinfo(&mounts, MNT_WAIT);
    if (num_mounts >= 0) {
        for (int i = 0; i < num_mounts; i++) {
            QString name = mounts[i].f_mntonname;

            // Only interested in drives mounted under /Volumes
            if (name.toLower().startsWith("/volumes/")) {
                drivelist.push_back(name);
//                qDebug() << QString("Disk type '%1' mounted at: %2").arg(mounts[i].f_fstypename).arg(mounts[i].f_mntonname);
            }
        }
    }

#elif defined(Q_OS_UNIX)
    // Unix / Linux (except BSD)
    FILE *mtab = setmntent("/etc/mtab", "r");
    struct mntent *m;
    struct mntent mnt;
    char strings[4096];

    // NOTE: getmntent_r is a GNU extension, requiring glibc.
    while ((m = getmntent_r(mtab, &mnt, strings, sizeof(strings)))) {

        struct statfs fs;
        if ((mnt.mnt_dir != NULL) && (statfs(mnt.mnt_dir, &fs) == 0)) {
            QString name = mnt.mnt_dir;
            quint64 size = fs.f_blocks * fs.f_bsize;

            if (size > 0) { // this should theoretically ignore /dev, /proc, /sys etc..
                drivelist.push_back(name);
            }
//            quint64 free = fs.f_bfree * fs.f_bsize;
//            quint64 avail = fs.f_bavail * fs.f_bsize;
        }
    }
    endmntent(mtab);

#elif defined(Q_OS_WIN)
    QFileInfoList list = QDir::drives();

    for (int i = 0; i < list.size(); ++i) {
        QFileInfo fileInfo = list.at(i);
        QString name = fileInfo.filePath();
        if (name.at(0).toUpper() != QChar('C')) { // Ignore the C drive
            drivelist.push_back(name);
        }
    }

#endif

    return drivelist;
}

QStringList MainWindow::detectCPAPCards()
{
    const int timeout = 20000;

    QStringList datapaths;

    QList<MachineLoader *>loaders = GetLoaders(MT_CPAP);
    QTime time;
    time.start();

    // Create dialog
    QDialog popup(this, Qt::SplashScreen);
    QLabel waitmsg(tr("Please wait, scanning for CPAP data cards..."));
    QProgressBar progress;
    QVBoxLayout waitlayout;
    popup.setLayout(&waitlayout);

    QHBoxLayout layout2;
    QIcon icon("://icons/sdcard.png");
    QPushButton skipbtn(icon, tr("Choose a folder"));
    skipbtn.setMinimumHeight(40);
    waitlayout.addWidget(&waitmsg,1,Qt::AlignCenter);
    waitlayout.addWidget(&progress,1);
    waitlayout.addLayout(&layout2,1);
    layout2.addWidget(&skipbtn);
    popup.connect(&skipbtn, SIGNAL(clicked()), &popup, SLOT(hide()));
    progress.setValue(0);
    progress.setMaximum(timeout);
    progress.setVisible(true);
    popup.show();
    QApplication::processEvents();

    do {
        // Rescan in case card inserted
        QStringList AutoScannerPaths = getDriveList();

        Q_FOREACH(const QString &path, AutoScannerPaths) {
            // Scan through available machine loaders and test if this folder contains valid folder structure
            Q_FOREACH(MachineLoader * loader, loaders) {
                if (loader->Detect(path)) {
                    datapaths.push_back(path);

                    qDebug() << "Found" << loader->ClassName() << "datacard at" << path;
                }
            }
        }
        int el=time.elapsed();
        progress.setValue(el);
        QApplication::processEvents();
        if (el > timeout) break;
        if (!popup.isVisible()) break;
        // needs a slight delay here
        QThread::msleep(200);
    } while (datapaths.size() == 0);
    popup.hide();
    popup.disconnect(&skipbtn, SIGNAL(clicked()), &popup, SLOT(hide()));

    return datapaths;
}

void MainWindow::on_action_Import_Data_triggered()
{
    if (m_inRecalculation) {
        Notify(tr("Access to Import has been blocked while recalculations are in progress."),STR_MessageBox_Busy);
        return;
    }

    QStringList datapaths = detectCPAPCards();

    QList<MachineLoader *>loaders = GetLoaders(MT_CPAP);

    QTime time;
    time.start();
    QDialog popup(this, Qt::FramelessWindowHint);
    popup.setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
    QLabel waitmsg(tr("Please wait, scanning for CPAP data cards..."));
    QVBoxLayout waitlayout(&popup);
    waitlayout.addWidget(&waitmsg,1,Qt::AlignCenter);
    waitlayout.addWidget(qprogress,1);

    bool asknew = false;
    qprogress->setVisible(false);

    if (datapaths.size() > 0) {
        int res = QMessageBox::question(this,
                                        tr("CPAP Data Located"),
                                        tr("CPAP Datacard structures were detected at the following locations:")+
                                        QString("\n\n%1\n\n").arg(QDir::toNativeSeparators(datapaths.join("\n")))+
                                        tr("Would you like to import from the path(s) shown above?"),
                                        STR_MessageBox_Yes,
                                        tr("Select another folder"),
                                        STR_MessageBox_Cancel,
                                        0, 2);
        if (res == 1) {
            waitmsg.setText(tr("Please wait, launching file dialog..."));
            datapaths.clear();
            asknew = true;
        }

        if (res == 2) {
            // Give the communal progress bar back
            ui->statusbar->insertWidget(2,qprogress,1);
            return;
        }

    } else {
        waitmsg.setText(tr("No CPAP data card detected, launching file dialog..."));
        asknew = true;
    }

    if (asknew) {
        popup.show();
        mainwin->Notify(tr("Please remember to point the importer at the root folder or drive letter of your data-card, and not a subfolder."),
                        tr("Import Reminder"),8000);

        QFileDialog w(this);

        QString folder;
        if (p_profile->contains(STR_PREF_LastCPAPPath)) {
            folder = (*p_profile)[STR_PREF_LastCPAPPath].toString();
        } else {
#if QT_VERSION  < QT_VERSION_CHECK(5,0,0)
            folder = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
#else
            folder = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
#endif
        }

        w.setDirectory(folder);
        w.setFileMode(QFileDialog::Directory);
        w.setOption(QFileDialog::ShowDirsOnly, true);

        // This doesn't work on WinXP

#if defined(Q_OS_MAC)
#if (QT_VERSION < QT_VERSION_CHECK(4,8,0))
        // Fix for tetragon, 10.6 barfs up Qt's custom dialog
        w.setOption(QFileDialog::DontUseNativeDialog, true);
#else
        w.setOption(QFileDialog::DontUseNativeDialog,false);
#endif // version check

#elif defined(Q_OS_UNIX)
        w.setOption(QFileDialog::DontUseNativeDialog,false);
#elif defined(Q_OS_WIN)
        // check the Os version.. winxp chokes
        w.setOption(QFileDialog::DontUseNativeDialog, true);
#endif
//#else
//        w.setOption(QFileDialog::DontUseNativeDialog, false);

//        QListView *l = w.findChild<QListView *>("listView");
//        if (l) {
//            l->setSelectionMode(QAbstractItemView::MultiSelection);
//        }

//        QTreeView *t = w.findChild<QTreeView *>();
//        if (t) {
//            t->setSelectionMode(QAbstractItemView::MultiSelection);
//        }

//#endif

        if (w.exec() != QDialog::Accepted) {
            popup.hide();
            ui->statusbar->insertWidget(2,qprogress,1);

            return;
        }

        popup.hide();

        for (int i = 0; i < w.selectedFiles().size(); i++) {
            datapaths.append(w.selectedFiles().at(i));
        }
    }

    int successful = false;

    QStringList goodlocations;

    waitmsg.setText(tr("Please wait, SleepyHead is importing data..."));
    qprogress->setVisible(true);

    popup.show();
    for (int i = 0; i < datapaths.size(); i++) {
        QString dir = datapaths[i];

        if (!dir.isEmpty()) {
            qprogress->setValue(0);
            qprogress->show();
            qstatus->setText(tr("Importing Data"));
            int c = p_profile->Import(dir);
            qDebug() << "Finished Importing data" << c;

            if (c) {
                goodlocations.push_back(dir);

                QDir d(dir.section("/",0,-2));
                (*p_profile)[STR_PREF_LastCPAPPath] = d.absolutePath();

                successful = true;
            }

            qstatus->setText("");
            qprogress->hide();
        }
    }
    popup.hide();

    ui->statusbar->insertWidget(2,qprogress,1);

    if (successful) {
        finishCPAPImport();

        QString str=tr("Data successfully imported from the following locations\n\n");
        for (int i=0; i<goodlocations.size(); i++) {
            str += goodlocations.at(i) + "\n";
        }
        mainwin->Notify(str);
    } else {
        mainwin->Notify(tr("Import Problem\n\nCouldn't find any new Machine Data at the locations given"));
    }
    PopulatePurgeMenu();
}

QMenu *MainWindow::CreateMenu(QString title)
{
    QMenu *menu = new QMenu(title, ui->menubar);
    ui->menubar->insertMenu(ui->menu_Help->menuAction(), menu);
    return menu;
}

void MainWindow::on_action_Fullscreen_triggered()
{
    if (ui->action_Fullscreen->isChecked()) {
        this->showFullScreen();
    } else {
        this->showNormal();
    }
}

void MainWindow::setRecBoxHTML(QString html)
{
    ui->recordsBox->setHtml(html);
}

class MyStatsPage: public QWebPage
{
  public:
    MyStatsPage(QObject *parent);
    virtual ~MyStatsPage();
  protected:
    //virtual void javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID);
    virtual void javaScriptAlert(QWebFrame *frame, const QString &msg);
};
MyStatsPage::MyStatsPage(QObject *parent)
    : QWebPage(parent)
{
}
MyStatsPage::~MyStatsPage()
{
}

void MyStatsPage::javaScriptAlert(QWebFrame *frame, const QString &msg)
{
    Q_UNUSED(frame);
    mainwin->sendStatsUrl(msg);
}

QString MainWindow::getWelcomeHTML()
{
    // This is messy, but allows it to be translated easier
    return "<html>\n<head>"
           " <style type='text/css'>"
           "  <!--h1,p,a,td,body { font-family: 'FreeSans', 'Sans Serif' } --/>"
           "  p,a,td,body { font-size: 14px }"
           "  a:link,a:visited { color: \"#000020\"; text-decoration: none; font-weight: bold;}"
           "  a:hover { background-color: inherit; color: red; text-decoration:none; font-weight: bold; }"
           " </style>\n"
           "</head>"
           "<body leftmargin=0 topmargin=0 rightmargin=0>"
           "<table width=\"100%\" cellspacing=0 cellpadding=4 border=0 >"
           "<tr><td bgcolor=\"#d0d0d0\" colspan=2 cellpadding=0 valign=center align=center><font color=\"black\" size=+1><b>"
           + tr("Welcome to SleepyHead") + "</b></font></td></tr>"
           "<tr>"
           "<td valign=\"top\" leftmargin=0 cellpadding=6>"
           "<h3>" + tr("About SleepyHead") + "</h3>"
           "<p>" + tr("This software has been created to assist you in reviewing the data produced by CPAP Machines, used in the treatment of various Sleep Disorders.")
           + "</p>"
           "<p>" + tr("SleepyHead has been designed by a software developer with personal experience with a sleep disorder, and shaped by the feedback of many other willing testers dealing with similar conditions.")
           + "</p>"
           "<p><i><b>" + tr("This is a beta release, some features may not yet behave as expected.") +
           "</b></i><br/>" + tr("Please report any bugs you find to SleepyHead's SourceForge page.") + "</p>"

           "<h3>" + tr("Currenly supported machines:") + "</h3>"
           "<b>" + tr("CPAP") + "</b>"
           "<li>" + tr("Philips Respironics System One (CPAP Pro, Auto, BiPAP & ASV models)") + "</li>"
           "<li>" + tr("ResMed S9 models (CPAP, Auto, VPAP)") + "</li>"
           "<li>" + tr("DeVilbiss Intellipap (Auto)") + "</li>"
           "<li>" + tr("Fisher & Paykel ICON (CPAP, Auto)") + "</li>"
           "<b>" + tr("Oximetry") + "</b>"
           "<li>" + tr("Contec CMS50D+, CMS50E and CMS50F (not 50FW) Oximeters") + "</li>"
           "<li>" + tr("ResMed S9 Oximeter Attachment") + "</li>"
           "<p><h3>" + tr("Online Help Resources") + "</h3></p>"
           "<p><b>" + tr("Note:") + "</b>" +
           tr("I don't recommend using this built in web browser to do any major surfing in, it will work, but it's mainly meant as a help browser.")
           +
           tr("(It doesn't support SSL encryption, so it's not a good idea to type your passwords or personal details anywhere.)")
           + "</p>" +

           tr("SleepyHead's Online <a href=\"http://sleepyhead.sourceforge.net/wiki/index.php?title=SleepyHead_Users_Guide\">Users Guide</a><br/>")
           +
           tr("<a href=\"http://sleepyhead.sourceforge.net/wiki/index.php?title=Frequently_Asked_Questions\">Frequently Asked Questions</a><br/>")
           +
           tr("<a href=\"http://sleepyhead.sourceforge.net/wiki/index.php?title=Glossary\">Glossary of Sleep Disorder Terms</a><br/>")
           +
           tr("<a href=\"http://sleepyhead.sourceforge.net/wiki/index.php?title=Main_Page\">SleepyHead Wiki</a><br/>")
           +
           tr("SleepyHead's <a href='http://www.sourceforge.net/projects/sleepyhead'>Project Website</a> on SourceForge<br/>")
           +
           "<p><h3>" + tr("Further Information") + "</h3></p>"
           "<p>" +
           tr("Here are the <a href='qrc:/docs/release_notes.html'>release notes</a> for this version.") +
           "<br/>" +
           tr("Plus a few <a href='qrc:/docs/usage.html'>usage notes</a>, and some important information for Mac users.")
           + "<br/>" +
           "<p>" + tr("About <a href='http://en.wikipedia.org/wiki/Sleep_apnea'>Sleep Apnea</a> on Wikipedia")
           + "</p>"

           "<p>" + tr("Friendly forums to talk and learn about Sleep Apnea:") + "<br/>" +
           tr("<a href='http://www.cpaptalk.com'>CPAPTalk Forum</a>,") +
           tr("<a href='http://www.apneaboard.com/forums/'>Apnea Board</a>") + "</p>"
           "</td>"
           "<td><image src='qrc:/icons/bob-v3.0.png' width=220 height=220><br/>"
           "</td>"
           "</tr>"
           "<tr>"
           "<td colspan=2>"
           "<hr/>"
           "<p><b>" + tr("Copyright:") + "</b> " + tr("&copy;2011-2014") +
           " <a href=\"http://jedimark64.blogspot.com\">Mark Watkins</a> (jedimark)</p>"
           "<p><b>" + tr("License:") + "</b> " +
           tr("This software is released freely under the <a href=\"qrc:/COPYING\">GNU Public License</a>.") +
           "</p>"
           "<hr/>"
           "<p><b>" + tr("DISCLAIMER:") + "</b></p>"
           "<b><p>" +
           tr("This is <font color='red'><u>NOT</u></font> medical software. This application is merely a data viewer, and no guarantee is made regarding accuracy or correctness of any calculations or data displayed.")
           + "</p>"
           "<p>" + tr("The author will NOT be held liable by anyone who harms themselves or others by use or misuse of this software.")
           + "</p>"
           "<p>" + tr("Your doctor should always be your first and best source of guidance regarding the important matter of managing your health.")
           + "</p>"
           "<p>" + tr("*** <u>Use at your own risk</u> ***") + "</p></b>"
           "<hr/>"
           "</td></tr>"
           "</table>"
           "</body>"
           "</html>"

           ;
}

void MainWindow::on_homeButton_clicked()
{

    ui->webView->setHtml(getWelcomeHTML());

    //QString infourl="qrc:/docs/index.html"; // use this as a fallback
    //ui->webView->setUrl(QUrl(infourl));
}

void MainWindow::updateFavourites()
{
    QDate date = p_profile->LastDay(MT_JOURNAL);

    if (!date.isValid()) {
        return;
    }

    QString html = "<html><head><style type='text/css'>"
                   "p,a,td,body { font-family: '" + QApplication::font().family() + "'; }"
                   "p,a,td,body { font-size: " + QString::number(QApplication::font().pointSize() + 2) + "px; }"
                   "a:link,a:visited { color: inherit; text-decoration: none; }" //font-weight: normal;
                   "a:hover { background-color: inherit; color: white; text-decoration:none; font-weight: bold; }"
                   "</style></head><body>"
                   "<table width=100% cellpadding=2 cellspacing=0>";

    do {
        Day *journal = p_profile->GetDay(date, MT_JOURNAL);

        if (journal) {
            if (journal->size() > 0) {
                Session *sess = (*journal)[0];
                QString tmp;
                bool filtered = !bookmarkFilter.isEmpty();
                bool found = !filtered;

                if (sess->settings.contains(Bookmark_Start)) {
                    //QVariantList start=sess->settings[Bookmark_Start].toList();
                    //QVariantList end=sess->settings[Bookmark_End].toList();
                    QStringList notes = sess->settings[Bookmark_Notes].toStringList();

                    if (notes.size() > 0) {
                        tmp += QString("<tr><td><b><a href='daily=%1'>%2</a></b><br/>")
                               .arg(date.toString(Qt::ISODate))
                               .arg(date.toString());

                        tmp += "<list>";

                        for (int i = 0; i < notes.size(); i++) {
                            //QDate d=start[i].toDate();
                            QString note = notes[i];

                            if (filtered && note.contains(bookmarkFilter, Qt::CaseInsensitive)) {
                                found = true;
                            }

                            tmp += "<li>" + note + "</li>";
                        }

                        tmp += "</list></td>";
                    }
                }

                if (found) { html += tmp; }
            }
        }

        date = date.addDays(-1);
    } while (date >= p_profile->FirstDay(MT_JOURNAL));

    html += "</table></body></html>";
    ui->bookmarkView->setHtml(html);
}

void MainWindow::on_backButton_clicked()
{
    ui->webView->back();
}

void MainWindow::on_forwardButton_clicked()
{
    ui->webView->forward();
}

void MainWindow::on_webView_urlChanged(const QUrl &arg1)
{
    ui->urlBar->setEditText(arg1.toString());
}

void MainWindow::on_urlBar_activated(const QString &arg1)
{
    QUrl url(arg1);
    ui->webView->setUrl(url);
}

void MainWindow::on_dailyButton_clicked()
{
    ui->tabWidget->setCurrentWidget(daily);
    daily->RedrawGraphs();
    qstatus2->setText(STR_TR_Daily);
}
void MainWindow::JumpDaily()
{
    on_dailyButton_clicked();
}

void MainWindow::on_overviewButton_clicked()
{
    ui->tabWidget->setCurrentWidget(overview);
    qstatus2->setText(STR_TR_Overview);
}

void MainWindow::on_webView_loadFinished(bool arg1)
{
    Q_UNUSED(arg1);
    qprogress->hide();

    if (first_load) {
        QTimer::singleShot(0, this, SLOT(Startup()));
        first_load = false;
    } else {
        qstatus->setText("");
    }

    ui->backButton->setEnabled(ui->webView->history()->canGoBack());
    ui->forwardButton->setEnabled(ui->webView->history()->canGoForward());

    connect(ui->webView->page(), SIGNAL(linkHovered(QString, QString, QString)), this,
            SLOT(LinkHovered(QString, QString, QString)));
}

void MainWindow::on_webView_loadStarted()
{
    disconnect(ui->webView->page(), SIGNAL(linkHovered(QString, QString, QString)), this,
               SLOT(LinkHovered(QString, QString, QString)));

    if (!first_load) {
        qstatus->setText(tr("Loading"));
        qprogress->reset();
        qprogress->show();
    }
}

void MainWindow::on_webView_loadProgress(int progress)
{
    qprogress->setValue(progress);
}

void MainWindow::aboutBoxLinkClicked(const QUrl &url)
{
    QDesktopServices::openUrl(url);
}

void MainWindow::on_action_About_triggered()
{
    QString gitrev = QString(GIT_REVISION);

    if (!gitrev.isEmpty()) { gitrev = tr("Revision:")+" " + gitrev + " (" + QString(GIT_BRANCH) + " " + tr("branch") + ")"; }

    //    "<style type=\"text/css\">body { margin:0; padding:0; } html, body, #bg { height:100%; width:100% } #bg { position: absolute; left:0; right:0; bottom:0; top:0; overflow:hidden; z-index:1; } #bg img { width:100%; min-width:100%; min-height:100%; } #content { z-index:0; }</style><body><div id=\"bg\"> <img style=\"display:block;\" src=\"qrc:/icons/Bob Strikes Back.png\"></div><div id=\"content\">"
    QString msg = QString(
                "<html>"
                "<head><style type=\"text/css\">a:link, a:visited { color: #000044; text-decoration: underline; font-weight: normal;}"
                "a:hover { background-color: inherit; color: #4444ff; text-decoration:none; font-weight: normal; }"
                "</style></head>"

                "<body>"
                "<span style=\"color:#000000; font-weight:600; vertical-align:middle;\">"
                "<table width=100%><tr><td>"
                "<p><h1>" + STR_TR_SleepyHead +
                QString(" v%1 (%2)</h1></p><font color=black><p>").arg(VersionString).arg(ReleaseStatus) +
                tr("Build Date: %1 %2").arg(__DATE__).arg(__TIME__) +
                QString("<br/>%1<br/>").arg(gitrev) +
                tr("Graphics Engine: %1").arg(getGraphicsEngine())+
                "<br/>" +
                tr("Data Folder Location: %1").arg(QDir::toNativeSeparators(GetAppRoot()) +
                "<hr/>"+tr("Copyright") + " &copy;2011-2014 Mark Watkins (jedimark) <br/> \n" +
                tr("This software is released under the GNU Public License v3.0<br/>") +
                "<hr>"

                // Project links
                "<p>" +tr("SleepyHead Project Page") +
                ": <a href=\"http://sourceforge.net/projects/sleepyhead\">http://sourceforge.net/projects/sleepyhead</a><br/>"
                +
                tr("SleepyHead Wiki") +
                ": <a href=\"http://sleepyhead.sourceforge.net/wiki\">http://sleepyhead.sourceforge.net/wiki</a><p/>" +

                // Social media links.. (Dear Translators, if one of these isn't available in your country, it's ok to leave it out.)
                tr("Don't forget to Like/+1 SleepyHead on <a href=\"http://www.facebook.com/SleepyHeadCPAP\">Facebook</a> or <a href=\"http://plus.google.com/u/0/b/101426655252362287937\">Google+")
                + "</p>" +

                // Image
                "</td><td align='center'><img src=\"qrc:/icons/Jedimark.png\" width=260px><br/> <br/><i>"
                +tr("SleepyHead, brought to you by Jedimark") + "</i></td></tr><tr colspan><td colspan=2>" +


                // Credits section
                "<hr/><p><b><font size='+1'>" +tr("Kudos & Credits") + "</font></b></p><b>" +
                tr("Bugfixes, Patches and Platform Help:") + "</b> " +
                tr("James Marshall, Rich Freeman, John Masters, Keary Griffin, Patricia Shanahan, Alec Clews, manders99, Sean Stangl and Roy Stone.")
                + "</p>"

                "<p><b>" + tr("Translators:") + "</b> " + tr("Arie Klerk (Dutch), Steffen Reitz (German), and others I've still to add here.") +
                "</p>"

                "<p><b>" + tr("3rd Party Libaries:") + "</b> " +
                tr("SleepyHead is built using the <a href=\"http://qt-project.org\">Qt Application Framework</a>.")
                + " " +
                tr("It uses the cross platform <a href=\"http://code.google.com/p/qextserialport\">QExtSerialPort</a> library for serial port access in the Oximetry module.")
                + " " +
                tr("In the updater code, SleepyHead uses <a href=\"http://sourceforge.net/projects/quazip\">QuaZip</a> by Sergey A. Tachenov, which is a C++ wrapper over Gilles Vollant's ZIP/UNZIP package.")
                + "<br/>"
                "<p>" + tr("Special thanks to Pugsy from <a href='http://cpaptalk.com'>CPAPTalk</a> for her help with documentation and tutorials, as well as everyone who helped out by testing and sharing their CPAP data.")
                + "</p>"

                // Donations
                "<hr><p><font color=\"blue\">" +
                tr("Thanks for using SleepyHead. If you find it within your means, please consider encouraging future development by making a donation via Paypal.")
                + "</font>"


                "<hr><p><b>Disclaimer</b><br/><i>" +
                tr("This software comes with absolutely no warranty, either express of implied.") + " " +
                tr("It comes with no guarantee of fitness for any particular purpose.") + " " +
                tr("No guarantees are made regarding the accuracy of any data this program displays.") + "</i></p>"
                "<p><i>" +
                tr("This is NOT medical software, it is merely a research tool that provides a visual interpretation of data recorded by supported devices.")
                +
                "<b> " + tr("This software is NOT suitable for medical diagnostics purposes, neither is it fit for CPAP complaince reporting purposes, or ANY other medical use for that matter.")
                + "</b></i></p>"
                "<p><i>" +
                tr("The author and anyone associated with him accepts NO responsibilty for damages, issues or non-issues resulting from the use or mis-use of this software.")
                + "</p><p><b>" +
                tr("Use this software entirely at your own risk.") + "</b></i></p>"
                "</font></td></tr></table></span></body>"
               ));
    //"</div></body></html>"

    QDialog aboutbox;
    aboutbox.setWindowTitle(QObject::tr("About SleepyHead"));


    QVBoxLayout layout;
    aboutbox.setLayout(&layout);

    QWebView webview(&aboutbox);

    webview.setHtml(msg);
    webview.page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
    connect(&webview, SIGNAL(linkClicked(const QUrl &)), SLOT(aboutBoxLinkClicked(const QUrl &)));

    layout.insertWidget(0, &webview, 1);

    QHBoxLayout layout2;
    layout.insertLayout(1, &layout2, 1);
    QPushButton okbtn(QObject::tr("&Close"), &aboutbox);
    aboutbox.connect(&okbtn, SIGNAL(clicked()), SLOT(reject()));
    layout2.insertWidget(1, &okbtn, 1);

    QPushButton donatebtn(QObject::tr("&Donate"), &aboutbox);
    aboutbox.connect(&donatebtn, SIGNAL(clicked()),
                     SLOT(accept())); //hack this button to use the accepted slot, so clicking x closes like it shouldß
    layout2.insertWidget(1, &donatebtn, 1);

    QApplication::processEvents(); // MW: Needed on Mac, as the html has to finish loading


    if (aboutbox.exec() == QDialog::Accepted) {
        QDesktopServices::openUrl(QUrl("http://sourceforge.net/p/sleepyhead/donate"));
        //spawn browser with paypal site.
    }

    disconnect(&webview, SIGNAL(linkClicked(const QUrl &)), this,
               SLOT(aboutBoxLinkClicked(const QUrl &)));
}

void MainWindow::on_actionDebug_toggled(bool checked)
{
    p_profile->general->setShowDebug(checked);

    logger->strlock.lock();
    if (checked) {
        ui->logText->show();
    } else {
        ui->logText->hide();
    }
  //  QApplication::processEvents();
    logger->strlock.unlock();
}

void MainWindow::on_action_Reset_Graph_Layout_triggered()
{
    if (daily && (ui->tabWidget->currentWidget() == daily)) { daily->ResetGraphLayout(); }

    if (overview && (ui->tabWidget->currentWidget() == overview)) { overview->ResetGraphLayout(); }
}

void MainWindow::on_action_Preferences_triggered()
{
    //MW: TODO: This will crash if attempted to enter while still loading..

    if (m_inRecalculation) {
        mainwin->Notify(tr("Access to Preferences has been blocked until recalculation completes."));
        return;
    }

    PreferencesDialog pd(this, p_profile);
    prefdialog = &pd;

    if (pd.exec() == PreferencesDialog::Accepted) {
        qDebug() << "Preferences Accepted";

        //pd.Save();
        if (daily) {
            //daily->ReloadGraphs();
            daily->RedrawGraphs();
        }

        if (overview) {
            overview->ReloadGraphs();
            overview->RedrawGraphs();
        }
    }

    prefdialog = nullptr;
}

#include "oximeterimport.h"
QDateTime datetimeDialog(QDateTime datetime, QString message);

void MainWindow::on_oximetryButton_clicked()
{
    OximeterImport oxiimp(this);
    oxiimp.exec();
}

void MainWindow::CheckForUpdates()
{
    on_actionCheck_for_Updates_triggered();
}

void MainWindow::on_actionCheck_for_Updates_triggered()
{
    UpdaterWindow *w = new UpdaterWindow(this);
    w->checkForUpdates();
}

void MainWindow::on_action_Screenshot_triggered()
{
    QTimer::singleShot(250, this, SLOT(DelayedScreenshot()));
}
void MainWindow::DelayedScreenshot()
{
    int w = width();
    int h = height();

    // Scale for high resolution displays (like Retina)
#if(QT_VERSION>=QT_VERSION_CHECK(5,0,0))
    qreal pr = devicePixelRatio();
    w /= pr;
    h /= pr;
#endif

    QPixmap pixmap = QPixmap::grabWindow(this->winId(), x(), y(), w, h);

    QString a = PREF.Get("{home}/Screenshots");
    QDir dir(a);

    if (!dir.exists()) {
        dir.mkdir(a);
    }

    a += "/screenshot-" + QDateTime::currentDateTime().toString("yyyyMMdd-hhmmss") + ".png";

    qDebug() << "Saving screenshot to" << a;

    if (!pixmap.save(a)) {
        Notify(tr("There was an error saving screenshot to file \"%1\"").arg(QDir::toNativeSeparators(a)));
    } else {
        Notify(tr("Screenshot saved to file \"%1\"").arg(QDir::toNativeSeparators(a)));
    }
}

void MainWindow::on_actionView_Oximetry_triggered()
{
    on_oximetryButton_clicked();
}
void MainWindow::updatestatusBarMessage(const QString &text)
{
    ui->statusbar->showMessage(text, 1000);
}

void MainWindow::on_actionPrint_Report_triggered()
{
#ifdef Q_WS_MAC
#if ((QT_VERSION <= QT_VERSION_CHECK(4, 8, 4)))
    QMessageBox::information(this, tr("Printing Disabled"),
                             tr("Please rebuild SleepyHead with Qt 4.8.5 or greater, as printing causes a crash with this version of Qt"),
                             QMessageBox::Ok);
    return;
#endif
#endif
    Report report;

    if (ui->tabWidget->currentWidget() == overview) {
        Report::PrintReport(overview->graphView(), STR_TR_Overview);
    } else if (ui->tabWidget->currentWidget() == daily) {
        Report::PrintReport(daily->graphView(), STR_TR_Daily, daily->getDate());
    } else {
        QPrinter printer(QPrinter::HighResolution);
#ifdef Q_WS_X11
        printer.setPrinterName("Print to File (PDF)");
        printer.setOutputFormat(QPrinter::PdfFormat);
        QString name;
        QString datestr;

        if (ui->tabWidget->currentWidget() == ui->statisticsTab) {
            name = "Statistics";
            datestr = QDate::currentDate().toString(Qt::ISODate);
        } else if (ui->tabWidget->currentWidget() == ui->helpTab) {
            name = "Help";
            datestr = QDateTime::currentDateTime().toString(Qt::ISODate);
        } else { name = "Unknown"; }

        QString filename = PREF.Get("{home}/" + name + "_" + p_profile->user->userName() + "_" + datestr + ".pdf");

        printer.setOutputFileName(filename);
#endif
        printer.setPrintRange(QPrinter::AllPages);
        printer.setOrientation(QPrinter::Portrait);
        printer.setFullPage(false); // This has nothing to do with scaling
        printer.setNumCopies(1);
        printer.setPageMargins(5, 5, 5, 5, QPrinter::Millimeter);
        QPrintDialog pdlg(&printer, this);

        if (pdlg.exec() == QPrintDialog::Accepted) {

            if (ui->tabWidget->currentWidget() == ui->statisticsTab) {
                ui->statisticsView->print(&printer);
            } else if (ui->tabWidget->currentWidget() == ui->helpTab) {
                ui->webView->print(&printer);
            }

        }

        //QMessageBox::information(this,tr("Not supported Yet"),tr("Sorry, printing from this page is not supported yet"),QMessageBox::Ok);
    }
}

void MainWindow::on_action_Edit_Profile_triggered()
{
    NewProfile *newprof = new NewProfile(this);
    QString name =PREF[STR_GEN_Profile].toString();
    newprof->edit(name);
    newprof->setWindowModality(Qt::ApplicationModal);
    newprof->setModal(true);
    newprof->exec();
    qDebug()  << newprof;
    delete newprof;
}

void MainWindow::on_action_Link_Graph_Groups_toggled(bool arg1)
{
    p_profile->general->setLinkGroups(arg1);

    if (daily) { daily->RedrawGraphs(); }
}

void MainWindow::on_action_CycleTabs_triggered()
{
    int i;
    qDebug() << "Switching Tabs";
    i = ui->tabWidget->currentIndex() + 1;

    if (i >= ui->tabWidget->count()) {
        i = 0;
    }

    ui->tabWidget->setCurrentIndex(i);
}

void MainWindow::on_actionExp_ort_triggered()
{
    ExportCSV ex(this);

    if (ex.exec() == ExportCSV::Accepted) {
    }
}

void MainWindow::on_actionOnline_Users_Guide_triggered()
{
    ui->webView->load(
        QUrl("http://sleepyhead.sourceforge.net/wiki/index.php?title=SleepyHead_Users_Guide"));
    ui->tabWidget->setCurrentWidget(ui->helpTab);
}

void MainWindow::on_action_Frequently_Asked_Questions_triggered()
{
    ui->webView->load(
        QUrl("http://sleepyhead.sourceforge.net/wiki/index.php?title=Frequently_Asked_Questions"));
    ui->tabWidget->setCurrentWidget(ui->helpTab);
}

void packEventList(EventList *el, EventDataType minval = 0)
{
    if (el->count() < 2) { return; }

    EventList nel(EVL_Waveform);
    EventDataType t = 0, lastt = 0; //el->data(0);
    qint64 ti = 0; //=el->time(0);
    //nel.AddEvent(ti,lastt);
    bool f = false;
    qint64 lasttime = 0;
    EventDataType min = 999, max = 0;


    for (quint32 i = 0; i < el->count(); i++) {
        t = el->data(i);
        ti = el->time(i);
        f = false;

        if (t > minval) {
            if (t != lastt) {
                if (!lasttime) {
                    nel.setFirst(ti);
                }

                nel.AddEvent(ti, t);

                if (t < min) { min = t; }

                if (t > max) { max = t; }

                lasttime = ti;
                f = true;
            }
        } else {
            if (lastt > minval) {
                nel.AddEvent(ti, lastt);
                lasttime = ti;
                f = true;
            }
        }

        lastt = t;
    }

    if (!f) {
        if (t > minval) {
            nel.AddEvent(ti, t);
            lasttime = ti;
        }
    }

    el->setFirst(nel.first());
    el->setLast(nel.last());
    el->setMin(min);
    el->setMax(max);

    el->getData().clear();
    el->getTime().clear();
    el->setCount(nel.count());

    el->getData() = nel.getData();
    el->getTime() = nel.getTime();
}

void MainWindow::on_action_Rebuild_Oximetry_Index_triggered()
{
    QVector<ChannelID> valid;
    valid.push_back(OXI_Pulse);
    valid.push_back(OXI_SPO2);
    valid.push_back(OXI_Plethy);
    //valid.push_back(OXI_PulseChange); // Delete these and recalculate..
    //valid.push_back(OXI_SPO2Drop);

    QVector<ChannelID> invalid;

    QList<Machine *> machines = p_profile->GetMachines(MT_OXIMETER);

    qint64 f = 0, l = 0;

    int discard_threshold = p_profile->oxi->oxiDiscardThreshold();
    Machine *m;

    for (int z = 0; z < machines.size(); z++) {
        m = machines.at(z);
        //m->sessionlist.erase(m->sessionlist.find(0));

        // For each Session
        for (QHash<SessionID, Session *>::iterator s = m->sessionlist.begin(); s != m->sessionlist.end();
                s++) {
            Session *sess = s.value();

            if (!sess) { continue; }

            sess->OpenEvents();

            // For each EventList contained in session
            invalid.clear();
            f = 0;
            l = 0;

            for (QHash<ChannelID, QVector<EventList *> >::iterator e = sess->eventlist.begin();
                    e != sess->eventlist.end(); e++) {

                // Discard any non data events.
                if (!valid.contains(e.key())) {
                    // delete and push aside for later to clean up
                    for (int i = 0; i < e.value().size(); i++)  {
                        delete e.value()[i];
                    }

                    e.value().clear();
                    invalid.push_back(e.key());
                } else {
                    // Valid event


                    //                    // Clean up outliers at start of eventlist chunks
                    //                    EventDataType baseline=sess->wavg(OXI_SPO2);
                    //                    if (e.key()==OXI_SPO2) {
                    //                        const int o2start_threshold=10000; // seconds since start of event

                    //                        EventDataType zz;
                    //                        int ii;

                    //                        // Trash suspect outliers in the first o2start_threshold milliseconds
                    //                        for (int j=0;j<e.value().size();j++) {
                    //                            EventList *ev=e.value()[j];
                    //                            if ((ev->count() <= (unsigned)discard_threshold))
                    //                                continue;

                    //                            qint64 ti=ev->time(0);

                    //                            zz=-1;
                    //                            // Peek o2start_threshold ms ahead and grab the value
                    //                            for (ii=0;ii<ev->count();ii++) {
                    //                                if (((ev->time(ii)-ti) > o2start_threshold)) {
                    //                                    zz=ev->data(ii);
                    //                                    break;
                    //                                }
                    //                            }
                    //                            if (zz<0)
                    //                                continue;
                    //                            // Trash any suspect outliers
                    //                            for (int i=0;i<ii;i++) {
                    //                                if (ev->data(i) < baseline) { //(zz-10)) {

                    //                                    ev->getData()[i]=0;
                    //                                }
                    //                            }
                    //                        }
                    //                    }
                    QVector<EventList *> newlist;

                    for (int i = 0; i < e.value().size(); i++)  {
                        if (e.value()[i]->count() > (unsigned)discard_threshold) {
                            newlist.push_back(e.value()[i]);
                        } else {
                            delete e.value()[i];
                        }
                    }

                    for (int i = 0; i < newlist.size(); i++) {
                        packEventList(newlist[i], 8);

                        EventList *el = newlist[i];

                        if (!f || f > el->first()) { f = el->first(); }

                        if (!l || l < el->last()) { l = el->last(); }
                    }

                    e.value() = newlist;
                }
            }

            for (int i = 0; i < invalid.size(); i++) {
                sess->eventlist.erase(sess->eventlist.find(invalid[i]));
            }

            if (f) { sess->really_set_first(f); }

            if (l) { sess->really_set_last(l); }

            sess->m_cnt.clear();
            sess->m_sum.clear();
            sess->m_min.clear();
            sess->m_max.clear();
            sess->m_cph.clear();
            sess->m_sph.clear();
            sess->m_avg.clear();
            sess->m_wavg.clear();
            sess->m_valuesummary.clear();
            sess->m_timesummary.clear();
            sess->m_firstchan.clear();
            sess->m_lastchan.clear();
            sess->SetChanged(true);
        }

    }

    for (int i = 0; i < machines.size(); i++) {
        Machine *m = machines[i];
        m->Save();
    }

    getDaily()->LoadDate(getDaily()->getDate());
    //getDaily()->ReloadGraphs();
    getOverview()->ReloadGraphs();
}

void MainWindow::RestartApplication(bool force_login, bool change_datafolder)
{
    QString apppath;
#ifdef Q_OS_MAC
    // In Mac OS the full path of aplication binary is:
    //    <base-path>/myApp.app/Contents/MacOS/myApp

    // prune the extra bits to just get the app bundle path
    apppath = QApplication::instance()->applicationDirPath().section("/", 0, -3);

    QStringList args;
    args << "-n"; // -n option is important, as it opens a new process
    args << apppath;

    args << "--args"; // SleepyHead binary options after this
    args << "-p"; // -p starts with 1 second delay, to give this process time to save..


    if (force_login) { args << "-l"; }

    if (change_datafolder) { args << "-d"; }

    if (QProcess::startDetached("/usr/bin/open", args)) {
        QApplication::instance()->exit();
    } else { QMessageBox::warning(nullptr, tr("Gah!"), tr("If you can read this, the restart command didn't work. Your going to have to do it yourself manually."), QMessageBox::Ok); }

#else
    apppath = QApplication::instance()->applicationFilePath();

    // If this doesn't work on windoze, try uncommenting this method
    // Technically should be the same thing..

    //if (QDesktopServices::openUrl(apppath)) {
    //    QApplication::instance()->exit();
    //} else
    QStringList args;
    args << "-p";

    if (force_login) { args << "-l"; }

    if (change_datafolder) { args << "-d"; }

    if (QProcess::startDetached(apppath, args)) {
        ::exit(0);
        //QApplication::instance()->exit();
    } else { QMessageBox::warning(nullptr, tr("Gah!"), tr("If you can read this, the restart command didn't work. Your going to have to do it yourself manually."), QMessageBox::Ok); }

#endif
}

void MainWindow::on_actionChange_User_triggered()
{
    p_profile->Save();
    PREF.Save();
    p_profile->removeLock();

    RestartApplication(true);
}

void MainWindow::on_actionPurge_Current_Day_triggered()
{
    QDate date = getDaily()->getDate();
    getDaily()->Unload(date);
    Day *day = p_profile->GetDay(date, MT_CPAP);
    Machine *m;

    if (day) {
        m = day->machine;
        QString path = p_profile->Get("{" + STR_GEN_DataFolder + "}/") + m->GetClass() + "_" +
                       m->properties[STR_PROP_Serial] + "/";

        QList<Session *>::iterator s;

        QList<Session *> list;

        for (s = day->begin(); s != day->end(); ++s) {
            SessionID id = (*s)->session();
            QString filename0 = path + QString().sprintf("%08lx.000", id);
            QString filename1 = path + QString().sprintf("%08lx.001", id);
            qDebug() << "Removing" << filename0;
            qDebug() << "Removing" << filename1;
            QFile::remove(filename0);
            QFile::remove(filename1);

            list.push_back(*s);
            m->sessionlist.erase(m->sessionlist.find(id)); // remove from machines session list
        }

        m->day.erase(m->day.find(date));

        for (int i = 0; i < list.size(); i++) {
            Session *sess = list.at(i);
            day->removeSession(sess);
            delete sess;
        }

        QList<Day *> &dl = p_profile->daylist[date];
        QList<Day *>::iterator it;//=dl.begin();

        for (it = dl.begin(); it != dl.end(); it++) {
            if ((*it) == day) { break; }
        }

        if (it != dl.end()) {
            dl.erase(it);
            //p_profile->daylist[date].  // ??
            delete day;
        }
    }

    getDaily()->clearLastDay();
    getDaily()->LoadDate(date);
}

void MainWindow::on_actionPurgeMachine(QAction *action)
{
    QString data = action->data().toString();
    QString cls = data.section(":",0,0);
    QString serial = data.section(":", 1);
    QList<Machine *> machines = p_profile->GetMachines(MT_CPAP);
    Machine * mach = nullptr;
    for (int i=0; i < machines.size(); ++i) {
        Machine * m = machines.at(i);
        if ((m->GetClass() == cls) && (m->properties[STR_PROP_Serial] == serial)) {
            mach = m;
            break;
        }
    }
    if (!mach) return;
    purgeMachine(mach);
}

void MainWindow::purgeMachine(Machine * mach)
{

    if (QMessageBox::question(this,
                              STR_MessageBox_Question,
                              tr("Are you sure you want to purge all CPAP data for the following machine:")+ "\n\n" +
                              mach->properties[STR_PROP_Brand] + " " + mach->properties[STR_PROP_Model] + " " +
                              mach->properties[STR_PROP_ModelNumber] + " (" + mach->properties[STR_PROP_Serial] + ")",
                              QMessageBox::Yes | QMessageBox::No,
                              QMessageBox::No) == QMessageBox::No) {
        return;
    }
    daily->Unload(daily->getDate());

    // Technicially the above won't sessions under short session limit.. Using Purge to clean up the rest.
    if (mach->Purge(3478216)) {
        mach->sessionlist.clear();
        mach->day.clear();
    } else {
        QMessageBox::warning(this, STR_MessageBox_Error,
                             tr("Not all session data could be removed, you have to delete the following folder manually.")
                             +"\n\n"+
                             QDir::toNativeSeparators(p_profile->Get(mach->properties[STR_PROP_Path])), QMessageBox::Ok, QMessageBox::Ok);
        if (overview) overview->ReloadGraphs();

        if (daily) {
            daily->clearLastDay(); // otherwise Daily will crash
            daily->ReloadGraphs();
        }
        GenerateStatistics();
        return;
    }

    if (overview) overview->ReloadGraphs();

    if (daily) {
        daily->clearLastDay(); // otherwise Daily will crash
        daily->ReloadGraphs();
    }
    GenerateStatistics();
    QApplication::processEvents();

    if (QMessageBox::question(this,
                              STR_MessageBox_Question,
                              tr("Machine data has been successfully purged.") + "\n\n" +
                              tr("Would you like to reimport from the backup folder?") + "\n\n" +
                              QDir::toNativeSeparators(p_profile->Get(mach->properties[STR_PROP_BackupPath])),
                              QMessageBox::Yes | QMessageBox::No,
                              QMessageBox::Yes) == QMessageBox::No) {
        p_profile->machlist.erase(p_profile->machlist.find(mach->id()));
        delete mach;
    } else {
        importCPAP(p_profile->Get(mach->properties[STR_PROP_BackupPath]),tr("Please wait, importing..."));
        if (overview) overview->ReloadGraphs();
        if (daily) {
            daily->clearLastDay(); // otherwise Daily will crash
            daily->ReloadGraphs();
        }
    }
    GenerateStatistics();
    p_profile->Save();
}

void MainWindow::keyPressEvent(QKeyEvent *event)
{
    Q_UNUSED(event)
    //qDebug() << "Keypress:" << event->key();
}

void MainWindow::on_action_Sidebar_Toggle_toggled(bool visible)
{
    ui->toolBox->setVisible(visible);
}

void MainWindow::on_recordsBox_linkClicked(const QUrl &linkurl)
{
    QString link = linkurl.toString().section("=", 0, 0).toLower();
    QString datestr = linkurl.toString().section("=", 1).toLower();
    qDebug() << linkurl.toString() << link << datestr;

    if (link == "daily") {
        QDate date = QDate::fromString(datestr, Qt::ISODate);
        daily->LoadDate(date);
        ui->tabWidget->setCurrentWidget(daily);
    } else if (link == "overview") {
        QString date1 = datestr.section(",", 0, 0);
        QString date2 = datestr.section(",", 1);

        QDate d1 = QDate::fromString(date1, Qt::ISODate);
        QDate d2 = QDate::fromString(date2, Qt::ISODate);
        overview->setRange(d1, d2);
        ui->tabWidget->setCurrentWidget(overview);
    }

}

void MainWindow::on_helpButton_clicked()
{
    ui->tabWidget->setCurrentWidget(ui->helpTab);
}

void MainWindow::on_actionView_Statistics_triggered()
{
    ui->tabWidget->setCurrentWidget(ui->statisticsTab);
}

void MainWindow::on_webView_linkClicked(const QUrl &url)
{
    QString s = url.toString();
    qDebug() << "Link Clicked" << url;

    if (s.toLower().startsWith("https:")) {
        QDesktopServices().openUrl(url);
    } else {
        ui->webView->setUrl(url);
    }
}

void MainWindow::on_webView_statusBarMessage(const QString &text)
{
    ui->statusbar->showMessage(text);
}

void MainWindow::LinkHovered(const QString &link, const QString &title, const QString &textContent)
{
    Q_UNUSED(title);
    Q_UNUSED(textContent);
    ui->statusbar->showMessage(link);
}

void MainWindow::on_tabWidget_currentChanged(int index)
{
    Q_UNUSED(index);
    QWidget *widget = ui->tabWidget->currentWidget();

    if ((widget == ui->statisticsTab) || (widget == ui->helpTab)) {
        qstatus2->setVisible(false);
    } else if (widget == daily) {
        qstatus2->setVisible(true);
        daily->graphView()->updateSelectionTime();
    } else if (widget == overview) {
        qstatus2->setVisible(true);
        overview->graphView()->updateSelectionTime();
    }
}


void MainWindow::on_bookmarkView_linkClicked(const QUrl &arg1)
{
    on_recordsBox_linkClicked(arg1);
}

void MainWindow::on_filterBookmarks_editingFinished()
{
    bookmarkFilter = ui->filterBookmarks->text();
    updateFavourites();
}

void MainWindow::on_filterBookmarksButton_clicked()
{
    if (!bookmarkFilter.isEmpty()) {
        ui->filterBookmarks->setText("");
        bookmarkFilter = "";
        updateFavourites();
    }
}

void MainWindow::reprocessEvents(bool restart)
{
    m_restartRequired = restart;
    QTimer::singleShot(0, this, SLOT(doReprocessEvents()));
}


void MainWindow::FreeSessions()
{
    QDate first = p_profile->FirstDay();
    QDate date = p_profile->LastDay();
    Day *day;
    QDate current = daily->getDate();

    do {
        day = p_profile->GetDay(date, MT_CPAP);

        if (day) {
            if (date != current) {
                day->CloseEvents();
            }
        }

        date = date.addDays(-1);
    }  while (date >= first);
}

void MainWindow::doReprocessEvents()
{
    if (p_profile->countDays(MT_CPAP, p_profile->FirstDay(), p_profile->LastDay()) == 0) {
        return;
    }

    m_inRecalculation = true;
    QDate first = p_profile->FirstDay();
    QDate date = p_profile->LastDay();
    Session *sess;
    Day *day;
    //FlowParser flowparser;

    mainwin->Notify(tr("Performance will be degraded during these recalculations."),
                    tr("Recalculating Indices"));

    // For each day in history
    int daycount = first.daysTo(date);
    int idx = 0;

    QList<Machine *> machines = p_profile->GetMachines(MT_CPAP);

    // Disabling multithreaded save as it appears it's causing problems
    bool cache_sessions = false; //p_profile->session->cacheSessions();

    if (cache_sessions) { // Use multithreaded save to handle reindexing.. (hogs memory like hell)
        qstatus->setText(tr("Loading Event Data"));
    } else {
        qstatus->setText(tr("Recalculating Summaries"));
    }

    if (qprogress) {
        qprogress->setValue(0);
        qprogress->setVisible(true);
    }

    bool isopen;

    do {
        day = p_profile->GetDay(date, MT_CPAP);

        if (day) {
            for (int i = 0; i < day->size(); i++) {
                sess = (*day)[i];
                isopen = sess->eventsLoaded();

                // Load the events if they aren't loaded already
                sess->OpenEvents();

                //if (!sess->channelDataExists(CPAP_FlowRate)) continue;

                //QVector<EventList *> & flowlist=sess->eventlist[CPAP_FlowRate];

                // Destroy any current user flags..
                sess->destroyEvent(CPAP_UserFlag1);
                sess->destroyEvent(CPAP_UserFlag2);
                sess->destroyEvent(CPAP_UserFlag3);

                // AHI flags
                sess->destroyEvent(CPAP_AHI);
                sess->destroyEvent(CPAP_RDI);

                sess->SetChanged(true);

                if (!cache_sessions) {
                    sess->UpdateSummaries();
                    sess->machine()->SaveSession(sess);

                    if (!isopen) {
                        sess->TrashEvents();
                    }
                }
            }
        }

        date = date.addDays(-1);
        // if (qprogress && (++idx % 10) ==0) {
        qprogress->setValue(0 + (float(++idx) / float(daycount) * 100.0));
        QApplication::processEvents();
        // }

    } while (date >= first);

    if (cache_sessions) {
        qstatus->setText(tr("Recalculating Summaries"));

        for (int i = 0; i < machines.size(); i++) {
            machines.at(i)->Save();
        }
    }

    qstatus->setText(tr(""));
    qprogress->setVisible(false);
    m_inRecalculation = false;

    if (m_restartRequired) {
        QMessageBox::information(this, tr("Restart Required"),
                                 tr("Recalculations are complete, the application now needs to restart to display the changes."),
                                 QMessageBox::Ok);
        RestartApplication();
        return;
    } else {
        Notify(tr("Recalculations are now complete."), tr("Task Completed"));

        FreeSessions();
        QDate current = daily->getDate();
        daily->LoadDate(current);

        if (overview) { overview->ReloadGraphs(); }
    }
}

void MainWindow::on_actionImport_ZEO_Data_triggered()
{
    QFileDialog w;
    w.setFileMode(QFileDialog::ExistingFiles);
    w.setOption(QFileDialog::ShowDirsOnly, false);
    w.setOption(QFileDialog::DontUseNativeDialog, true);
    w.setNameFilters(QStringList("Zeo CSV File (*.csv)"));

    ZEOLoader zeo;

    if (w.exec() == QFileDialog::Accepted) {
        QString filename = w.selectedFiles()[0];

        if (!zeo.OpenFile(filename)) {
            Notify(tr("There was a problem opening ZEO File: ") + filename);
            return;
        }

        Notify(tr("Zeo CSV Import complete"));
        daily->LoadDate(daily->getDate());
    }


}

void MainWindow::on_actionImport_RemStar_MSeries_Data_triggered()
{
#ifdef REMSTAR_M_SUPPORT
    QFileDialog w;
    w.setFileMode(QFileDialog::ExistingFiles);
    w.setOption(QFileDialog::ShowDirsOnly, false);
    w.setOption(QFileDialog::DontUseNativeDialog, true);
    w.setNameFilters(QStringList("M-Series data file (*.bin)"));

    MSeriesLoader mseries;

    if (w.exec() == QFileDialog::Accepted) {
        QString filename = w.selectedFiles()[0];

        if (!mseries.Open(filename, p_profile)) {
            Notify(tr("There was a problem opening MSeries block File: ") + filename);
            return;
        }

        Notify(tr("MSeries Import complete"));
        daily->LoadDate(daily->getDate());
    }

#endif
}

void MainWindow::on_actionSleep_Disorder_Terms_Glossary_triggered()
{
    ui->webView->load(
        QUrl("http://sleepyhead.sourceforge.net/wiki/index.php?title=Glossary"));
    ui->tabWidget->setCurrentWidget(ui->helpTab);
}

void MainWindow::on_actionHelp_Support_SleepyHead_Development_triggered()
{
    QUrl url =
        QUrl("http://sleepyhead.sourceforge.net/wiki/index.php?title=Support_SleepyHead_Development");
    QDesktopServices().openUrl(url);
    //    ui->webView->load(url);
    //    ui->tabWidget->setCurrentWidget(ui->helpTab);
}

void MainWindow::on_actionChange_Language_triggered()
{
    QSettings *settings = new QSettings(getDeveloperName(), getAppName());
    settings->remove("Settings/Language");
    delete settings;
    p_profile->Save();
    PREF.Save();
    p_profile->removeLock();

    RestartApplication(true);
}

void MainWindow::on_actionChange_Data_Folder_triggered()
{
    p_profile->Save();
    PREF.Save();
    p_profile->removeLock();

    RestartApplication(false, true);
}

void MainWindow::on_actionImport_Somnopose_Data_triggered()
{
    QFileDialog w;
    w.setFileMode(QFileDialog::ExistingFiles);
    w.setOption(QFileDialog::ShowDirsOnly, false);
    w.setOption(QFileDialog::DontUseNativeDialog, true);
    w.setNameFilters(QStringList("Somnopause CSV File (*.csv)"));

    SomnoposeLoader somno;

    if (w.exec() == QFileDialog::Accepted) {
        QString filename = w.selectedFiles()[0];

        if (!somno.OpenFile(filename)) {
            Notify(tr("There was a problem opening Somnopose Data File: ") + filename);
            return;
        }

        Notify(tr("Somnopause Data Import complete"));
        daily->LoadDate(daily->getDate());
    }

}

void MainWindow::GenerateStatistics()
{
    QDate first = p_profile->FirstDay();
    QDate last = p_profile->LastDay();
    ui->statStartDate->setMinimumDate(first);
    ui->statStartDate->setMaximumDate(last);

    ui->statEndDate->setMinimumDate(first);
    ui->statEndDate->setMaximumDate(last);

    Statistics stats;
    QString html = stats.GenerateHTML();

    updateFavourites();

    //QWebFrame *frame=ui->statisticsView->page()->currentFrame();
    //frame->addToJavaScriptWindowObject("mainwin",this);
    //ui->statisticsView->setHtml(html);
    MyStatsPage *page = new MyStatsPage(this);
    page->currentFrame()->setHtml(html);
    ui->statisticsView->setPage(page);
    //    connect(ui->statisticsView->page()->currentFrame(),SIGNAL(javaScriptWindowObjectCleared())
    //    QString file="qrc:/docs/index.html";
    //    QUrl url(file);
    //    ui->webView->setUrl(url);
}


void MainWindow::on_statisticsButton_clicked()
{
    ui->tabWidget->setCurrentWidget(ui->statisticsTab);
}

void MainWindow::on_statisticsView_linkClicked(const QUrl &arg1)
{
    //qDebug() << arg1;
    on_recordsBox_linkClicked(arg1);
}

void MainWindow::on_reportModeMonthly_clicked()
{
    ui->statStartDate->setVisible(false);
    ui->statEndDate->setVisible(false);
    if (p_profile->general->statReportMode() != 1) {
        p_profile->general->setStatReportMode(1);
        GenerateStatistics();
    }
}

void MainWindow::on_reportModeStandard_clicked()
{
    ui->statStartDate->setVisible(false);
    ui->statEndDate->setVisible(false);
    if (p_profile->general->statReportMode() != 0) {
        p_profile->general->setStatReportMode(0);
        GenerateStatistics();
    }
}


void MainWindow::on_reportModeRange_clicked()
{
    ui->statStartDate->setVisible(true);
    ui->statEndDate->setVisible(true);
    if (p_profile->general->statReportMode() != 2) {
        p_profile->general->setStatReportMode(2);
        GenerateStatistics();
    }
}

void MainWindow::on_actionPurgeCurrentDaysOximetry_triggered()
{
    if (!getDaily())
        return;
    QDate date = getDaily()->getDate();
    Day * day = p_profile->GetDay(date, MT_OXIMETER);
    if (day) {
        if (QMessageBox::question(this, STR_MessageBox_Warning,
            tr("Are you sure you want to delete oximetry data for %1").
                arg(getDaily()->getDate().toString(Qt::DefaultLocaleLongDate))+"<br/><br/>"+
            tr("<b>Please be aware you can not undo this operation!</b>"),
            QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No) {
            return;
        }

        QList<Session *> sessions;
        sessions.append(day->getSessions());

        for (int i=0; i < sessions.size(); ++i) {
            sessions.at(i)->Destroy();
        }


        daily->clearLastDay(); // otherwise Daily will crash

        getDaily()->ReloadGraphs();
    } else {
        QMessageBox::information(this, STR_MessageBox_Information,
            tr("Select the day with valid oximetry data in daily view first."),QMessageBox::Ok);
    }
}

void MainWindow::on_importButton_clicked()
{
    on_action_Import_Data_triggered();
}