/* -*- mode: c++; tab-width: 4 -*- */

/* $Id: launcher.cpp,v 1.6 2003/03/07 17:59:37 kaoru-k Exp $ */

/*
 * Copyright (c) 2003 AGAWA Koji <kaoru-k@self-core.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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/**
 * @file launcher.cpp
 * @brief Launcher for Emulators
 *
 * 
 *
 * @author AGAWA Koji
 * @date $Date: 2003/03/07 17:59:37 $
 * @version $Revision: 1.6 $
 */

#include "launcher.h"
#include "unzip.h"
#include "LnkWrapper.h"

#include <cstdlib>
#include <qlayout.h>
#include <qlabel.h>
#include <qstring.h>
#include <qpushbutton.h>
#include <qdir.h>
#include <qtextstream.h>
#include <qfont.h>
#include <qpe/qpemenubar.h>
#include <qpopupmenu.h>
#include <qheader.h>
#include <qpe/qpeapplication.h>
#include <qpe/global.h>
#include <qpe/config.h>
#include <qpe/qcopenvelope_qws.h>
#include <qpe/resource.h>
#include <qpe/menubutton.h>
#include <qmessagebox.h>
#include "setting.h"
#include "settingsdialog.h"
#include <sys/wait.h>
#include <qdialog.h>
#include <qmultilineedit.h>
#include <qevent.h>
#include <qtextcodec.h>

// Shinichi Yamashita[OGONIGO.COM] Add
#include <qfileinfo.h>

RomFileItem::RomFileItem(QListView *parent, const QFileInfo &fi)
	: QListViewItem(parent),
	  fileInfo_(fi)
{
	if (fi.isDir()) {
		setText(0, fi.fileName() + "/");
    } else {
		setText(0, fi.fileName());
    }
}

RomFileItem::System
RomFileItem::getSystemFromFilename(const QString &filename, bool isZip)
{
    QString lowerFilename = filename.lower();

/*    if (lowerFilename.right(3) == ".gb" ||
        lowerFilename.right(4) == ".gbc")
        return GB;
    else if (lowerFilename.right(4) == ".nes" ||
             lowerFilename.right(4) == ".fds")
        return NES;
    else if (lowerFilename.right(4) == ".pce")	// Shinichi Yamashita[OGONIGO.COM] Add
        return PCE;
    else
*/
	if (!isZip && lowerFilename.right(4) == ".zip")
          return getSystemFromZipFile(filename);

	// Shinichi Yamashita[OGONIGO.COM] Add Start
	QFileInfo qfi(filename);
	QString fileExp	= (qfi.extension(false)).lower();

	QString shName = globalSetting->shPath;
	if(shName.right(1) != "/"){ shName += "/"; }
	shName += "zemufeex_" + fileExp + ".sh";

	qfi.setFile(shName);
	if(qfi.exists()){
		return EX;
	}
	// Shinichi Yamashita[OGONIGO.COM] Add End

    return UNKNOWN;
}

RomFileItem::System RomFileItem::getSystemFromZipFile(const QString &abspath)
{
    unzFile fp;
    char filename[512];
    System sys = UNKNOWN;

    fp = unzOpen(abspath.utf8());
    if (!fp)
        return UNKNOWN;

    for (int status = unzGoToFirstFile(fp);
        status == UNZ_OK;
        status = unzGoToNextFile(fp)) {
        unzGetCurrentFileInfo(fp, NULL, filename, sizeof(filename) - 1,
                              NULL, 0, NULL, 0);
        QString f(filename);
		// Shinichi Yamashita[OGONIGO.COM] Edit
        // sys = getSystemFromFilename(f.lower(), true);
		sys = getSystemFromFilename(f, true);
       if (sys != UNKNOWN)
            break;
    }

    unzClose(fp);

    return sys;

}


// Shinichi Yamashita[OGONIGO.COM] Add Start
QString RomFileItem::getFileExpFromZipFile(const QString &abspath) {
    unzFile fp;
    char filename[512];
	QString fileExp, shName;

    fp = unzOpen(abspath.utf8());
    if (!fp)
        return "smc";

    for (int status = unzGoToFirstFile(fp);
        status == UNZ_OK;
        status = unzGoToNextFile(fp)) {
        unzGetCurrentFileInfo(fp, NULL, filename, sizeof(filename) - 1,
                              NULL, 0, NULL, 0);
        QString f(filename);
		QFileInfo qfi(filename);
		fileExp	= (qfi.extension(false)).lower();
		shName = globalSetting->shPath;
		if(shName.right(1) != "/"){ shName += "/"; }
		shName += "zemufeex_" + fileExp + ".sh";
		qfi.setFile(shName);
		if(qfi.exists()){
			break;
		}
    }

    unzClose(fp);

    return fileExp;
}
// Shinichi Yamashita[OGONIGO.COM] Add End

// ================================================================
RomFileView::RomFileView(const QString &dir, QWidget *parent,
						 const char *name)
	: QListView(parent, name)
{
//     addColumn(tr("Type"));
	addColumn(tr("Filename"));

    setColumnWidthMode(0, Maximum);
//     setColumnWidth(0, QWidget::width() - columnWidth(1));

// 	header()->hide();
	setAllColumnsShowFocus(TRUE);

	generateDir(dir);

	connect(this, SIGNAL(clicked(QListViewItem *)),
			this, SLOT(itemClicked(QListViewItem *)));
	connect(this, SIGNAL(doubleClicked(QListViewItem *)),
			this, SLOT(itemDoubleClicked(QListViewItem *)));
	connect(this, SIGNAL(returnPressed(QListViewItem *)),
			this, SLOT(itemDoubleClicked(QListViewItem *)));
}

// ---------------- public slots
void RomFileView::setDir(const QString &dir)
{
	if (dir != currentDir_)
		generateDir(dir);
}

void RomFileView::updateDir()
{
	generateDir(currentDir_);
}

void RomFileView::parentDir()
{
	setDir(currentDir_ + "./..");
}

// ---------------- protected methos
void RomFileView::generateDir(const QString &dir)
{
	QDir d(dir);

	if (!d.exists())
		return;

	if (d.exists() && !d.isReadable())
		return;

	currentDir_ = d.absPath();

	// generate dir info
	d.setFilter(QDir::Dirs | QDir::Files | QDir::Readable);
	d.setSorting(QDir::Name | QDir::IgnoreCase);// | QDir::Reversed);
    d.setMatchAllDirs(true);
	// Shinichi Yamashita[OGONIGO.COM] Edit
	QString fileExp = globalSetting->fileExp;
	if(fileExp != "") {fileExp = ";" + fileExp;}
	const QFileInfoList *list = d.entryInfoList("*.gbc;*.gb;*.GB;*.GBC;*.nes;*.NES;*.fds;*.FDS;*.smc;*.SMC;*.pce;*.PCE;*.gba;*.GBA;*.smd;*.SMD;*.zip;*.ZIP" + fileExp);
	QFileInfoListIterator it(*list);
	QFileInfo *fi;

	clear();
	while ((fi = it.current())) {
		if ((fi->fileName() == ".") || (fi->fileName() == "..")) {
			++it;
			continue;
		}
		(void) new RomFileItem(this, *fi);
		++it;
	}

 	setSelected(firstChild(), true);

	emit dirChanged(currentDir_);
}

// -------------------------------- protected slots
void RomFileView::itemClicked(QListViewItem *i)
{
	RomFileItem *t = static_cast<RomFileItem *>(i);

	if (t == NULL)
		return;
	if (t->isDir())
		setDir(t->getFilePath());
}

void RomFileView::itemDoubleClicked(QListViewItem *i)
{
	RomFileItem *t = static_cast<RomFileItem *>(i);

	if (t == NULL)
		return;
	if (t->isDir())
		setDir(t->getFilePath());
	else
		emit romFileActivated(t->getFileInfo());
}

// Shinichi Yamashita[OGONIGO.COM] Add
void RomFileView::keyPressEvent(QKeyEvent *key)
{
    shiftFlag	= (key->state() & ShiftButton);
    altFlag		= (key->state() & AltButton);
    ctrlFlag	= (key->state() & ControlButton);

	if(key->key() == Key_Space) {
		spaceFlag = true;
		itemDoubleClicked( currentItem() );
		return;
	} else {spaceFlag = false;}

	return QListView::keyPressEvent(key);
}

// ================================================================
// static char * const Launcher::CONFIG_GROUP;

Launcher::Launcher(QWidget *parent, const char *name)
    : QMainWindow(parent, name)
{
    setCaption(CAPTION);

    globalSetting = new Setting();
    globalSetting->readAll();

    initGUI();
}

Launcher::~Launcher()
{
    globalSetting->lastDir = romFileView_->getDir();
    globalSetting->recentFiles = recentFilesButton_->getFiles();
    globalSetting->userDirs = dirSelector_->getUserDirs();

    globalSetting->writeAll();
    delete globalSetting;
/*
    QCopEnvelope qcop("QPE/KeyHelper", "repeater(int)");
    qcop << 1;
*/
	// cleanup
	QDir dir(CMDLINE_DIR);
	dir.remove(GB_CMDLINE_FILE);
	dir.remove(NES_CMDLINE_FILE);
	// Shinichi Yamashita[OGONIGO.COM] Add
	dir.remove(PCE_CMDLINE_FILE);
	dir.remove(EX_CMDLINE_FILE);

}

void Launcher::initGUI()
{
    QWidget *cw = new QWidget(this, "cw");
    setCentralWidget(cw);

// 	QFont defaultFont;
// 	defaultFont.setPixelSize(12);

    QVBoxLayout *topLayout = new QVBoxLayout(cw);
//     topLayout->setSpacing(2);
//     topLayout->setMargin(2);

	QPEMenuBar *menubar = new QPEMenuBar(cw);
	topLayout->setMenuBar(menubar);

	QHBoxLayout *filerLayout = new QHBoxLayout(topLayout);

	// -------- フォルダ選択ボックス
	dirSelector_ = new DirSelector(globalSetting->userDirs, cw);
	filerLayout->addWidget(dirSelector_, 1);
	dirSelector_->setDir(globalSetting->lastDir);
	connect(dirSelector_, SIGNAL(userDirStatusChanged(bool)),
			this, SLOT(setFolderMenuEnabled(bool)));

	// -------- RecentFiles ボタン
	recentFilesButton_ = new RecentFilesButton(globalSetting->recentFiles,
                                               10, cw);
	filerLayout->addWidget(recentFilesButton_);
	recentFilesButton_->setPixmap(Resource::loadPixmap("recent"));
	recentFilesButton_->setAutoDefault(FALSE);
 	connect(recentFilesButton_, SIGNAL(fileActivated(const QFileInfo &)),
			this, SLOT(launchRom(const QFileInfo &)));

	// -------- 「一つ上へ」ボタン
	QPushButton *updir = new QPushButton(cw);
	updir->setPixmap(Resource::loadPixmap("folderup"));
	updir->setAutoDefault(FALSE);
	updir->setAccel(Key_Backspace);
	filerLayout->addWidget(updir);

	// --------
	romFileView_ = new RomFileView(globalSetting->lastDir, cw);
	topLayout->addWidget(romFileView_, 1);
	romFileView_->setFocus();
	connect(updir, SIGNAL(clicked()), romFileView_, SLOT(parentDir()));
	connect(romFileView_, SIGNAL(romFileActivated(const QFileInfo &)),
			recentFilesButton_, SLOT(addFile(const QFileInfo &)));
	connect(romFileView_, SIGNAL(romFileActivated(const QFileInfo &)),
			this, SLOT(launchRom(const QFileInfo &)));
	connect(dirSelector_, SIGNAL(dirChanged(const QString &)),
			romFileView_, SLOT(setDir(const QString &)));
	connect(romFileView_, SIGNAL(dirChanged(const QString &)),
			dirSelector_, SLOT(setDir(const QString &)));

	// menu
	filePopup_ = new QPopupMenu(cw);
	menuIdAddToFolderList_ =
		filePopup_->insertItem(tr("&Add to Folder List"),
							   dirSelector_, SLOT(addCurrentToUserDir()));
	menuIdRemoveFolderFromList_ =
		filePopup_->insertItem(tr("&Remove from Folder List"),
							   dirSelector_, SLOT(removeCurrentFromUserDir()));
	filePopup_->insertSeparator();
 	filePopup_->insertItem(tr("About"), this, SLOT(about()));
	filePopup_->insertItem(tr("E&xit"), qApp, SLOT(quit()));

    QPopupMenu *settingsPopup_ = new QPopupMenu(cw);
    settingsPopup_->insertItem(tr("&Frontend..."),
                               this, SLOT(settingFrontend()));

// 	QPopupMenu *help = new QPopupMenu(cw);
// 	help->insertItem(tr("&Help"), qApp, SLOT(quit()));
// 	help->insertItem(tr("&About"), this, SLOT(about()));

	menubar->insertItem(tr("&File"), filePopup_);
    menubar->insertItem(tr("&Settings"), settingsPopup_);
}

void Launcher::about()
{
	QMessageBox::about(this, CAPTION,
					   QString(CAPTION) + " version " + VERSION + "\n"
					   + "Copyright (C) 2003 AGAWA Koji <kaoru-k@self-core.org>\n");
}

void Launcher::setFolderMenuEnabled(bool isContain)
{
    isContain = 0;
#if 0
	if (isContain) {
		filePopup_->setItemEnabled(menuIdAddToFolderList_, false);
		filePopup_->setItemEnabled(menuIdRemoveFolderFromList_, true);
	} else {
		filePopup_->setItemEnabled(menuIdAddToFolderList_, true);
		filePopup_->setItemEnabled(menuIdRemoveFolderFromList_, false);
	}
#endif
}

void Launcher::settingFrontend()
{
    SettingsDialog dialog(globalSetting);

    dialog.showMaximized();
    dialog.exec();
}

void Launcher::launchRom(const QFileInfo &fi)
{
    RomFileItem::System system =
        RomFileItem::getSystemFromFilename(fi.absFilePath());
    QString executable;
    QString cmdline;

    switch (system) {
    case RomFileItem::GB:
//        cmdline = QString(CMDLINE_DIR) + "/" + GB_CMDLINE_FILE;
//        executable = GB_EXECUTABLE_FILENAME;
//        break;
    case RomFileItem::NES:
//        cmdline = QString(CMDLINE_DIR) + "/" + NES_CMDLINE_FILE;
//        executable = NES_EXECUTABLE_FILENAME;
//        break;
    case RomFileItem::PCE:	// Shinichi Yamashita[OGONIGO.COM] Add
//        cmdline = QString(CMDLINE_DIR) + "/" + PCE_CMDLINE_FILE;
//        executable = PCE_EXECUTABLE_FILENAME;
//        break;
    case RomFileItem::EX:	// Shinichi Yamashita[OGONIGO.COM] Add
        cmdline = QString(CMDLINE_DIR) + "/" + EX_CMDLINE_FILE;
        executable = EX_EXECUTABLE_FILENAME;
        break;
    default:
        return;
    }
/*
    QCopEnvelope qcop("QPE/KeyHelper", "repeater(int)");
	// Shinichi Yamashita[OGONIGO.COM] Edit
    //qcop << 0;
	qcop << 2;
*/

    QFile f(cmdline);
		// Shinichi Yamashita[OGONIGO.COM] Edit Start
    if (f.open(IO_WriteOnly)) {
	  QTextStream t(&f);
	  t.setEncoding(QTextStream::UnicodeUTF8);
	  if (false){				// system != RomFileItem::EX) {
		t << '"' << fi.filePath() << '"';
		f.close();
		if (globalSetting->isUseQtopiaLaunchMethod) {
		  Global::execute(executable);
		} else {
		  std::system(executable);
		}
	  } else {
		QFileInfo qfi(fi.absFilePath());
		QString fileName	= qfi.fileName();
		QString fileExp		= (qfi.extension(false)).lower();
		QString fileDir		= qfi.dirPath(true);
		QString shName		= globalSetting->shPath;
		
		if(shName.right(1) != "/"){ shName += "/"; }
		if(fileExp == "zip") { fileExp = (RomFileItem::getFileExpFromZipFile(fi.absFilePath())).lower(); }
		
		if(romFileView_->shiftFlag){
		  shName += "zemufeex_" + fileExp + "_s.sh";
		} else if(romFileView_->altFlag){
		  shName += "zemufeex_" + fileExp + "_a.sh";
		} else if(romFileView_->ctrlFlag){
		  shName += "zemufeex_" + fileExp + "_c.sh";
		} else {
		  shName += "zemufeex_" + fileExp + ".sh";
		}
		QString temp = shName + "\t" + fileDir + "\t" + fileName;
		t << temp;
		f.close();
		
		QFile sf(shName);
		if(sf.exists()){
		  sf.close();
		  if(romFileView_->spaceFlag){
			directRun();
		  } else {
			if (globalSetting->isUseQtopiaLaunchMethod) {
			  Global::execute(executable);
			} else {
			  std::system(executable);
			}
		  }
		} else {sf.close();}
	  }
	}
        // Shinichi Yamashita[OGONIGO.COM] Edit End
}

void Launcher::directRun(){

	QString lfile = CMDLINE_DIR;
	lfile += "/";
	lfile += EX_CMDLINE_FILE;
	QFile f(lfile);
	bool ok = false;
	if(f.exists()){
		ok = f.open(IO_ReadOnly);
	}
	if(ok) {
		QTextStream stream(&f);
		stream.setEncoding(QTextStream::UnicodeUTF8);
		f.flush();
		QString arg = stream.read();
		f.close();

		//arg = "@exec\t" + arg;
		QTextCodec *codec = QTextCodec::codecForName( "UTF8" );
		arg = codec->fromUnicode(arg);
		QStringList args = QStringList::split("\t", arg, true);
		LnkWrapper lnk(args);
		if(lnk.isValid()){
			lnk.instance().execute();
		}
	}
}

/*

必要な機能

 - GoodGB -> JPTools
 - GoodGB Attribute

filelist
foldernew
folderup
recent
Save
UnknownDocument
MainDeviceS
SDDeviceS
CFDeviceS
SettingsIcon
slfolder
sound-on
sound-off
*/
