/*****************************************************************************
** $Source: /cvsroot/bluemsx/blueMSX/Src/Emulator/FileHistory.c,v $
**
** $Revision: 1.39 $
**
** $Date: 2008/10/26 19:48:18 $
**
** More info: http://www.bluemsx.com
**
** Copyright (C) 2003-2006 Daniel Vik
**
** 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., 675 Mass Ave, Cambridge, MA 02139, USA.
**
******************************************************************************
*/
#define USE_ARCH_GLOB
#include "FileHistory.h"
#include "Properties.h"
#include "ziphelper.h"
#include "RomLoader.h"
#include "MsxTypes.h"
#include "ArchNotifications.h"
#include "Disk.h"
#include "AppConfig.h"
#ifdef USE_ARCH_GLOB
#include "ArchGlob.h"
#include "ArchFile.h"
#endif
#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>

static char extendedName[PROP_MAX_CARTS][256];
static char extendedDiskName[PROP_MAX_DISKS][256];
static char extendedCasName[PROP_MAX_TAPES][256];

const char* stripPathExt(const char* filename) {
    static char buffer[128];

    strcpy(buffer, stripPath(filename));

    if (buffer[strlen(buffer) - 4] == '.') {
        buffer[strlen(buffer) - 4] = 0;
    }

    return buffer;
}

const char* stripPath(const char* filename) {
    const char* ptr = filename + strlen(filename) - 1;

    while (--ptr >= filename) {
        if (*ptr == '/' || *ptr == '\\') {
            return ptr + 1;
        }
    }

    return filename;
}

#ifndef NO_FILE_HISTORY
void updateFileHistory(char* history, RomType* historyType, char* filename, RomType romType) {
    char fname[PROP_MAXPATH];
    int i = 0;

    if (appConfigGetInt("filehistory", 1) == 0) {
        return;
    }

    if (strcmp(filename, CARTNAME_GAMEREADER) == 0) {
        return;
    }

    strcpy(fname, filename);

    for (i = 0; i < MAX_HISTORY - 1; i++) {
        if (*(history + PROP_MAXPATH * i) == 0 || 0 == strcmp(history + PROP_MAXPATH * i, fname)) {
            break;
        }
    }
    while (i > 0) {
        strcpy(history + PROP_MAXPATH * i, history + PROP_MAXPATH * (i - 1));
        if (historyType) historyType[i] = historyType[i - 1];
        i--;
    }
    strcpy(history, fname);
    if (historyType) historyType[0] = romType;
}

void verifyFileHistory(char* history, RomType* historyType) {
    int i, j;

    if (appConfigGetInt("filehistory", 1) == 0) {
        return;
    }

    for (i = 0; i < MAX_HISTORY; i++) {
        char *fname = history + PROP_MAXPATH * i;
        if (fname[0] != '\0' &&
            strcmp(fname, CARTNAME_SNATCHER)    &&
            strcmp(fname, CARTNAME_SDSNATCHER)  &&
            strcmp(fname, CARTNAME_SCCMIRRORED) &&
            strcmp(fname, CARTNAME_SCCEXPANDED) &&
            strcmp(fname, CARTNAME_SCC)         &&
            strcmp(fname, CARTNAME_SCCPLUS)     &&
            strcmp(fname, CARTNAME_JOYREXPSG)   &&
            strcmp(fname, CARTNAME_FMPAC)       &&
            strcmp(fname, CARTNAME_PAC)         &&
            strcmp(fname, CARTNAME_GAMEREADER)  &&
            strcmp(fname, CARTNAME_SUNRISEIDE)  &&
            strcmp(fname, CARTNAME_BEERIDE)     &&
            strcmp(fname, CARTNAME_GIDE)        &&
            strcmp(fname, CARTNAME_NMS1210)     &&
            strcmp(fname, CARTNAME_GOUDASCSI)   &&
            strcmp(fname, CARTNAME_SONYHBI55)   &&
            strcmp(fname, CARTNAME_EXTRAM16KB)  && 
            strcmp(fname, CARTNAME_EXTRAM32KB)  && 
            strcmp(fname, CARTNAME_EXTRAM48KB)  && 
            strcmp(fname, CARTNAME_EXTRAM64KB)  && 
            strcmp(fname, CARTNAME_EXTRAM512KB) &&
            strcmp(fname, CARTNAME_EXTRAM1MB)   &&
            strcmp(fname, CARTNAME_EXTRAM2MB)   &&
            strcmp(fname, CARTNAME_EXTRAM4MB)   &&
            strcmp(fname, CARTNAME_MEGARAM128)  &&
            strcmp(fname, CARTNAME_MEGARAM256)  &&
            strcmp(fname, CARTNAME_MEGARAM512)  &&
            strcmp(fname, CARTNAME_MEGARAM768)  &&
            strcmp(fname, CARTNAME_MEGARAM2M)   &&
            strcmp(fname, CARTNAME_MEGASCSI128) &&
            strcmp(fname, CARTNAME_MEGASCSI256) &&
            strcmp(fname, CARTNAME_MEGASCSI512) &&
            strcmp(fname, CARTNAME_MEGASCSI1MB) &&
            strcmp(fname, CARTNAME_NOWINDDOS1)  &&
            strcmp(fname, CARTNAME_NOWINDDOS2)  &&
            strcmp(fname, CARTNAME_ESERAM128)   &&
            strcmp(fname, CARTNAME_ESERAM256)   &&
            strcmp(fname, CARTNAME_ESERAM512)   &&
            strcmp(fname, CARTNAME_ESERAM1MB)   &&
            strcmp(fname, CARTNAME_MEGAFLSHSCC) &&
            strcmp(fname, CARTNAME_MEGAFLSHSCCPLUS) &&
            strcmp(fname, CARTNAME_WAVESCSI128) &&
            strcmp(fname, CARTNAME_WAVESCSI256) &&
            strcmp(fname, CARTNAME_WAVESCSI512) &&
            strcmp(fname, CARTNAME_WAVESCSI1MB) &&
            strcmp(fname, CARTNAME_ESESCC128)   &&
            strcmp(fname, CARTNAME_ESESCC256)   &&
            strcmp(fname, CARTNAME_ESESCC512))
        {
            struct stat s;
            int rv = archFileExists(fname);
            if (rv) {
                rv = stat(fname, &s) == 0;
            }
            if (rv && (s.st_mode & S_IFDIR)) {
            }
            else {
                if (!archFileExists(fname)) {
                    if (i == MAX_HISTORY - 1) {
                        *(history + PROP_MAXPATH * i) = 0;
                    }
                    else {
                        for (j = i + 1; j < MAX_HISTORY; j++) {
                            strcpy(history + PROP_MAXPATH * (j - 1), history + PROP_MAXPATH * j);
                            if (historyType) historyType[j - 1] = historyType[j];
                            *(history + PROP_MAXPATH * j) = 0;
                        }
                        i--;
                    }
                }
            }
        }
    }
}
#endif

int fileExist(char* fileName, char* zipFile) {
    if (fileName == NULL || *fileName == 0) {
        return 0;
    }

    if (zipFile == NULL || *zipFile == 0) {
        return archFileExists(fileName);
        return 0;
    }
    else {
        if (archFileExists(zipFile)) {
            if( zipFileExists(zipFile, fileName) ) {
                return 1;
            }
        }
        return 0;
    }

    return 0;
}

char* fileGetNext(char* filename, char* zipFile) {
    static char name[512];
    static int pos = -1;
    int c;
    int j;

    strcpy(name, filename);

    pos = strlen(name) - 5;

    if (pos < 0) {
        return name;
    }

    while (pos >= 0) {
        c = name[pos];

        if (c >= '0' && c <= '9') {
            if (c < '9') {
                name[pos] = c + 1;
                if (fileExist(name, zipFile)) {
                    return name;
                }
            }

            for (j = '0'; j < c; j++) {
                name[pos] = j;
                if (fileExist(name, zipFile)) {
                    return name;
                }
            }
            name[pos] = c;
        }
        pos--;
    }

    pos = strlen(name) - 5;
    c = name[pos];

    if (c >= 'A' && c <= 'Z') {
        if (c < 'Z') {
            name[pos] = c + 1;
            if (fileExist(name, zipFile)) {
                pos = -1;
                return name;
            }
        }

        for (j = 'A'; j <= c; j++) {
            name[pos] = j;
            if (fileExist(name, zipFile)) {
                pos = -1;
                return name;
            }
        }
    }

    if (c >= 'a' && c <= 'z') {
        if (c < 'z') {
            name[pos] = c + 1;
            if (fileExist(name, zipFile)) {
                pos = -1;
                return name;
            }
        }

        for (j = 'a'; j <= c; j++) {
            name[pos] = j;
            if (fileExist(name, zipFile)) {
                pos = -1;
                return name;
            }
        }
    }

    return name;
}

void updateExtendedRomName(int drive, char* filename, char* zipFile) {
    int size;
    char* buf = romLoad(filename, zipFile[0] ? zipFile : NULL, &size);

    if (buf != NULL) {
        strcpy(extendedName[drive], mediaDbGetPrettyString(mediaDbLookupRom(buf, size)));
        free(buf);
        if (extendedName[drive][0] == 0) {
            strcpy(extendedName[drive], stripPathExt(zipFile[0] ? zipFile : filename));
        }
    }
}

void updateExtendedDiskName(int drive, char* filename, char* zipFile) {
    int size;
    char* buf;
    char* name;

    extendedDiskName[drive][0] = 0;

#ifndef WII
    if (drive < MAX_FDC_COUNT) {
        buf = romLoad(filename, zipFile[0] ? zipFile : NULL, &size);
        if (buf != NULL) {
            strcpy(extendedDiskName[drive], mediaDbGetPrettyString(mediaDbLookupDisk(buf, size)));
            free(buf);
            if (extendedDiskName[drive][0] == 0) {
                strcpy(extendedDiskName[drive], stripPathExt(zipFile[0] ? zipFile : filename));
            }
        }
    } else {
#else
    {
#endif
        name = zipFile[0] ? zipFile : filename;
        if ((name != NULL) && name[0]) {
            archFileExists(name);
            strcpy(extendedDiskName[drive], stripPathExt(name));
        }
    }
/*
    int size;
    char* buf = romLoad(filename, zipFile[0] ? zipFile : NULL, &size);

    extendedDiskName[drive][0] = 0;
    if (buf != NULL) {
        strcpy(extendedDiskName[drive], mediaDbGetPrettyString(mediaDbLookupDisk(buf, size)));
        free(buf);
        if (extendedDiskName[drive][0] == 0) {
            strcpy(extendedDiskName[drive], stripPathExt(zipFile[0] ? zipFile : filename));
        }
    }
*/
}

void updateExtendedCasName(int drive, char* filename, char* zipFile) {
    int size;
    char* buf = romLoad(filename, zipFile[0] ? zipFile : NULL, &size);

    extendedCasName[drive][0] = 0;
    if (buf != NULL) {
        strcpy(extendedCasName[drive], mediaDbGetPrettyString(mediaDbLookupCas(buf, size)));
        free(buf);
        if (extendedCasName[drive][0] == 0) {
            strcpy(extendedCasName[drive], stripPathExt(zipFile[0] ? zipFile : filename));
        }
    }
}

void setExtendedRomName(int drive, const char* name) {
    strcpy(extendedName[drive], name);
}

int createSaveFileBaseName(char* fileBase,Properties* properties, int useExtendedName)
{
    int done = 0;
    int i;
    fileBase[0]=0;

    for (i = 0; !done && i < PROP_MAX_CARTS; i++) {
        if (properties->media.carts[i].fileName[0]) {
            if (useExtendedName && extendedName[i][0]) {
                strcpy(fileBase, extendedName[i]);
            }
            else if (*properties->media.carts[i].fileNameInZip) {
                strcpy(fileBase, stripPathExt(properties->media.carts[i].fileNameInZip));
            }
            else {
                strcpy(fileBase, stripPathExt(properties->media.carts[i].fileName));
            }

            if (strcmp(properties->media.carts[i].fileName, CARTNAME_SNATCHER)     &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_SDSNATCHER)   &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_SCCMIRRORED)  &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_SCCEXPANDED)  &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_SCC)          &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_SCCPLUS)      &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_JOYREXPSG)    &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_FMPAC)        &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_PAC)          &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_GAMEREADER)   &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_SUNRISEIDE)   &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_BEERIDE)      &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_GIDE)         &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_NMS1210)      &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_GOUDASCSI)    &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_SONYHBI55)    &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_EXTRAM512KB)  &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_EXTRAM16KB)   &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_EXTRAM32KB)   &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_EXTRAM48KB)   &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_EXTRAM64KB)   &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_EXTRAM512KB)  &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_EXTRAM1MB)    &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_EXTRAM2MB)    &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_EXTRAM4MB)    &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_MEGARAM128)   &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_MEGARAM256)   &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_MEGARAM512)   &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_MEGARAM768)   &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_MEGARAM2M)    &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_MEGASCSI128)  &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_MEGASCSI256)  &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_MEGASCSI512)  &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_MEGASCSI1MB)  &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_NOWINDDOS1)   &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_NOWINDDOS2)   &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_ESERAM128)    &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_ESERAM256)    &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_ESERAM512)    &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_ESERAM1MB)    &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_MEGAFLSHSCC)  &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_MEGAFLSHSCCPLUS) &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_WAVESCSI128)  &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_WAVESCSI256)  &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_WAVESCSI512)  &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_WAVESCSI1MB)  &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_ESESCC128)    &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_ESESCC256)    &&
                strcmp(properties->media.carts[i].fileName, CARTNAME_ESESCC512)    &&
                properties->media.carts[i].type != ROM_FMPAC               &&
                properties->media.carts[i].type != ROM_PAC                 &&
                properties->media.carts[i].type != ROM_GAMEREADER          &&
                properties->media.carts[i].type != ROM_SUNRISEIDE          &&
                properties->media.carts[i].type != ROM_BEERIDE             &&
                properties->media.carts[i].type != ROM_GIDE                &&
                properties->media.carts[i].type != ROM_GOUDASCSI           &&
                properties->media.carts[i].type != ROM_MSXAUDIO            &&
                properties->media.carts[i].type != ROM_MOONSOUND           &&
                properties->media.carts[i].type != ROM_SNATCHER            &&
                properties->media.carts[i].type != ROM_SDSNATCHER          &&
                properties->media.carts[i].type != ROM_SCCMIRRORED         &&
                properties->media.carts[i].type != ROM_SCC                 &&
                properties->media.carts[i].type != ROM_SCCPLUS             &&
                properties->media.carts[i].type != ROM_SONYHBI55           &&
                properties->media.carts[i].type != ROM_YAMAHASFG01         &&
                properties->media.carts[i].type != ROM_YAMAHASFG05         &&
                properties->media.carts[i].type != ROM_YAMAHANET           &&
                properties->media.carts[i].type != ROM_SF7000IPL)
            {
                done = 1;
            }
        }
    }

    for (i = 0; !done && i < PROP_MAX_DISKS; i++) {
        if (properties->media.disks[i].fileName[0]) {
            if (useExtendedName && extendedDiskName[i][0]) {
                strcpy(fileBase, extendedDiskName[i]);
            }
            else if (*properties->media.disks[i].fileNameInZip) {
#ifdef WII      // Use the same name for state files for every disk image within one zip file
                strcpy(fileBase, stripPathExt(properties->media.disks[i].fileName));
#else
                strcpy(fileBase, stripPathExt(properties->media.disks[i].fileNameInZip));
#endif
            }
            else {
                strcpy(fileBase, stripPathExt(properties->media.disks[i].fileName));
            }
            done = 1;
        }
    }

    for (i = 0; !done && i < PROP_MAX_TAPES; i++) {
        if (properties->media.tapes[0].fileName[0]) {
            if (useExtendedName && extendedCasName[i][0]) {
                strcpy(fileBase, extendedCasName[i]);
            }
            else if (*properties->media.tapes[i].fileNameInZip) {
                strcpy(fileBase, stripPathExt(properties->media.tapes[i].fileNameInZip));
            }
            else {
                strcpy(fileBase, stripPathExt(properties->media.tapes[i].fileName));
            }
            done = 1;
        }
    }

    if (fileBase[0] == 0) {
        strcpy(fileBase, "unknown");
        return 0;
    }

    return strlen(fileBase);
}



#ifdef USE_ARCH_GLOB
static UInt32 fileWriteTime(const char* filename)
{
  struct stat s;
  int rv;

  rv = stat(filename, &s);

  return rv < 0 ? 0 : (UInt32)s.st_mtime;
}

char* generateSaveFilename(Properties* properties, char* directory, char* prefix, char* extension, int digits)
{
    ArchGlob* glob;
    static char filename[512];
    char baseName[128];
    int fileIndex = 0;
    int extensionLen = strlen(extension);
    int i;
    int numMod = 1;
    char filenameFormat[32] = "%s/%s%s_";
    char destfileFormat[32];

    for (i = 0; i < digits; i++) {
        strcat(filenameFormat, "?");
        numMod *= 10;
    }
    strcat(filenameFormat, "%s");
    sprintf(destfileFormat, "%%s/%%s%%s_%%0%di%%s", digits);

    createSaveFileBaseName(baseName, properties, 0);

    sprintf(filename, filenameFormat, directory, prefix, baseName, extension);

    glob = archGlob(filename, ARCH_GLOB_FILES);

    if (glob) {
        if (glob->count > 0) {
            UInt32 writeTime = fileWriteTime(glob->pathVector[0]);
	        char lastfile[512];
            int filenameLen;
		    strcpy(lastfile, glob->pathVector[0]);

            for (i = 1; i < glob->count; i++) {
                UInt32 thisWriteTime = fileWriteTime(glob->pathVector[i]);
                if (thisWriteTime > 0 && thisWriteTime < writeTime) {
                    break;
                }
                writeTime = thisWriteTime;
		        strcpy(lastfile, glob->pathVector[i]);
            }

            filenameLen = strlen(lastfile);

            if (filenameLen > extensionLen + digits) {
                lastfile[filenameLen - extensionLen] = 0;
                fileIndex = (atoi(&lastfile[filenameLen - extensionLen - digits]) + 1) % numMod;
            }
        }
        archGlobFree(glob);
    }

    sprintf(filename, destfileFormat, directory, prefix, baseName, fileIndex, extension);

    return filename;
}
#else
char* generateSaveFilename(Properties* properties, char* directory, char* prefix, char* extension, int digits)
{
	WIN32_FIND_DATA fileData;
    FILETIME writeTime;
    HANDLE hFile;
	char lastfile[512];
    static char filename[512];
    char baseName[128];
    int fileIndex = 0;
    int extensionLen = strlen(extension);
    int i;
    int numMod = 1;
    char filenameFormat[32] = "%s" DIR_SEPARATOR "%s%s_";
    char destfileFormat[32];

    for (i = 0; i < digits; i++) {
        strcat(filenameFormat, "?");
        numMod *= 10;
    }
    strcat(filenameFormat, "%s");
    sprintf(destfileFormat, "%%s" DIR_SEPARATOR "%%s%%s_%%0%di%%s", digits);

    createSaveFileBaseName(baseName, properties, 0);

    sprintf(filename, filenameFormat, directory, prefix, baseName, extension);

    hFile = FindFirstFile(filename,&fileData);
    if (hFile != INVALID_HANDLE_VALUE) {
        int filenameLen;

        writeTime = fileData.ftLastWriteTime;
		strcpy(lastfile, fileData.cFileName);

	    while (FindNextFile(hFile, &fileData ) != 0) {
            if (CompareFileTime(&fileData.ftLastWriteTime, &writeTime) < 0) {
                break;
            }
            writeTime = fileData.ftLastWriteTime;
            strcpy(lastfile, fileData.cFileName);
		}

        filenameLen = strlen(lastfile);

        if (filenameLen > extensionLen + digits) {
            lastfile[filenameLen - extensionLen] = 0;
            fileIndex = (atoi(&lastfile[filenameLen - extensionLen - digits]) + 1) % numMod;
        }
        FindClose(hFile);
    }

    sprintf(filename, destfileFormat, directory, prefix, baseName, fileIndex, extension);

    return filename;
}
#endif
