#include "specsupport.h"
#include "specsupport.moc"
#include "kdevproject.h"
#include "kdevmakefrontend.h"
#include "distpart_widget.h"

#include <kdebug.h>
#include <kfiledialog.h>
#include <klocale.h>
#include <qfile.h>
#include <qdir.h>
#include <qerrormessage.h>
#include <qregexp.h>
#include <qpushbutton.h>
#include <qvbox.h>
#include <qgroupbox.h>
#include <qtabwidget.h>
#include <qmessagebox.h>

/// \FIXME This is at least the fifth place in the kdevelop code something like this exists
QString QRegExp_escape(const QString& str )
{
#if QT_VERSION >= 0x030100
    return QRegExp::escape(str);
#else
    // this block is copyrighted by Trolltech AS (GPL)
    static const char meta[] = "$()*+.?[\\]^{|}";
    QString quoted = str;
    int i = 0;

    while ( i < (int) quoted.length() ) {
       if ( strchr(meta, quoted[i].latin1()) != 0 )
           quoted.insert( i++, "\\" );
       i++;
    }
    return quoted;
#endif
}

SpecSupport::SpecSupport(DistpartPart * part) : packageBase(part,"Rpm") {

    m_part = part;

    srcPackagePushButton = new QPushButton(i18n("Src Package"),area());
    buildAllPushButton = new QPushButton(i18n("Src/Binary Packages"),area());
    exportSPECPushButton = new QPushButton(i18n("Export SPEC File"),area());
    importSPECPushButton = new QPushButton(i18n("Import SPEC File"),area());



    connect(buildAllPushButton, SIGNAL(clicked()),
            this, SLOT(slotbuildAllPushButtonPressed()));
    connect(exportSPECPushButton, SIGNAL(clicked()),
            this, SLOT(slotexportSPECPushButtonPressed()));
    connect(importSPECPushButton, SIGNAL(clicked()),
            this, SLOT(slotimportSPECPushButtonPressed()));
    connect(srcPackagePushButton, SIGNAL(clicked()),
            this, SLOT(slotsrcPackagePushButtonPressed()));

    parseDotRpmmacros();
}

SpecSupport::~SpecSupport() {
}

//    QPushButton* buildAllPushButton;
void SpecSupport::slotbuildAllPushButtonPressed() {
    QMap<QString,QString>::Iterator it;
    QFile file1(dir + "/" + getAppSource());
    QFile file2(*(map.find("_sourcedir")) + "/" + getAppSource());
    if (!file2.exists()) {
	if (!file1.exists()) {
	    QMessageBox::critical(this,i18n("Error"),i18n("You need to create a source archive first!"));
	    return;
	}
	else
	    m_part->makeFrontend()->queueCommand(dir,"cd " + dir + " && cp " + getAppSource() + " " + *(map.find("_sourcedir")));
    }
    m_part->makeFrontend()->queueCommand(dir,"cd " + (((it = map.find("_specdir")) != map.end()) ? (*it) : dir) + " && rpmbuild -ba " + m_part->project()->projectName() + ".spec");
}

//    QPushButton* exportSPECPushButton;
void SpecSupport::slotexportSPECPushButtonPressed() {
    QMap<QString,QString>::Iterator it;
    QString specname = ((it = map.find("_specdir")) != map.end()) ? (*it) : (m_part->project()->projectDirectory());
    specname += ("/" + m_part->project()->projectName() + ".spec");
    QFile file(specname);

    if(file.open(IO_WriteOnly)) {
        QTextStream stream(&file);

        stream << "# This spec file was generated by KDevelop" << "\n"
        << "# Please report any problem to KDevelop Team <kdevelop-devel@kdevelop.org>" << "\n"
        << "# Thanks to Matthias Saou for his explanations on http://freshrpms.net/docs/fight.html\n\n";

        stream << "Name: " << getAppName() << "\n";
        stream << "Version: " << getAppVersion() << "\n";
        stream << "Release: " << getAppRevision() << "\n";
        stream << "Vendor: " << getAppVendor() << "\n";
        stream << "Copyright: " << getAppLicense() << "\n";
        stream << "Summary: " << getAppSummary() << "\n";
        stream << "Group: " << getAppGroup() << "\n";
        stream << "Packager: " << getAppPackager() << "\n";
        stream << "BuildRoot: " << "%{_tmppath}/%{name}-root" << "\n";
        stream << "Source: " << getAppSource() << "\n";

        stream << "\n";
        stream << "%description\n";
        stream << getAppDescription()<< "\n";

        stream << "\n";
        stream << "%prep\n";
        stream << "%setup\n";
        stream << "CFLAGS=\"$RPM_OPT_FLAGS\" CXXFLAGS=\"$RPM_OPT_FLAGS\" ./configure \\" << "\n";
        stream << "--target=" << getAppArch() << "\n";

        stream << "\n";
        stream << "%build\n";
        stream << "%configure\n";
        stream << "make\n";

        stream << "\n";
        stream << "%install\n";
        stream << "rm -rf %{buildroot}\n";
        stream << "%makeinstall\n";

        stream << "\n";
        stream << "%clean\n";
        stream << "rm -rf %{buildroot}\n";

        stream << "\n";
        stream << "%post -p /sbin/ldconfig\n";

        stream << "%postun -p /sbin/ldconfig\n";

        stream << "%files\n";
        stream << "%defattr(-, root, root)\n";
        stream << "%doc AUTHORS COPYING ChangeLog NEWS README TODO\n";
        stream << "%{_bindir}/*\n";
        stream << "%{_libdir}/*.so.*\n";
        stream << "%{_datadir}/%{name}\n";
        stream << "%{_mandir}/man8/*\n";

        stream << "%changelog\n";
        stream << getAppChangelog() << "\n";


        file.close();
    } else {
        kdDebug() << "TODO : intercept write error in SpecSupport::slotexportSPECPushButtonPressed()";
    }
}

QString SpecSupport::getInfo(QString s, QString motif) {
    QRegExp re(motif + "[ \t]*([^ \t].*[^ \t])[ \t]*");
    if (re.exactMatch(s))
        return re.cap(1);
    return QString::null;
}

//    QPushButton* importSPECPushButton;
void SpecSupport::slotimportSPECPushButtonPressed() {
    QFile file(KFileDialog::getOpenFileName(dir,"*.spec"));
    if(file.open(IO_ReadOnly)) {
        QTextStream stream(&file);

        while (!stream.atEnd()) {
            QString s = stream.readLine();
            QString info;
            if (!(info = getInfo(s,"Name:")).isEmpty())
                setAppName(info);
            else if (!(info = !getInfo(s,"Version:")).isEmpty())
                setAppVersion(info);
            else if (!(info = getInfo(s,"Release:")).isEmpty())
                setAppRevision(info);
            else if (!(info = getInfo(s,"Vendor:")).isEmpty())
                setAppVendor(info);
            else if (!(info = getInfo(s,"Copyright:")).isEmpty())
                setAppLicense(info);
            else if (!(info = getInfo(s,"Summary:")).isEmpty())
                setAppSummary(info);
            else if (!(info = getInfo(s,"Group:")).isEmpty())
                setAppGroup(info);
            else if (!(info = getInfo(s,"Packager:")).isEmpty())
                setAppPackager(info);
            else if (s.startsWith("%description")) {
                QString desc;
                while (!stream.atEnd()) {
                    QString str = stream.readLine();
                    if (str.startsWith("%")) break;
                    else desc += str + "\n";
                }
                setAppDescription(desc);
            }
	    else if (s.startsWith("%changelog")) {
		QString change;
		while (!stream.atEnd()) {
		    QString str = stream.readLine();
		    if (str.startsWith("%")) break;
                    else change += str + "\n";
                }
		setAppChangelog(change);
            }
        }
    }
}

//    QPushButton* srcPackagePushButton;
void SpecSupport::slotsrcPackagePushButtonPressed() {
    QMap<QString,QString>::Iterator it;

    QFile file1(dir + "/" + getAppSource());
    QFile file2(*(map.find("_sourcedir")) + "/" + getAppSource());
    if (!file2.exists()) {
	if (!file1.exists()) {
	    QMessageBox::critical(this,i18n("Error"),i18n("You need to create a source archive first!"));
	    return;
	}
	else
	    m_part->makeFrontend()->queueCommand(dir,"cd " + dir + " && cp " + getAppSource() + " " + *(map.find("_sourcedir")));
    }
    m_part->makeFrontend()->queueCommand(dir,"cd " + (((it = map.find("_specdir")) != map.end()) ? (*it) : dir) + " && rpmbuild -bs " + m_part->project()->projectName() + ".spec");
}

void SpecSupport::parseDotRpmmacros() {
    QFile dotfile(QDir::homeDirPath() + "/.rpmmacros");

    if (!dotfile.open(IO_ReadOnly)) {
//        QErrorMessage * msg = new QErrorMessage(this);
//        msg->message("It seems you don't have a ~/.rpmmacros\nYou may experience problems building packages.\n");
//        msg->exec();
        return;
    }
    QTextStream stream(&dotfile);

    // Perhaps will it appear as a necessity to parse the global rpm config file?

    // Pre defined macros :
    map.insert("name",getAppName());

    // .rpmmacros parsing :
    while (!stream.atEnd()) {
        QString s = stream.readLine();
        QRegExp re("%([^ \t]*)[ \t][ \t]*([^\t]*)$");
        if(re.exactMatch(s)) {
            QRegExp subst("%\\{([^%]*)\\}");
            QString value = re.cap(2).stripWhiteSpace();

            while(subst.search(value) != -1) {			
                value.replace(QRegExp("%\\{"+ QRegExp_escape( subst.cap(1) ) +"\\}"),*map.find(subst.cap(1)));
            }
            map.insert(re.cap(1),value);
        }
    }
    dotfile.close();

    // create directories if necessary :
    createRpmDirectoryFromMacro("_topdir");
    createRpmDirectoryFromMacro("_tmppath");
    createRpmDirectoryFromMacro("_builddir");
    createRpmDirectoryFromMacro("_rpmdir");
    createRpmDirectoryFromMacro("_sourcedir");
    createRpmDirectoryFromMacro("_specdir");
    createRpmDirectoryFromMacro("_srcrpmdir");
}

bool SpecSupport::createRpmDirectoryFromMacro(const QString & name) {
    QMap<QString,QString>::Iterator it;
    if((it = map.find(name)) != map.end()) {
        QDir dir(*it);
        if (!dir.exists()) return dir.mkdir(*it);
    }
    return false;
}
