#include <cstdio>
#include <cstring>
#include <cctype>
#include <dirent.h>
#include <string>
#include <deque>
#include <iterator>

#ifdef _MSC_VER

 #undef strcasecmp
 #define strcasecmp _stricmp

 #undef strncasecmp
 #define strncasecmp _strnicmp

#endif

#include "version.h"

class FileList {
	deque<string> TheList;
	unsigned int ListIndex;
public:
	string* first() {
		if (TheList.size()) {
			ListIndex = 0;
			return &TheList[ListIndex];
		} else {
			return NULL;
		}
	}

	string* next() {
		if (ListIndex >= TheList.size() - 1) {
			return NULL;
		}
		ListIndex++;
		return &TheList[ListIndex];
	}

	void add(char* szString) {
			TheList.push_back(szString);
	}
};

int main(int argc, char** argv)
{
	unsigned int nStateVersion = BURN_VERSION;
	int nFunction = 0;

	FileList States;

	if (argc <= 1) {
		nFunction = -1;
	} else {
		for (int i = 1; i < argc; i++) {
			if (strcasecmp(argv[i], "-r") == 0) {
				nFunction = 1;
				continue;
			}
			if (strcasecmp(argv[i], "-w") == 0) {
				nFunction = 2;
				continue;
			}
			if (strncasecmp(argv[i], "-v", 2) == 0) {
				char* szToVersion;
				if (strlen(argv[i]) == 2) {
					i++;
					szToVersion = argv[i];
				} else {
					if (strlen(argv[i]) == 8) {
						szToVersion = &argv[i][2];
					} else {
						nFunction = -1;
						break;
					}
				}

				for (int si = 0; si < 6; si++) {
					if (!isdigit(szToVersion[si])) {
						nFunction = -1;
						break;
					}
				}
				sscanf(szToVersion, "%6x", &nStateVersion);
				continue;
			}

			if (strlen(argv[i]) > 3 && strcasecmp(argv[i] + strlen(argv[i]) - 3, ".fs") == 0) {
				States.add(argv[i]);
			} else {
				DIR* curdir;
				if ((curdir = opendir(argv[i]))) {
					struct dirent* curentry;
					while ((curentry = readdir(curdir))) {
						if (strlen(curentry->d_name) > 3 && strcasecmp(curentry->d_name + strlen(curentry->d_name) - 3, ".fs") == 0) {
							char szTmp[128];
							sprintf(szTmp, "%s\\%s", argv[i], curentry->d_name);
							States.add(szTmp);
						}
					}
					closedir(curdir);
				} else {
					if (strchr(argv[i], '.')) {
						printf("\"%s\" was ignored.\n", argv[i]);
					} else {
						printf("\"%s\" could not be opened.\n", argv[i]);
					}
				}
			}
		}
	}

	if (nFunction == -1) {
		printf("Usage: %s [-r | -w] [-v nnnnnn] <FILE | DIR> [<FILE | DIR>...]\n", argv[0]);
		return 1;
	}

	if (nFunction == 0) {
		// No function specified -> perform read
		nFunction = 1;
	}

	for (string* statename = States.first(); statename; statename = States.next()) {
		FILE* state;
		char szIdentifier[4];

		if ((state = fopen(statename->c_str(), "rb+"))) {
			int nBytesRead = fread(szIdentifier, 1, 4, state);
			if (nBytesRead == 4 && strncmp("fs  ", szIdentifier, 4) == 0) {
				switch (nFunction) {
					case 1:
						fread(&nStateVersion, 4, 1, state);
						printf("\"%s\" was created using FB(A) v%06x.\n", statename->c_str(), nStateVersion);
						break;
					case 2:
						fseek(state, 0, SEEK_CUR);
						if (fwrite(&nStateVersion, 4, 1, state) == 1) {
							printf("\"%s\" was updated to FB(A) v%06x.\n", statename->c_str(), nStateVersion);
						} else {
							printf("***ERROR: \"%s\" was not updated!\n", statename->c_str());
						}
						break;
				}
			} else {
				printf("\"%s\" is not a Final Burn (Alpha) savestate.\n", statename->c_str());
			}
			fclose(state);
		} else {
			printf("\"%s\" could not be opened.\n", statename->c_str());
		}
	}

	return 0;
}
