/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $URL$
 * $Id$
 *
 */

#include "common/stdafx.h"
#include "common/endian.h"
#include "common/file.h"

#include "gob/gob.h"
#include "gob/saveload.h"
#include "gob/global.h"
#include "gob/game.h"

namespace Gob {

SaveLoad_v2::SaveLoad_v2(GobEngine *vm, const char *targetName) :
	SaveLoad(vm, targetName) {

	_stagesCount = 1;
}

SaveType SaveLoad_v2::getSaveType(const char *fileName) {
	const char *backSlash;
	if ((backSlash = strrchr(fileName, '\\')))
		fileName = backSlash + 1;

	if (!scumm_stricmp(fileName, "cat.inf"))
		return kSaveGame;
	if (!scumm_stricmp(fileName, "cat.cat"))
		return kSaveGame;
	if (!scumm_stricmp(fileName, "save.inf"))
		return kSaveTempSprite;
	if (!scumm_stricmp(fileName, "bloc.inf"))
		return kSaveNotes;

	return kSaveNone;
}

uint32 SaveLoad_v2::getSaveGameSize() {
	return 80 + (READ_LE_UINT32(_vm->_game->_totFileData + 0x2C) * 4) * 2;
}

int32 SaveLoad_v2::getSizeNotes() {
	Common::SaveFileManager *saveMan = g_system->getSavefileManager();
	Common::InSaveFile *in;
	int32 size = -1;

	in = saveMan->openForLoading(_saveFiles[(int) kSaveNotes]);
	if (in) {
		size = in->size();
		delete in;
	}

	return size;
}

int32 SaveLoad_v2::getSizeGame() {
	Common::SaveFileManager *saveMan = g_system->getSavefileManager();
	Common::InSaveFile *in;

	for (int i = 14; i >= 0; i--) {
		in = saveMan->openForLoading(setCurSlot(i));
		if (in) {
			delete in;
			return (i + 1) * READ_LE_UINT32(_vm->_game->_totFileData + 0x2C) *
				4 + 600;
		}
	}

	return -1;
}

int32 SaveLoad_v2::getSizeScreenshot() {
	return -1;
}

bool SaveLoad_v2::loadGame(int16 dataVar, int32 size, int32 offset) {
	Common::SaveFileManager *saveMan = g_system->getSavefileManager();
	Common::InSaveFile *in;
	int32 varSize = READ_LE_UINT32(_vm->_game->_totFileData + 0x2C) * 4;

	if (size == 0) {
		dataVar = 0;
		size = varSize;
	}

	int slot = (offset - 600) / varSize;
	int slotR = (offset - 600) % varSize;

	if ((offset == 0) && (size == 600)) {

		byte *varBuf = _vm->_global->_inter_variables + dataVar;
		for (int i = 0; i < 15; i++, varBuf += 40) {
			in = saveMan->openForLoading(setCurSlot(i));
			if (in) {
				in->read(varBuf, 40);
				delete in;
			} else
				memset(varBuf, 0, 40);
		}
		memset(_vm->_global->_inter_variablesSizes + dataVar, 0, 600);
		return true;

	} else if ((offset > 0) && (slot < 15) &&
			(slotR == 0) && (size == varSize)) {

		in = saveMan->openForLoading(setCurSlot(slot));
		if (!in) {
			warning("Can't open file for slot %d", slot);
			return false;
		}

		uint32 sGameSize = getSaveGameSize();
		uint32 fSize = in->size();
		if (fSize != sGameSize) {
			warning("Can't load from slot %d: Wrong size (%d, %d)", slot,
					fSize, sGameSize);
			delete in;
			return false;
		}

		in->seek(80);
		if (loadDataEndian(*in, dataVar, size)) {
			delete in;
			debugC(1, kDebugFileIO, "Loading from slot %d", slot);
			return true;
		}
		delete in;

	} else
		warning("Invalid loading procedure (%d, %d, %d, %d)",
				offset, size, slot, slotR);

	return false;
}

bool SaveLoad_v2::loadNotes(int16 dataVar, int32 size, int32 offset) {
	bool retVal;

	if ((size <= 0) || (offset != 0)) {
		warning("Invalid attempt at loading the notes (%d, %d)", size, offset);
		return false;
	}

	Common::SaveFileManager *saveMan = g_system->getSavefileManager();
	char *sName = _saveFiles[(int) kSaveNotes];

	Common::InSaveFile *in = saveMan->openForLoading(sName);
	if (!in) {
		warning("Can't open file \"%s\" for reading", sName);
		return false;
	}

	retVal = loadDataEndian(*in, dataVar, size);
	delete in;
	return retVal;
}

bool SaveLoad_v2::loadScreenshot(int16 dataVar, int32 size, int32 offset) {
	return false;
}

bool SaveLoad_v2::saveGame(int16 dataVar, int32 size, int32 offset) {
	int32 varSize = READ_LE_UINT32(_vm->_game->_totFileData + 0x2C) * 4;

	initBuffer();

	if (size == 0) {
		dataVar = 0;
		size = varSize;
	}

	int slot = (offset - 600) / varSize;
	int slotR = (offset - 600) % varSize;

	if ((offset == 0) && (size == 600)) {

		delete[] _buffer[0];
		_buffer[0] = new byte[1200];
		assert(_buffer[0]);

		memcpy(_buffer[0], _vm->_global->_inter_variables + dataVar, 600);
		memset(_buffer[0] + 600, 0, 600);

		return true;

	} else if ((offset > 0) && (slot < 15) &&
			(slotR == 0) && (size == varSize)) {

		if (!_buffer[0]) {
			warning("Tried to save without writing the index first");
			return false;
		}

		Common::SaveFileManager *saveMan = g_system->getSavefileManager();
		Common::OutSaveFile *out;
		
		out = saveMan->openForSaving(setCurSlot(slot));
		if (!out) {
			warning("Can't open file for slot %d for writing", slot);
			delete[] _buffer[0];
			_buffer[0] = 0;
			return false;
		}

		bool retVal = false;

		if (out->write(_buffer[0] + slot * 40, 40) == 40)
			if (out->write(_buffer[0] + 600 + slot * 40, 40) == 40)
				if (saveDataEndian(*out, dataVar, size)) {
					out->finalize();
					if (!out->ioFailed())
						retVal = true;
				}

		if (!retVal)
			warning("Can't save to slot %d", slot);
		else
			debugC(1, kDebugFileIO, "Saved to slot %d", slot);

		delete[] _buffer[0];
		_buffer[0] = 0;
		delete out;

		return retVal;

	} else
		warning("Invalid saving procedure (%d, %d, %d, %d)",
				offset, size, slot, slotR);

	return false;
}

bool SaveLoad_v2::saveNotes(int16 dataVar, int32 size, int32 offset) {
	bool retVal;

	if ((size <= 0) || (offset != 0)) {
		warning("Invalid attempt at saving the notes (%d, %d)", size, offset);
		return false;
	}

	Common::SaveFileManager *saveMan = g_system->getSavefileManager();
	char *sName = _saveFiles[(int) kSaveNotes];

	Common::OutSaveFile *out = saveMan->openForSaving(sName);
	if (!out) {
		warning("Can't open file \"%s\" for writing", sName);
		return false;
	}

	retVal = saveDataEndian(*out, dataVar, size);

	out->finalize();
	if (out->ioFailed()) {
		warning("Can't save notes");
		retVal = false;
	}

	delete out;
	return retVal;
}

bool SaveLoad_v2::saveScreenshot(int16 dataVar, int32 size, int32 offset) {
	return false;
}

void SaveLoad_v2::initBuffer() {
	if (_buffer)
		return;

	_buffer = new byte*[_stagesCount];
	assert(_buffer);
	_buffer[0] = 0;
}

} // End of namespace Gob