/* -*- 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 <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 "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 "summary.h"

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

extern Profile * profile;

void MainWindow::Log(QString s)
{

    if (!strlock.tryLock())
        return;

//  strlock.lock();
    QString tmp=QString("%1: %2").arg(logtime.elapsed(),5,10,QChar('0')).arg(s);

    logbuffer.append(tmp); //QStringList appears not to be threadsafe
    strlock.unlock();

    strlock.lock();
    // only do this in the main thread?
    for (int i=0;i<logbuffer.size();i++) {
        ui->logText->appendPlainText(logbuffer[i]);
        fprintf(stderr,"%s\n",logbuffer[i].toLocal8Bit().constData());
    }
    logbuffer.clear();
    strlock.unlock();

    //loglock.unlock();
}

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

    logtime.start();
    ui->setupUi(this);

    QString version=FullVersionString;
    if (QString(GIT_BRANCH)!="master") version+=QString(" ")+QString(GIT_BRANCH);
    this->setWindowTitle(STR_TR_SleepyHead+QString(" v%1 ("+tr("Profile")+": %2)").arg(version).arg(PREF[STR_GEN_Profile].toString()));
    //ui->tabWidget->setCurrentIndex(1);

#ifdef Q_OS_MAC
#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

    overview=NULL;
    daily=NULL;
    oximetry=NULL;
    prefdialog=NULL;

    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(PROFILE.general->showDebug());

    if (!PROFILE.general->showDebug()) {
        ui->logText->hide();
    }

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

    first_load=true;

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

    // Load previous Window geometry
    this->restoreGeometry(settings.value("MainWindow/geometry").toByteArray());

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


    // Start with the Summary Tab
    ui->tabWidget->setCurrentWidget(ui->summaryTab); // 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=NULL;
        systraymenu=NULL;
    }
    ui->toolBox->setCurrentIndex(0);
    daily->graphView()->redraw();

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

    ui->recordsBox->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
    ui->summaryView->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->summaryView->setHtml(loadingtxt);
    on_tabWidget_currentChanged(0);

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

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

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

    if (daily) {
        daily->close();
        delete daily;
    }
    if (overview) {
        overview->close();
        delete overview;
    }
    if (oximetry) {
        oximetry->close();
        delete oximetry;
    }

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

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

    mainwin=NULL;
    delete ui;
}


void MainWindow::Notify(QString s, QString title, int ms)
{
    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;
        char *desktop = getenv("DESKTOP_SESSION");
        if (desktop && !strncmp(desktop, "gnome", 5))
            msg += "\n";

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

void MainWindow::Startup()
{
    qDebug() << STR_TR_SleepyHeadVersion.toLocal8Bit().data() << "built with Qt"<< QT_VERSION_STR << "on" << __DATE__ << __TIME__;
    qstatus->setText(tr("Loading Data"));
    qprogress->show();
    //qstatusbar->showMessage(tr("Loading Data"),0);

    // profile is a global variable set in main after login
    PROFILE.LoadMachineData();

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

    // the following are platform overides for the UsePixmapCache preference settings
#ifdef Q_OS_MAC
    //Mac needs this to be able to offscreen render
    SnapshotGraph->setUsePixmapCache(true);
#else
    //Windows & Linux barfs when offscreen rendering with pixmap cached text
    SnapshotGraph->setUsePixmapCache(false);
#endif

    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);
    if (PROFILE.oxi->oximetryEnabled()) {
        oximetry=new Oximetry(ui->tabWidget,daily->graphView());
        ui->tabWidget->insertTab(3,oximetry,STR_TR_Oximetry);
    }


    ui->tabWidget->setCurrentWidget(ui->summaryTab);
    if (daily) daily->ReloadGraphs();
    if (overview) overview->ReloadGraphs();
    qprogress->hide();
    qstatus->setText("");
    on_summaryButton_clicked();

}

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

    }

    QStringList importLocations;
    {
        QString filename=PROFILE.Get("{DataFolder}/ImportLocations.txt");
        QFile file(filename);
        file.open(QFile::ReadOnly);
        QTextStream textStream(&file);
        while (1) {
            QString line = textStream.readLine();
             if (line.isNull())
                 break;
             else if (line.isEmpty())
                 continue;
             else {
                 importLocations.append(line);
             }
        }
        file.close();
    }

    //bool addnew=false;
    QString newdir;

    bool asknew=false;
    if (importLocations.size()==0) {
        asknew=true;
    } else {
        int res=QMessageBox::question(this,tr("Import from where?"),tr("Do you just want to Import from the usual (remembered) locations?\n"),tr("The Usual"),tr("New Location"),tr("Cancel"),0,2);
        if (res==1) {
            asknew=true;
        }
        if (res==2) return;
    }
    if (asknew) {
        //mainwin->Notify("Please remember to point the importer at the root folder or drive letter of your data-card, and not a subfolder.","Import Reminder",8000);
    }

    QStringList importFrom;

    if (asknew) {
        QFileDialog w;
        w.setFileMode(QFileDialog::Directory);
        w.setOption(QFileDialog::ShowDirsOnly, true);

#if defined(Q_OS_MAC) && (QT_VERSION_CHECK(4,8,0) > QT_VERSION)
        // Fix for tetragon, 10.6 barfs up Qt's custom dialog
        w.setOption(QFileDialog::DontUseNativeDialog,true);
#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) {
            return;
        }
        for (int i=0;i<w.selectedFiles().size();i++) {
            QString newdir=w.selectedFiles().at(i);
            if (!importFrom.contains(newdir)) {
                importFrom.append(newdir);
                //addnew=true;
            }
        }
    } else importFrom=importLocations;

    int successful=false;

    QStringList goodlocations;
    for (int i=0;i<importFrom.size();i++) {
        QString dir=importFrom[i];
        if (!dir.isEmpty()) {
            qprogress->setValue(0);
            qprogress->show();
            qstatus->setText(tr("Importing Data"));
            int c=PROFILE.Import(dir);
            qDebug() << "Finished Importing data" << c;
            if (c) {
                if (!importLocations.contains(dir))
                    goodlocations.push_back(dir);
                successful=true;
            }
            qstatus->setText("");
            qprogress->hide();
        }
    }
    if (successful) {
        PROFILE.Save();
        if (overview) overview->ReloadGraphs();
        on_summaryButton_clicked();
        if (daily) daily->ReloadGraphs();
        if ((goodlocations.size()>0) && (QMessageBox::question(this,tr("Remember this Location?"),tr("Would you like to remember this import location for next time?")+"\n"+newdir,QMessageBox::Yes,QMessageBox::No)==QMessageBox::Yes)) {
            for (int i=0;i<goodlocations.size();i++) {
                importLocations.push_back(goodlocations[i]);
            }
            QString filename=PROFILE.Get("{DataFolder}/ImportLocations.txt");
            QFile file(filename);
            file.open(QFile::WriteOnly);
            QTextStream ts(&file);
            for (int i=0;i<importLocations.size();i++) {
                ts << importLocations[i] << endl;
               //file.write(importLocations[i].toUtf8());
            }
            file.close();
        }
    } else {
        mainwin->Notify(tr("Import Problem\n\nCouldn't find any new Machine Data at the locations given"));
    }
}
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, Auto, BiPAP & ASV models)")+"</li>"
"<li>"+tr("ResMed S9 models (CPAP, Auto, VPAP)")+"</li>"
"<li>"+tr("DeVilbiss Intellipap (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://sourceforge.net/apps/mediawiki/sleepyhead/index.php?title=SleepyHead_Users_Guide\">Users Guide</a><br/>")+
tr("<a href=\"http://sourceforge.net/apps/mediawiki/sleepyhead/index.php?title=Frequently_Asked_Questions\">Frequently Asked Questions</a><br/>")+
tr("<a href=\"http://sourceforge.net/apps/mediawiki/sleepyhead/index.php?title=Glossary\">Glossary of Sleep Disorder Terms</a><br/>")+
tr("<a href=\"http://sourceforge.net/apps/mediawiki/sleepyhead/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/>")+
tr("Got a neat idea on how to improve SleepyHead? Check out SleepyHeads <a href=\"http://sourceforge.net/apps/ideatorrent/sleepyhead/\">Idea Torrent</a>")+
"<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://s7.zetaboards.com/Apnea_Board/index'>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::on_summaryButton_clicked()
{
    QString html=Summary::GenerateHTML();

    updateFavourites();

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


void MainWindow::updateFavourites()
{
    QDate date=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=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>=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="Revision: "+gitrev+" ("+QString(GIT_BRANCH)+" 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+" v%1.%2.%3-%4 (%8)</h1></p><font color=black><p>"+tr("Build Date")+": %5 %6<br/>%7<br/>"+tr("Data Folder Location")+": %9<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\">http://sleepyhead.sourceforge.net</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, Patricia Shanahan, Alec Clews, manders99, and Sean Stangl.")+"</p>"

"<p><b>"+tr("Translators:")+"</b> "+tr("Arie Klerk (Dutch), Steffen Reitz (German).")+"</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>"
).arg(major_version).arg(minor_version).arg(revision_number).arg(release_number).arg(__DATE__).arg(__TIME__).arg(gitrev).arg(ReleaseStatus).arg(QDir::toNativeSeparators(GetAppRoot()));
    //"</div></body></html>"

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


     QVBoxLayout layout(&aboutbox);

     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(&aboutbox);
     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)
{
    PROFILE.general->setShowDebug(checked);
    if (checked) {
        ui->logText->show();
    } else {
        ui->logText->hide();
    }
}

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=NULL;
}
void MainWindow::selectOximetryTab()
{
    on_oximetryButton_clicked();
}

void MainWindow::on_oximetryButton_clicked()
{
    bool first=false;
    if (!oximetry) {
        if (!PROFILE.oxi->oximetryEnabled()) {
            if (QMessageBox::question(this,tr("Question"),tr("Do you have a CMS50[x] Oximeter?\nOne is required to use this section."),QMessageBox::Yes,QMessageBox::No)==QMessageBox::No) return;
            PROFILE.oxi->setOximetryEnabled(true);
        }
        oximetry=new Oximetry(ui->tabWidget,daily->graphView());
        ui->tabWidget->insertTab(3,oximetry,STR_TR_Oximetry);
        first=true;
    }

    // MW: Instead, how about starting a direct import?
    oximetry->serialImport();

    ui->tabWidget->setCurrentWidget(oximetry);
    if (!first) oximetry->RedrawGraphs();
    qstatus2->setText(STR_TR_Oximetry);
}

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 if (ui->tabWidget->currentWidget()==oximetry) {
        if (oximetry)
            Report::PrintReport(oximetry->graphView(),STR_TR_Oximetry);
    } else {
        QPrinter printer;
#ifdef Q_WS_X11
        printer.setPrinterName("Print to File (PDF)");
        printer.setOutputFormat(QPrinter::PdfFormat);
        QString name;
        QString datestr;
        if (ui->tabWidget->currentWidget()==ui->summaryTab) {
            name="Summary";
            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+"_"+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(10,10,10,10,QPrinter::Millimeter);

        QPrintDialog pdlg(&printer,this);
        if (pdlg.exec()==QPrintDialog::Accepted) {

            if (ui->tabWidget->currentWidget()==ui->summaryTab) {
                ui->summaryView->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(this);
    newprof.edit(PREF[STR_GEN_Profile].toString());
    newprof.exec();

}

void MainWindow::on_action_Link_Graph_Groups_toggled(bool arg1)
{
    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://sourceforge.net/apps/mediawiki/sleepyhead/index.php?title=SleepyHead_Users_Guide"));
    ui->tabWidget->setCurrentWidget(ui->helpTab);
}

void MainWindow::on_action_Frequently_Asked_Questions_triggered()
{
    ui->webView->load(QUrl("http://sourceforge.net/apps/mediawiki/sleepyhead/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=PROFILE.GetMachines(MT_OXIMETER);

    qint64 f=0,l=0;

    int discard_threshold=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(NULL,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(NULL,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()
{
    PROFILE.Save();
    PREF.Save();
    RestartApplication(true);
}

void MainWindow::on_actionPurge_Current_Day_triggered()
{
    QDate date=getDaily()->getDate();
    Day *day=PROFILE.GetDay(date,MT_CPAP);
    Machine *m;
    if (day) {
        m=day->machine;
        QString path=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=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);
            //PROFILE.daylist[date].  // ??
            delete day;
        }
    }
    getDaily()->clearLastDay();
    getDaily()->LoadDate(date);
}

void MainWindow::on_actionAll_Data_for_current_CPAP_machine_triggered()
{
    QDate date=getDaily()->getDate();
    Day *day=PROFILE.GetDay(date,MT_CPAP);
    Machine *m;
    if (day) {
        m=day->machine;
        if (!m) {
            qDebug() << "Gah!! no machine to purge";
            return;
        }
        if (QMessageBox::question(this,tr("Are you sure?"),tr("Are you sure you want to purge all CPAP data for the following machine:\n")+m->properties[STR_PROP_Brand]+" "+m->properties[STR_PROP_Model]+" "+m->properties[STR_PROP_ModelNumber]+" ("+m->properties[STR_PROP_Serial]+")",QMessageBox::Yes,QMessageBox::No)==QMessageBox::Yes) {
            m->Purge(3478216);
            PROFILE.machlist.erase(PROFILE.machlist.find(m->id()));
            // delete or not to delete.. this needs to delete later.. :/
            //delete m;
            RestartApplication();
        }
    }
}

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

void MainWindow::on_summaryButton_2_clicked()
{
    ui->tabWidget->setCurrentWidget(ui->summaryTab);
    on_summaryButton_clicked();
}

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->summaryTab);
}

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->summaryTab) || (widget==ui->helpTab)) {
        qstatus2->setVisible(false);
    } else if (widget==daily) {
        qstatus2->setVisible(true);
        daily->graphView()->selectionTime();
    } else if (widget==overview) {
        qstatus2->setVisible(true);
        overview->graphView()->selectionTime();
    } else if (widget==oximetry) {
        qstatus2->setVisible(true);
        oximetry->graphView()->selectionTime();
    }
}

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

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=PROFILE.FirstDay();
    QDate date=PROFILE.LastDay();
    Day *day;
    QDate current=daily->getDate();
    do {
        day=PROFILE.GetDay(date,MT_CPAP);
        if (day) {
            if (date!=current) {
                day->CloseEvents();
            }
        }
        date=date.addDays(-1);
    }  while (date>=first);
}

void MainWindow::doReprocessEvents()
{
    if (PROFILE.countDays(MT_CPAP,PROFILE.FirstDay(),PROFILE.LastDay())==0)
        return;

    m_inRecalculation=true;
    QDate first=PROFILE.FirstDay();
    QDate date=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=PROFILE.GetMachines(MT_CPAP);

    // Disabling multithreaded save as it appears it's causing problems
    bool cache_sessions=false; //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=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://sourceforge.net/apps/mediawiki/sleepyhead/index.php?title=Glossary"));
    ui->tabWidget->setCurrentWidget(ui->helpTab);
}

void MainWindow::on_actionHelp_Support_SleepyHead_Development_triggered()
{
    QUrl url=QUrl("http://sourceforge.net/apps/mediawiki/sleepyhead/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;
    PROFILE.Save();
    PREF.Save();

    RestartApplication(true);
}

void MainWindow::on_actionChange_Data_Folder_triggered()
{
    PROFILE.Save();
    PREF.Save();
    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());
    }

}