/***************************************************************************
                          kswizard.cpp  -  description
                             -------------------
    begin                : Wed 28 Jan 2004
    copyright            : (C) 2004 by Jason Harris
    email                : kstars@30doradus.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "kswizard.h"
#include "ksutils.h"
#include <QFile>
#include <QStackedWidget>
#include <QPixmap>
#include <QLineEdit>
#include <QPushButton>
#include <kns3/downloaddialog.h>
#include <QStandardPaths>
#include <QDesktopServices>

#include "kspaths.h"
#include "kstarsdata.h"
#include "geolocation.h"
#include "widgets/dmsbox.h"

#include "Options.h"

namespace {
    bool hasPrefix(QString str, QString prefix) {
        if( prefix.isEmpty() )
            return true;
        return str.startsWith( prefix, Qt::CaseInsensitive );
    }
}

WizWelcomeUI::WizWelcomeUI( QWidget *parent ) : QFrame( parent ) {
    setupUi( this );
}

WizLocationUI::WizLocationUI( QWidget *parent ) : QFrame( parent ) {
    setupUi( this );
}

WizDownloadUI::WizDownloadUI( QWidget *parent ) : QFrame( parent ) {
    setupUi( this );
}

#ifdef Q_OS_OSX
WizDataUI::WizDataUI( QWidget *parent ) : QFrame( parent ) {
    setupUi( this );
}
WizAstrometryUI::WizAstrometryUI( QWidget *parent ) : QFrame( parent ) {
    setupUi( this );
}
#endif

KSWizard::KSWizard( QWidget *parent ) :
    QDialog( parent )
{
#ifdef Q_OS_OSX
        setWindowFlags(Qt::Dialog|Qt::WindowStaysOnTopHint);
#endif


    wizardStack = new QStackedWidget( this );
    adjustSize();


    setWindowTitle( i18n("Setup Wizard") );

    QVBoxLayout *mainLayout = new QVBoxLayout;
    mainLayout->addWidget(wizardStack);
    setLayout(mainLayout);

    buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel, Qt::Horizontal);
    nextB = new QPushButton(i18n("&Next >"));
    nextB->setDefault(true);
    backB = new QPushButton(i18n("< &Back"));
    backB->setEnabled(false);

    buttonBox->addButton(backB, QDialogButtonBox::ActionRole);
    buttonBox->addButton(nextB, QDialogButtonBox::ActionRole);

    mainLayout->addWidget(buttonBox);


    welcome = new WizWelcomeUI( wizardStack );
#ifdef Q_OS_OSX
    data = new WizDataUI( wizardStack );
    astrometry = new WizAstrometryUI( wizardStack );
#endif
    location = new WizLocationUI( wizardStack );
    WizDownloadUI* download = new WizDownloadUI( wizardStack );


    wizardStack->addWidget( welcome );
#ifdef Q_OS_OSX
    wizardStack->addWidget( data );
    wizardStack->addWidget( astrometry );
#endif
    wizardStack->addWidget( location );
    wizardStack->addWidget( download );
    wizardStack->setCurrentWidget( welcome );

    //Load images into banner frames.
    QPixmap im;
    if( im.load(KSPaths::locate(QStandardPaths::GenericDataLocation, "wzstars.png")) )
        welcome->Banner->setPixmap( im );
    else if( im.load(QDir(QCoreApplication::applicationDirPath()+"/../Resources/data").absolutePath()+"/wzstars.png"))
        welcome->Banner->setPixmap( im );
    if( im.load(KSPaths::locate(QStandardPaths::GenericDataLocation, "wzgeo.png")) )
        location->Banner->setPixmap( im );
    else if( im.load(QDir(QCoreApplication::applicationDirPath()+"/../Resources/data").absolutePath()+"/wzgeo.png"))
        location->Banner->setPixmap( im );
    if( im.load(KSPaths::locate(QStandardPaths::GenericDataLocation, "wzdownload.png")) )
        download->Banner->setPixmap( im );
    else if( im.load(QDir(QCoreApplication::applicationDirPath()+"/../Resources/data").absolutePath()+"/wzdownload.png"))
        download->Banner->setPixmap( im );

    #ifdef Q_OS_OSX
    if( im.load(KSPaths::locate(QStandardPaths::GenericDataLocation, "wzdownload.png")) )
        data->Banner->setPixmap( im );
    else if( im.load(QDir(QCoreApplication::applicationDirPath()+"/../Resources/data").absolutePath()+"/wzdownload.png"))
        data->Banner->setPixmap( im );
    if( im.load(KSPaths::locate(QStandardPaths::GenericDataLocation, "wzdownload.png")) )
        astrometry->Banner->setPixmap( im );
    else if( im.load(QDir(QCoreApplication::applicationDirPath()+"/../Resources/data").absolutePath()+"/wzdownload.png"))
        astrometry->Banner->setPixmap( im );


    data->dataPath->setText(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString(), QStandardPaths::LocateDirectory)+"kstars");
    slotUpdateDataButtons();
    astrometry->astrometryPath->setText(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString(), QStandardPaths::LocateDirectory)+"Astrometry");
    updateAstrometryButtons();

    connect(data->copyKStarsData, SIGNAL(clicked()), this, SLOT(slotOpenOrCopyKStarsDataDirectory()));
    connect(astrometry->astrometryButton, SIGNAL(clicked()), this, SLOT(slotOpenOrCreateAstrometryFolder()));
    connect(data->installGSC, SIGNAL(clicked()), this, SLOT(slotInstallGSC()));
    connect(astrometry->pipInstall, SIGNAL(clicked()), this, SLOT(slotInstallPip()));
    connect(astrometry->pyfitsInstall, SIGNAL(clicked()), this, SLOT(slotInstallPyfits()));
    connect(astrometry->netpbmInstall, SIGNAL(clicked()), this, SLOT(slotInstallNetpbm()));
    connect(this,SIGNAL(accepted()),this,SLOT(slotFinishWizard()));

    install = new QProcess(this);
    QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
    QString path=env.value("PATH","");
    env.insert("PATH","/usr/local/bin:" + path);
    install->setProcessEnvironment(env);
    install->setProcessChannelMode ( QProcess::MergedChannels );
    connect(install, SIGNAL(readyReadStandardOutput()), this, SLOT(slotUpdateText()));
    connect(install, SIGNAL(readyReadStandardError()), this, SLOT(slotUpdateText()));
    connect(install, SIGNAL(finished(int)), this, SLOT(slotInstallerFinished()));

    gscMonitor = new QProgressIndicator(data);
    data->GSCLayout->addWidget(gscMonitor);
    data->downloadProgress->setValue(0);
    data->downloadProgress->setEnabled(false);
    data->downloadProgress->setVisible(false);
    installMonitor = new QProgressIndicator(astrometry);
    astrometry->installersLayout->addWidget(installMonitor);

    astrometry->programOutput->appendPlainText("Available Paths:");
    astrometry->programOutput->appendPlainText(env.value("PATH","No Paths!"));
    astrometry->programOutput->appendPlainText("🔭 Installer Ready!\n");

    #endif

    //connect signals/slots
    connect(nextB, SIGNAL(clicked()), this, SLOT(slotNextPage()));
    connect(backB, SIGNAL(clicked()), this, SLOT(slotPrevPage()));
    connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
    connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));

    connect( location->CityListBox, SIGNAL( itemSelectionChanged () ), this, SLOT( slotChangeCity() ) );
    connect( location->CityFilter, SIGNAL( textChanged( const QString & ) ), this, SLOT( slotFilterCities() ) );
    connect( location->ProvinceFilter, SIGNAL( textChanged( const QString & ) ), this, SLOT( slotFilterCities() ) );
    connect( location->CountryFilter, SIGNAL( textChanged( const QString & ) ), this, SLOT( slotFilterCities() ) );
    connect( download->DownloadButton, SIGNAL( clicked() ), this, SLOT( slotDownload() ) );


    //Initialize Geographic Location page
    if(KStars::Instance())
        initGeoPage();
}

//Do NOT delete members of filteredCityList!  They are not created by KSWizard.
KSWizard::~KSWizard()
{}

void KSWizard::setButtonsEnabled() {
    nextB->setEnabled(wizardStack->currentIndex() < wizardStack->count()-1 );
    backB->setEnabled(wizardStack->currentIndex() > 0 );

     #ifdef Q_OS_OSX
    buttonBox->button(QDialogButtonBox::Ok)->setEnabled(dataDirExists());
    if((wizardStack->currentWidget()==data) &&(!dataDirExists())){
        nextB->setEnabled(false);
    }
    if(wizardStack->currentWidget()==location){
            if(KStars::Instance()){
                if(location->LongBox->isReadOnly()==false){
                    initGeoPage();

                }
            }
    }
       #endif
}

void KSWizard::slotNextPage() {
    wizardStack->setCurrentIndex( wizardStack->currentIndex() + 1 );
    setButtonsEnabled();
}

void KSWizard::slotPrevPage() {
    wizardStack->setCurrentIndex( wizardStack->currentIndex() - 1 );
    setButtonsEnabled();
}

void KSWizard::initGeoPage() {
    KStarsData* data = KStarsData::Instance();
    location->LongBox->setReadOnly( true );
    location->LatBox->setReadOnly( true );

    //Populate the CityListBox
    //flag the ID of the current City
    foreach ( GeoLocation *loc, data->getGeoList() ) {
        location->CityListBox->addItem( loc->fullName() );
        filteredCityList.append( loc );
        if ( loc->fullName() == data->geo()->fullName() ) {
            Geo = loc;
        }
    }

    //Sort alphabetically
    location->CityListBox->sortItems();
    //preset to current city
    location->CityListBox->setCurrentItem(location->CityListBox->findItems(QString(data->geo()->fullName()),
									   Qt::MatchExactly).at(0));
}

void KSWizard::slotChangeCity() {
    if ( location->CityListBox->currentItem() ) {
        for ( int i=0; i < filteredCityList.size(); ++i ) {
            if ( filteredCityList[i]->fullName() == location->CityListBox->currentItem()->text() ) {
                Geo = filteredCityList[i];
                break;
            }
        }
        location->LongBox->showInDegrees( Geo->lng() );
        location->LatBox->showInDegrees( Geo->lat() );
    }
}

void KSWizard::slotFilterCities() {
    location->CityListBox->clear();
    //Do NOT delete members of filteredCityList!
    filteredCityList.clear();

    foreach ( GeoLocation *loc, KStarsData::Instance()->getGeoList() ) {
        if( hasPrefix( loc->translatedName(),     location->CityFilter->text()     ) &&
            hasPrefix( loc->translatedCountry(),  location->CountryFilter->text() ) &&
            hasPrefix( loc->translatedProvince(), location->ProvinceFilter->text()  )
            )
        {
            location->CityListBox->addItem( loc->fullName() );
            filteredCityList.append( loc );
        }
    }
    location->CityListBox->sortItems();

    if ( location->CityListBox->count() > 0 )  // set first item in list as selected
        location->CityListBox->setCurrentItem( location->CityListBox->item(0) );
}

void KSWizard::slotDownload() {
    KNS3::DownloadDialog dlg;
    dlg.exec();
}


void KSWizard::slotFinishWizard(){
    Options::setRunStartupWizard(false);
    if(KStars::Instance()&&geo())
        KStars::Instance()->updateLocationFromWizard(*(geo()));
    delete this;
}


void KSWizard::slotOpenOrCopyKStarsDataDirectory(){
    #ifdef Q_OS_OSX
    QString dataLocation=QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory);
    if(dataLocation.isEmpty()) {
        QString dataSourceLocation=QDir(QCoreApplication::applicationDirPath()+"/../Resources/data").absolutePath();
        if(dataSourceLocation.isEmpty()){ //If there is no default data directory in the app bundle
            KMessageBox::sorry(0, i18n("Error! There was no default data directory found in the app bundle!"));
            return;
        }
        QDir writableDir;
        writableDir.mkdir(KSPaths::writableLocation(QStandardPaths::GenericDataLocation));
        dataLocation=QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory);
        if(dataLocation.isEmpty()){  //If there *still* is not a kstars data directory
            KMessageBox::sorry(0, i18n("Error! There was a problem creating the data directory ~/Library/Application Support/ !"));
            return;
        }
        KSUtils::copyRecursively(dataSourceLocation, dataLocation);
        //This will update the next, ok, and copy kstars dir buttons.
        slotUpdateDataButtons();

        //This will let the program load after the data folder is copied
        hide();
        setModal(false);
        setWindowFlags(Qt::Dialog|Qt::WindowStaysOnTopHint);
        show();
    } else{
        QUrl path = QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory));
        QDesktopServices::openUrl(path);
    }

#endif
}

void KSWizard::slotOpenOrCreateAstrometryFolder()
{
#ifdef Q_OS_OSX
    QString astrometryLocation=QStandardPaths::locate(QStandardPaths::GenericDataLocation, "Astrometry", QStandardPaths::LocateDirectory);
    if(astrometryLocation.isEmpty()) {
        KSUtils::configureDefaultAstrometry();
        updateAstrometryButtons();
    } else{
        QUrl path = QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, "Astrometry", QStandardPaths::LocateDirectory));
        QDesktopServices::openUrl(path);
    }
#endif
}

void KSWizard::slotInstallGSC(){
    #ifdef Q_OS_OSX
    QString location=QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory);
    gscZipPath=location+"/gsc.zip";
    QProcess *downloadGSC=new QProcess();
    downloadGSC->setWorkingDirectory(location);
    connect(downloadGSC, SIGNAL(finished(int)), this, SLOT(slotExtractGSC()));
    connect(downloadGSC, SIGNAL(finished(int)), this, SLOT(downloadGSC.deleteLater()));
    downloadGSC->start("curl", QStringList() << "-L" << "-o" << "gsc.zip" << "http://www.indilib.org/jdownloads/Mac/gsc.zip" );
    data->GSCFeedback->setText("downloading GSC . . .");

    downloadMonitor=new QTimer(this);
    connect(downloadMonitor, SIGNAL(timeout()), this, SLOT(slotCheckDownloadProgress()));
    downloadMonitor->start(1000);
    gscMonitor->startAnimation();
    data->downloadProgress->setVisible(true);
    data->downloadProgress->setEnabled(true);
    data->downloadProgress->setMaximum(240);
    data->downloadProgress->setValue(0);
   #endif
}

void KSWizard::slotExtractGSC(){
#ifdef Q_OS_OSX
    QString location=QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory);
    QProcess *gscExtractor=new QProcess();
    connect(gscExtractor, SIGNAL(finished(int)), this, SLOT(slotGSCInstallerFinished()));
    connect(gscExtractor, SIGNAL(finished(int)), this, SLOT(gscExtractor.deleteLater()));
    gscExtractor->setWorkingDirectory(location);
    gscExtractor->start("unzip", QStringList() << "-ao" << "gsc.zip");
    gscMonitor->startAnimation();
 #endif
}

void KSWizard::slotCheckDownloadProgress(){
#ifdef Q_OS_OSX
    if(QFileInfo(gscZipPath).exists());
        data->downloadProgress->setValue(QFileInfo(gscZipPath).size()/1048576);
#endif
}

void KSWizard::slotGSCInstallerFinished(){
#ifdef Q_OS_OSX
    if(downloadMonitor){
        downloadMonitor->stop();
        delete downloadMonitor;
    }
    data->downloadProgress->setEnabled(false);
    data->downloadProgress->setValue(0);
    data->downloadProgress->setVisible(false);
    gscMonitor->stopAnimation();
    slotUpdateDataButtons();
    if(QFile(gscZipPath).exists())
        QFile(gscZipPath).remove();
#endif
}



void KSWizard::slotInstallPip()
{
#ifdef Q_OS_OSX
    astrometry->programOutput->appendPlainText("🔭 INSTALLING PIP:\n");
    if(!pythonExists()){
        if(brewExists()){
            astrometry->programOutput->appendPlainText("/usr/local/bin/brew install python\n");
             install->start("/usr/local/bin/brew" , QStringList() << "install" << "python");
             installMonitor->startAnimation();
         } else
            KMessageBox::sorry(0,"Python is not installed.  Please install python to /usr/local/bin first, or <a href=http://brew.sh>install homebrew</a> and click again.", "", KMessageBox::AllowLink);
    }else{
        if(!pipExists()){
            if(brewExists()){
                astrometry->programOutput->appendPlainText("/usr/local/bin/brew install python\n");
                 install->start("/usr/local/bin/brew" , QStringList() << "install" << "python");
                 installMonitor->startAnimation();
             } else if(QProcess::execute("type /usr/local/bin/easy_install")==QProcess::NormalExit){
                astrometry->programOutput->appendPlainText("/usr/local/bin/easy_install pip\n");
                install->start("/usr/local/bin/easy_install" , QStringList() << "pip");
                installMonitor->startAnimation();
            } else{
                KMessageBox::sorry(0,"pip failed to install with homebrew and easy_install.  Try <a href=http://brew.sh>installing homebrew</a> and clicking again.", "", KMessageBox::AllowLink);
            }

        }
    }
#endif
}

void KSWizard::slotInstallPyfits()
{
#ifdef Q_OS_OSX
    astrometry->programOutput->appendPlainText("🔭 INSTALLING PYFITS:\n");

    if(!pythonExists()){
        KMessageBox::sorry(0,"/usr/local/bin/python is not installed.  Please install python first.");
    }else if(!pipExists()){
        KMessageBox::sorry(0,"/usr/local/bin/pip is not installed.  Please install pip first.");
    } else{
        astrometry->programOutput->appendPlainText("/usr/local/bin/pip install pyfits\n");
        install->start("/usr/local/bin/pip" , QStringList() << "install" << "pyfits");
        installMonitor->startAnimation();
    }
#endif
}

void KSWizard::slotInstallNetpbm()
{
#ifdef Q_OS_OSX
    astrometry->programOutput->appendPlainText("🔭 INSTALLING NETPBM:\n");

   if(brewExists()){
        install->start("/usr/local/bin/brew" , QStringList() << "install" << "netpbm");
        installMonitor->startAnimation();
    } else{
        //install->start("ruby", QStringList() << "-e" << "'$(curl -fsSL raw.githubusercontent.com/Homebrew/install/master/install)'" << "<" << "/dev/null" << "2>" << "/dev/null");
        //install->waitForFinished();
        //install->start("/usr/local/bin/brew" , QStringList() << "install" << "netpbm");
       KMessageBox::sorry(0,"homebrew is not installed.  Try <a href=http://brew.sh>installing homebrew</a> and clicking again.", "", KMessageBox::AllowLink);
    }
#endif
}


void KSWizard::slotUpdateText()
{
#ifdef Q_OS_OSX
   QByteArray data = install->readAllStandardOutput();
   astrometry->programOutput->appendPlainText(QString(data));
#endif
}

#ifdef Q_OS_OSX
bool KSWizard::dataDirExists(){
    QString dataLocation=QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory);
    return !dataLocation.isEmpty();
}

bool KSWizard::astrometryDirExists(){
    QString astrometryLocation=QStandardPaths::locate(QStandardPaths::GenericDataLocation, "Astrometry", QStandardPaths::LocateDirectory);
    return !astrometryLocation.isEmpty();
}

bool KSWizard::pythonExists(){
    return QProcess::execute("type /usr/local/bin/python")==QProcess::NormalExit;
}
bool KSWizard::GSCExists(){
    QString GSCLocation=QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars/gsc", QStandardPaths::LocateDirectory);
    return !GSCLocation.isEmpty();
}

bool KSWizard::pipExists(){
    return QProcess::execute("type /usr/local/bin/pip")==QProcess::NormalExit;
}

bool KSWizard::pyfitsExists(){
    QProcess testPyfits;
    testPyfits.start("/usr/local/bin/pip list");
    testPyfits.waitForFinished();
    QString listPip(testPyfits.readAllStandardOutput());
    qDebug()<<listPip;
    return listPip.contains("pyfits", Qt::CaseInsensitive);
}

bool KSWizard::netpbmExists(){
    return QProcess::execute("type /usr/local/bin/jpegtopnm")==QProcess::NormalExit;
}

bool KSWizard::brewExists(){
    return QProcess::execute("type /usr/local/bin/brew")==QProcess::NormalExit;
}

void KSWizard::updateAstrometryButtons(){
     astrometry->astrometryFound->setChecked(astrometryDirExists());
     if(astrometryDirExists()) {
         astrometry->astrometryButton->setText("Open Folder");
         astrometry->astrometryFeedback->setText("<html><head/><body><p>To plate solve, you need to put index files in the following folder. See the documentation at this link: <a href=http://astrometry.net/doc/readme.html>Astrometry Readme</a> for details on how to get files.</p></body></html>");
    } else{
        astrometry->astrometryButton->setText("Create Folder");
        astrometry->astrometryFeedback->setText("KStars needs to configure the astrometry.cfg file and create a folder for the index files. Please click the button below to complete this task.");
    }

    if(!pythonExists()){
        astrometry->pipFound->setChecked(false);
        astrometry->pipInstall->setDisabled(false);
        astrometry->pyfitsFound->setChecked(false);
        astrometry->pyfitsInstall->setDisabled(false);
    }
    else{
        bool ifPipExists=pipExists();
        astrometry->pipFound->setChecked(ifPipExists);
        astrometry->pipInstall->setDisabled(ifPipExists);
        if(ifPipExists){
            bool ifPyfitsExists=pyfitsExists();
            astrometry->pyfitsFound->setChecked(ifPyfitsExists);
            astrometry->pyfitsInstall->setDisabled(ifPyfitsExists);
        }
    }

    //Testing a random netpbm command.
    bool ifNetpbmExists=netpbmExists();
    astrometry->netpbmFound->setChecked(ifNetpbmExists);
    astrometry->netpbmInstall->setDisabled(ifNetpbmExists);
}
#endif

void KSWizard::slotUpdateDataButtons(){
#ifdef Q_OS_OSX
    data->dataDirFound->setChecked(dataDirExists());
    if(dataDirExists()) {
        data->copyKStarsData->setText("Open KStars Data Directory");
        data->foundFeedback1->setText("The KStars Data Directory called kstars is located at:");
        data->foundFeedback2->setText("Your data directory was found.  If you have any problems with it, you can always delete this data directory and KStars will give you a new data directory.  You can click this button to open the data directory, just be careful not to delete any important files.");
     } else{
        data->foundFeedback1->setText("The KStars Data Directory called kstars should be located at:");
        data->foundFeedback2->setText("<html><head/><body><p>Your data directory was not found. You can click the button below to copy a default KStars data directory to the correct location, or if you have a KStars directory already some place else, you can exit KStars and copy it to that location yourself.</p></body></html>");
    }
    bool ifGSCExists=GSCExists();
    data->GSCFound->setChecked(ifGSCExists);
    data->installGSC->setDisabled(ifGSCExists||!dataDirExists());
    if(ifGSCExists)
        data->GSCFeedback->setText("GSC was found on your system.  To use it, just take an image in the CCD simulator. To uninstall, just delete the gsc folder from your data directory above.");
    setButtonsEnabled();
#endif
}

void KSWizard::slotInstallerFinished(){
#ifdef Q_OS_OSX
    astrometry->programOutput->appendPlainText("🔭 Installer Finished!\n");
    updateAstrometryButtons();
    installMonitor->stopAnimation();
#endif
}

