////////////////////////////////////////////////////////////////////////////////
// Installers.cpp
// Screen for installing games via MechVM
// Copyright Bjoern Ganster 
// Developed since 2007
////////////////////////////////////////////////////////////////////////////////

#include "Installers.h"

#include "dialogs.h"
#include "Config.h"
#include "Font.h"
#include "MechVM.h"
#include "MechWarriorInstallers.h"
#include "MW2MechImporter.h"
#include "MechWarrior3ZBD.h"
#include "StartGameScreen.h"

#include "GLButton.h"

#define FILE_NUMBER FILE_NO_INSTALLERS_CPP

// Constructor
Installers::Installers(int _left, int _top, int _width, int _height)
: GLWindowContainer("Installers", Installers_ID, nullptr, _left, _top, _width, _height)
{
}

// Destructor
Installers::~Installers()
{
	// Base class handles destruction of the registered view components ...
}

// Set up dialog
void Installers::setup()
{
	SET_SCOPE;
	int buttonWidth = width -2*margin;
	getMechVm()->createToolbar(this);

	int y = 2*margin+getDefaultButtonHeight();
	label = new GLLabel (this, margin, y, width-2*margin, getDefaultButtonHeight());
	add(label);
	y += margin+getDefaultButtonHeight();

	// Display install widgets
	showCDROMWidgets(y);
	showInstallFromImageButton(y);
	RETURN;
}

////////////////////////////////////////////////////////////////////////////////
// Misc functions

void Installers::displayMsg (const BGCPString& msg)
{
	msg.printLn();
	label->setTitle (msg);
	getMechVm()->renderScene();
}

///////////////////////////////////////////////////////////////////////////////

class GLInstallButton: public GLButton {
public:
	// Constructor
	GLInstallButton (GLWindow* parent, int& y, const char* title, 
		const PathChar* sourcePath, int InstallAlgorithmID)
	: GLButton (parent, margin, y, 30, 30)
	{
		SET_SCOPE;
		height = getDefaultFont()->getCharHeight();
		width  = 40 * getDefaultFont()->getMaxCharWidth();

		//setListener(parent);
		m_sourcePath = BGCPString(sourcePath, CP_SYSTEM);
		m_variantName = title;
		BGCPString newTitle = tr(47);
		newTitle += title;
		newTitle += tr(48);
		newTitle += m_sourcePath;
		setTitle (newTitle);
		m_InstallAlgorithmID = InstallAlgorithmID;
		parent->add (this);
		setListener (dynamic_cast<MessageListener*> (parent));
		userData = installFromDriveButtonID;
		COUT << "Install option: ";
		newTitle.printLn();
		y += margin + getDefaultButtonHeight();

		Config* cfg = getConfig();
		Color backgroundColor = cfg->getViewerBackgroundColor();
		setBackgroundColor(backgroundColor);
		RETURN;
    }

    // Destructor
    ~GLInstallButton() OVERRIDE
    {}

    // Get data
    const PathChar* getSourcePath()
    { return m_sourcePath.getAsPath(); }
    int getInstallAlgorithmID () const
    { return m_InstallAlgorithmID; }
private:
    BGCPString m_sourcePath;
    BGCPString m_variantName;
    int m_InstallAlgorithmID;
};

void Installers::createInstallButton(const PathChar* CDpath, int& y)
{
    int CDID = getCDType (CDpath);
    GLButton * b1 = NULL, * b2 = NULL;
    switch (CDID) {
        case MW2_31stcc_DOS:
            b1 = new GLInstallButton(this, y, "MW2: 31st Century Combat (DOS)", CDpath, CDID);
            break;
        case MW2_31stcc_PE:
            b1 = new GLInstallButton(this, y, "MW2: 31st Century Combat (Pentium Edition)", CDpath, CDID);
            break;
        case MW2_31stcc_Hybrid_DOS_Win:
            b1 = new GLInstallButton(this, y, "MW2: 31st Century Combat (DOS+Win)", CDpath, CDID);
            break;
        case MW2_NetMech:
            b1 = new GLInstallButton(this, y, "MechWarrior 2: 31stCC (NetMech edition, DOS+Win)", CDpath, CDID);
            break;
        case MW2_GBL_DOS:
            b1 = new GLInstallButton(this, y, "MW2: Ghost Bear's Legacy (DOS)", CDpath, CDID);
            break;
        case MW2_Mercs_3DFX:
        case MW2_Mercs_DOS:
            b1 = new GLInstallButton(this, y, "MW2: Mercenaries (DOS+Windows)", CDpath, CDID);
            break;
        case MW2_31stcc_TITAN:
            b1 = new GLInstallButton(this, y, "MW2: 31st Century Combat (Titanium Trilogy)", CDpath, CDID);
            b2 = new GLInstallButton(this, y, "MW2: 31st Century Combat (Battlepack)", CDpath, MW2_31stcc_TT_BP);
            break;
        case MW2_GBL_TITAN:
            b1 = new GLInstallButton(this, y, "MW2: Ghost Bear's Legacy (Titanium Trilogy)", CDpath, CDID);
            b2 = new GLInstallButton(this, y, "MW2: Ghost Bear's Legacy (Battlepack)", CDpath, MW2_GBL_TT_BP);
            break;
        case MW2_Mercs_TITAN:
            b1 = new GLInstallButton(this, y, "MW2: Mercenaries (Titanium Trilogy)", CDpath, CDID);
            b2 = new GLInstallButton(this, y, "MW2: Mercenaries (Battlepack)", CDpath, MW2_Mercs_TT_BP);
            break;
        case MW2_GBL_PE:
            b1 = new GLInstallButton(this, y, "MW2: Ghost Bear's Legacy (Pentium Edition)", CDpath, CDID);
            break;
        case MW2_GBL_Hybrid_DOS_Win:
            b1 = new GLInstallButton(this, y, "MW2: Ghost Bear's Legacy (DOS+Win)", CDpath, CDID);
            break;
        case MW2_31stcc_BP:
            b1 = new GLInstallButton(this, y, "MW2: 31st Century Combat (Battlepack)", CDpath, CDID);
            break;
        case MW2_GBL_BP:
            b1 = new GLInstallButton(this, y, "MW2: Ghost Bear's Legacy (Battlepack)", CDpath, CDID);
            break;
        case MW2_31stcc_3DFX:
            b1 = new GLInstallButton(this, y, "MW2: 31st Century Combat (3DFX)", CDpath, CDID);
            break;
        case MW2_31stcc_Matrox:
            b1 = new GLInstallButton(this, y, "MW2: 31st Century Combat (Matrox Mystique)", CDpath, CDID);
            break;
        case MW2_31stcc_ATI:
            b1 = new GLInstallButton(this, y, "MW2: 31st Century Combat (ATI Edition)", CDpath, CDID);
            break;
        case MW3_CD_eng:
        case MW3_CD_ger:
            b1 = new GLInstallButton(this, y, "MechWarrior 3", CDpath, CDID);
            break;
        case MW3_PM_CD:
            b1 = new GLInstallButton(this, y, "MechWarrior 3 - Pirate's Moon", CDpath, CDID);
            break;
        default:
        {
            BGCPString str = "Nothing to install found in ";
            str += BGCPString(CDpath, CP_SYSTEM);
            str.printLn();
        }
    }
}

///////////////////////////////////////////////////////////////////////////////

#ifdef _WIN32
size_t Installers::showCDROMWidgets(int& y)
{
    size_t count = 0;
    size_t v = 1;
    char driveLetter = 'A';

    // Drives is a bitmask indicating which drives are present
    DWORD drives = GetLogicalDrives();

    while (v < drives) {
        size_t x = v & drives;
        if (x != 0) {
            BGCPString driveName = BGCPString(driveLetter) + BGCPString(":\\");
            int dt = GetDriveTypeW(driveName.getAsPath());
            if (dt == DRIVE_CDROM) {
                BOOL mounted = GetVolumeInformationW(driveName.getAsPath(), 0, 0, 0, 0, 0, 0, 0); 
                if (mounted) {
                    createInstallButton(driveName.getAsPath(), y);
                }
            }
        }
        v += v;
        driveLetter++;
    }
    return count;
}

#else

size_t Installers::showCDROMWidgets(int& y)
{
    // MemoryBlock can't read /proc/mounts, because that files says it has size 0
    size_t count = 0;
    size_t BytesToRead = 10*1024;
    char* buf = new char[BytesToRead];
    FILE* newHandle = fopen ("/proc/mounts", "rb");
    size_t size = fread (buf, 1, BytesToRead, newHandle);
    fclose (newHandle);

    size_t i = 0;
    while (i < size) {
        // Find end of line
        size_t j = i+1;
        while (buf[j] != 0x0a && buf[j] != 0x0d && j < size) {
            ++j;
        }

        BGCPString str((const char*) (buf + i), j - i);
        //printf ("Line starting at %i: %s\n", i, str.getChars());

        if (j-i > 5) {
            if (buf[i]   == '/' 
                &&  buf[i+1] == 'd' 
                &&  buf[i+2] == 'e' 
                &&  buf[i+3] == 'v' 
                &&  buf[i+4] == '/')
            {
                while (buf[i] != ' ' && i < j) {
                    ++i;
                }

                ++i;
                size_t k = i;
                while (buf[k] != ' ' && k < j) {
                    ++k;
                }

                BGCPString mountPoint((const char*) buf + i, k - i);
                //printf("Found potential mount point: \"%s\" ... not?\n", mountPoint.getChars());
                createInstallButton(mountPoint.getAsPath(), y);
            }
        }
        i = j+1;
    }

    return count;
}

#endif

void Installers::showInstallFromImageButton(int& y)
{
	SET_SCOPE;
	auto buttonHeight = getDefaultFont()->getCharHeight();
	y += getDefaultButtonHeight() + margin;
	int buttonWidth  = 40 * getDefaultFont()->getMaxCharWidth();
	GLButton* button = new GLButton(this, margin, y, buttonWidth, getDefaultButtonHeight());
	button->setTitle(tr(64));
	Config* cfg = getConfig();
	Color backgroundColor = cfg->getViewerBackgroundColor();
	button->setBackgroundColor(backgroundColor);
	add(button);
	button->setListener (dynamic_cast<MessageListener*> (this));
	button->userData = installFromCDImageID;
	RETURN;
}

///////////////////////////////////////////////////////////////////////////////

void Installers::installFromCDImage()
{
	SET_SCOPE;
	BGCPString imgFN;
	if (getOpenFileName(imgFN)) {
		FileType fileType = getFileTypeFromExtension(imgFN.getAsPath());
		if (isArchiveType (fileType)) {
			displayMsg ("Analyzing CD\n");
			MWArchive* image = openArchive (imgFN.getAsPath());
			BGCPString path = getDataDir();
			path.addPathSep("temp");
			bgmkdir(path.getAsPath());
			unpackArchive(image, path.getAsPath());
			int cdType = getCDType (path.getAsPath());

			if (cdType != unknownCD) {
				BGCPString str = "Installing ";
				str += getCDName (cdType);
				displayMsg (str);
				install(cdType, path.getAsPath(), imgFN.getAsPath(), this);
				deleteTree(path.getAsPath());
			} else {
				displayMsg("Unrecognized CD type");
			}
		} else {
			displayMsg ("Unrecognized filetype");
		}
	}
	RETURN;
}

///////////////////////////////////////////////////////////////////////////////
// Receive messages

void Installers::receiveMessage (VirtualClass* sender, int /*MessageType*/, VirtualClass* data)
{
    GLWindow* win = dynamic_cast <GLWindow*> (sender);
    if (win != NULL) {
        switch (win->userData) {
            case playGameButtonID:
            {
                clear();
                StartGameScreen* screen = new StartGameScreen(0, 0, width, height);
                screen->setup();
                getMechVm()->setScreen(screen);
                getMechVm()->deleteLater(this);
                break;
            }
            case MechLabButtonID:
                getMechVm()->startMechLab(this);
                break;
            case viewFileButtonID:
                getMechVm()->viewExternalFile();
                getMechVm()->deleteLater(this);
                break;
            case installButtonID:
                // Already active, do nothing ...
                break;
         default:
             if (win->userData == reportCDRomButtonID) {
                 BGCPString ofn;
                 if (getSaveFileName(ofn)) {
                     GLButton* b = dynamic_cast <GLButton*> (win);
                     BGCPString bTitle = b->getTitle();
                     BGCPString str = "Creating contents report for ";
                     str += b->getTitle();
                     displayMsg (str);
                     if (createInstallReport(bTitle.getAsPath(), ofn.getAsPath(), unknownCD, false))
                         displayMsg ("Report created.");
                     else
                         displayMsg ("Aborted");
                 }
                 #ifdef _WIN32
             } else if (win->userData == installNetMechID) {
                 installNetMech(this);
                 #endif
             } else if (win->userData == installFromCDImageID) {
                 installFromCDImage();
             } else if (win->userData >= importFromDriveButtonID) {
                 #ifdef _WIN32
                 BGCPString sourcePath;
                 sourcePath += ((char) (win->userData-importFromDriveButtonID));
                 sourcePath += ":\\";
                 importPathEdit->setText (sourcePath);
                 #endif
             } else if (win->userData == installFromDriveButtonID) {
                 GLInstallButton* ib = dynamic_cast <GLInstallButton*> (win);
                 if (ib != NULL) {
                     clear();
                     getMechVm()->createToolbar(this);

                     // Install selected game type
                     int installType = ib->getInstallAlgorithmID();
                     if (!install (installType, ib->getSourcePath(), NULL, this)) {
                         displayMsg ("Installation failed");
                     }
                 }
             }
        }
    }
}
