/*
   AtrMemoryImage.C - access ATR images in RAM.

   Copyright (C) 2002 Matthias Reichl <hias@horus.com>

   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.
*/

#include "AtrMemoryImage.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef USE_ZLIB
#include <zlib.h>
#endif

#include <ctype.h>

#include "AtariDebug.h"

AtrMemoryImage::AtrMemoryImage()
	: fData(0), fChanged(false)
{
}

AtrMemoryImage::~AtrMemoryImage()
{
	FreeImageData();
}

void AtrMemoryImage::FreeImageData()
{
	if (fData) {
		delete[] fData;
		fData=0;
	}
	SetFormat(eNoDisk);
}

int AtrMemoryImage::CreateImage(EDiskFormat format)
{
	size_t imgSize;
	FreeImageData();
	fChanged = true;
	if (SetFormat(format)) {
		DPRINTF("SetFormat failed\n");
		return 1;
	}

	SetWriteProtect(false);

	if ((imgSize = GetImageSize()) == 0) {
		DPRINTF("GetImageSize = 0\n");
		return 1;
	}
	fData = new unsigned char[imgSize];

	if (fData) {
		memset (fData, 0, imgSize);
		return 0;
	} else {
		DPRINTF("couldnt alloc fData\n");
		FreeImageData();
		return 1;
	}
}

int AtrMemoryImage::CreateImage(ESectorLength density, size_t sectors)
{
	size_t imgSize;
	FreeImageData();
	fChanged = true;
	if (SetFormat(density, sectors)) {
		DPRINTF("SetFormat failed\n");
		return 1;
	}

	SetWriteProtect(false);

	if ((imgSize = GetImageSize()) == 0) {
		DPRINTF("GetImageSize = 0\n");
		return 1;
	}
	fData = new unsigned char[imgSize];

	if (fData) {
		memset (fData, 0, imgSize);
		return 0;
	} else {
		DPRINTF("couldnt alloc fData\n");
		FreeImageData();
		return 1;
	}
}

int AtrMemoryImage::CreateImage(ESectorLength density, size_t sectorsPerTrack, size_t tracks, size_t sides)
{
	size_t imgSize;
	FreeImageData();
	fChanged = true;
	if (SetFormat(density, sectorsPerTrack, tracks, sides)) {
		DPRINTF("SetFormat failed\n");
		return 1;
	}

	SetWriteProtect(false);

	if ((imgSize = GetImageSize()) == 0) {
		DPRINTF("GetImageSize = 0\n");
		return 1;
	}
	fData = new unsigned char[imgSize];

	if (fData) {
		memset (fData, 0, imgSize);
		return 0;
	} else {
		DPRINTF("couldnt alloc fData\n");
		FreeImageData();
		return 1;
	}
}


int AtrMemoryImage::ReadImageFromFile(const char* filename)
{
	unsigned char hdr[16];
	size_t imgSize, s;

#ifdef USE_ZLIB
	gzFile f;
#else
	FILE *f;
#endif

	FreeImageData();

#ifdef USE_ZLIB
	if (!(f = gzopen(filename,"rb"))) {
		DPRINTF("couldnt open %s\n",filename);
		return 1;
	}

	if ( gzread(f, hdr, 16) != 16 ) {
		DPRINTF("couldnt read header\n");
		goto failure;
	}
#else
	if (!(f = fopen(filename,"rb"))) {
		DPRINTF("couldnt open %s\n",filename);
		return 1;
	}

	if ( fread(hdr, 1, 16, f) != 16 ) {
		DPRINTF("couldnt read header\n");
		goto failure;
	}
#endif

	if (SetFormatFromATRHeader(hdr)) {
		DPRINTF("SetFormatFromATRHeader failed\n");
		goto failure;
	}

	if ((imgSize = GetImageSize()) == 0) {
		DPRINTF("GetImageSize = 0\n");
		goto failure;
	}
	fData = new unsigned char[imgSize];
	memset(fData, 0, imgSize);

	if (!fData) {
		DPRINTF("couldnt alloc fData\n");
		goto failure;
	}

#ifdef USE_ZLIB
	if ( (s=gzread(f, fData, imgSize)) != imgSize) {
		DPRINTF("truncated ATR file: only got %d of %d bytes\n", s, imgSize);
		//goto failure;
	}
#else
	if ( (s=fread(fData, 1, imgSize, f)) != imgSize) {
		DPRINTF("truncated ATR file: only got %d of %d bytes\n", s, imgSize);
		//goto failure;
	}
#endif

	fChanged = false;

#ifdef USE_ZLIB
	gzclose(f);
#else
	fclose(f);
#endif
	return 0;

failure:
	fChanged = false;
	FreeImageData();
#ifdef USE_ZLIB
	gzclose(f);
#else
	fclose(f);
#endif
	return 1;
}

int AtrMemoryImage::WriteBackImageToFile() const
{
	if (GetFilename()) {
		return WriteImageToFile(GetFilename());
	} else {
		return 1;
	}
}

int AtrMemoryImage::WriteImageToFile(const char* filename) const
{
	unsigned char hdr[16];
	size_t imgSize;

#ifdef USE_ZLIB
	size_t len;
#endif

	if (! fData) {
		DPRINTF("no fData\n");
		return 1;
	}

	if (CreateATRHeaderFromFormat(hdr)) {
		DPRINTF("CreateATRHeaderFromFormat failed\n");
		return 1;
	}

	if ((imgSize = GetImageSize()) == 0) {
		DPRINTF("GetImageSize = 0\n");
		return 1;
	}
#ifdef USE_ZLIB
	if ( (len=strlen(filename))>=3
	     && filename[len-3]=='.'
	     && toupper(filename[len-2]) == 'G'
	     && toupper(filename[len-1]) == 'Z')
	{
		gzFile gzf;

		if (!(gzf = gzopen(filename,"wb"))) {
			DPRINTF("couldn't open %s for writing\n",filename);
			return 1;
		}
		if (gzwrite(gzf,hdr,16) != 16) {
			DPRINTF("couldn't write ATR header\n");
			goto gzfailure;
		}
		if (gzwrite(gzf,fData,imgSize) != (int) imgSize) {
			DPRINTF("couldn't write ATR image\n");
			goto gzfailure;
		}
		fChanged = false;
		gzclose(gzf);
		return 0;

	gzfailure:
		gzclose(gzf);
		unlink(filename);
		return 1;
	}
#endif
	{
		FILE *f;

		if (!(f = fopen(filename,"wb"))) {
			DPRINTF("couldn't open %s for writing\n",filename);
			return 1;
		}
		if (fwrite(hdr,1,16,f) != 16) {
			DPRINTF("couldn't write ATR header\n");
			goto failure;
		}
		if (fwrite(fData,1,imgSize,f) != imgSize) {
			DPRINTF("couldn't write ATR image\n");
			goto failure;
		}
		fChanged = false;
		fclose(f);
		return 0;

	failure:
		fclose(f);
		unlink(filename);
		return 1;
	}
}

int AtrMemoryImage::ReadSector(size_t sector,unsigned char* buffer,size_t buffer_length) const
{
	int ret=0;
	int len, offset;

	if ((offset=CalculateOffset(sector)) < 0 ) {
		DPRINTF("illegal sector in ReadSector: %d\n", sector);
		return 1;
	}

	if ((len=GetSectorLength(sector)) < 0) {
		DPRINTF("GetSectorLength(%d) failed\n",sector);
		return 1;
	}

	if (!buffer_length) {
		DPRINTF("buffer length = 0\n");
		return 1;
	}

	if (buffer_length < (size_t) len) {
		DPRINTF("buffer length < sector length [ %d < %d ]\n",buffer_length, (size_t) len);
		ret = 1;
		len = buffer_length;
	} else if (buffer_length > (size_t)len) {
		DPRINTF("buffer length > sector length [ %d > %d ]\n",buffer_length, (size_t) len);
		ret = 1;
	}

	memcpy(buffer, fData+offset, len);

	return ret;
}

int AtrMemoryImage::WriteSector(size_t sector,const unsigned char* buffer,size_t buffer_length)
{
	int ret=0;
	int len, offset;

	if (IsWriteProtected()) {
		DPRINTF("attempting to write sector to write protected image\n");
		return 1;
	}

	if ((offset=CalculateOffset(sector)) < 0 ) {
		DPRINTF("illegal sector in WriteSector: %d\n", sector);
		return 1;
	}

	if ((len=GetSectorLength(sector)) < 0) {
		DPRINTF("GetSectorLength(%d) failed\n",sector);
		return 1;
	}

	if (buffer_length != (size_t) len) {
		DPRINTF("buffer length = len [ %d != %d ]\n", buffer_length, (size_t) len);
		return 1;
	}

	fChanged = true;
	memcpy(fData+offset, buffer, len);

	return ret;
}

bool AtrMemoryImage::IsAtrMemoryImage() const
{
	return true;
}
