//    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
//    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 "cdsk_xfd.h"
#include "autil.h"
#include "cfile.h"

CXfd::CXfd() : CDisk()
{
	#ifdef _MEMORY_DUMP_
		printf( "CXfd constructed: %08X\n", this );
	#endif
}

CXfd::~CXfd()
{
	#ifdef _MEMORY_DUMP_
		printf( "CXfd destructed: %08X\n", this );
	#endif
}

typedef enum
{
	LOAD_OK,
	LOAD_BAD_DD_1,
	LOAD_BAD_DD_2,
	LOAD_BAD_DD_3
} LOAD_VARIANT;

BOOL CXfd::Load( char* szFname, BOOL bRepair, BOOL bRepairAuto )
{
	LOAD_VARIANT load_method = LOAD_OK;	

	int iFirstSectorsSize = 0x80;

	CFile cf;

	if ( !cf.Open( szFname ) )
	{
		sprintf( m_szLastError, "XFD: Can't open '%s'", szFname );
		return FALSE;
	}

	strcpy( m_szFname, szFname );

	LONG lFileLen = cf.GetLength();

	int iSecs;
	int iSecSize;

	if ( lFileLen % 0x80 )
	{
		sprintf( m_szLastError, "XFD: Strange length!" );
		cf.Close();
		return FALSE;
	}

	if ( ( lFileLen / 0x80 ) > 1040 )
	{
		iSecSize = 0x100;

		iSecs = ( ( lFileLen - 0x180 ) / 0x100 ) + 3;
	}
	else
	{
		iSecSize = 0x80;
		iSecs = lFileLen / 0x80;
	}

	if ( ( ( ( iSecs - 3 ) * iSecSize ) + 0x180 ) != lFileLen )
	{
		sprintf( m_szLastError, "XFD: Format violated: (%08lX != %08X)", lFileLen, iSecs * iSecSize );
		m_iErrorCode = CXFD_FORMAT_VIOLATED;

		if ( !bRepair )
		{
			cf.Close();
			return FALSE;
		}
		else
		{
			iSecs = lFileLen / iSecSize;
			BYTE abtBuff[ 0x100 ];

			memset( abtBuff, 0, 0x100 );

			int iM1zeroes = 3;
			int iM2zeroes = 3;
			int iM3zeroes = 3;

			cf.Seek( ( 0x02 - 1 ) * 0x80, SEEK_SET );
			cf.Read( abtBuff, 0x80 );

			if ( IsBlockEmpty( abtBuff, 0x80 ) )
				iM1zeroes--;

			cf.Seek( ( 0x04 - 1 ) * 0x80, SEEK_SET );
			cf.Read( abtBuff, 0x80 );

			if ( IsBlockEmpty( abtBuff, 0x80 ) )
			{
				iM1zeroes--;
				iM2zeroes--;
			}

			cf.Seek( ( 0x05 - 1 ) * 0x80, SEEK_SET );
			cf.Read( abtBuff, 0x80 );

			if ( IsBlockEmpty( abtBuff, 0x80 ) )
				iM2zeroes--;

			cf.Seek( ( 0x06 - 1 ) * 0x80, SEEK_SET );
			cf.Read( abtBuff, 0x80 );

			if ( IsBlockEmpty( abtBuff, 0x80 ) )
			{
				iM1zeroes--;
				iM2zeroes--;
			}

			cf.Seek( -0x180, SEEK_END );
			cf.Read( abtBuff, 0x80 );
			if ( IsBlockEmpty( abtBuff, 0x80 ) )
				iM3zeroes--;

			cf.Read( abtBuff, 0x80 );
			if ( IsBlockEmpty( abtBuff, 0x80 ) )
				iM3zeroes--;

			cf.Read( abtBuff, 0x80 );
			if ( IsBlockEmpty( abtBuff, 0x80 ) )
				iM3zeroes--;

			if ( !iM1zeroes )
			{
				load_method = LOAD_BAD_DD_1;
			}
			else if ( !iM2zeroes )
			{
				load_method = LOAD_BAD_DD_2;
			}
			else if ( !iM3zeroes )
			{
				load_method = LOAD_BAD_DD_3;
			}

			if ( !bRepairAuto )
			{
				printf( "Invalid DD ATR file encountered.\n" );
				printf( "Choose repair method:\n" );
				printf( "1) Sector, gap, sector, gap, sector, gap, data\n" );
				printf( "2) Three sectors, three empty sectors, data\n" );
				printf( "3) Data, three empty sectors\n" );
				printf( "4) Don't repair\n" );

				switch( load_method )
				{
					case LOAD_BAD_DD_1:
						printf( "(Method 1 looks best)\n" );
						break;

					case LOAD_BAD_DD_2:
						printf( "(Method 2 looks best)\n" );
						break;

					case LOAD_BAD_DD_3:
						printf( "(Method 3 looks best)\n" );
						break;

					default:
						break;
				}

				int iMethod;

				printf( "\n" );
				do
				{
					iMethod = getch() - '0';
				} while( ( iMethod < 1 ) || ( iMethod > 4 ) );

				if ( iMethod == 4 )
				{
					cf.Close();
					return FALSE;
				}
			}
			else
			{
				if ( load_method == LOAD_OK )
					load_method = LOAD_BAD_DD_1;
			}

			cf.Seek( 0, SEEK_SET );

			switch( load_method )
			{
				case LOAD_BAD_DD_1:
				case LOAD_BAD_DD_2:
					iFirstSectorsSize = 0x100;
					break;

				default:
					break;
					
			}
					
		
		}
	}

	DISK_GEOMETRY dg;

	GuessClassicSizes( iSecs, iSecSize, &dg );

	if ( !Format( &dg ) )
	{
		cf.Close();
		return FALSE;
	}

	BYTE abtBuff[ 0x100 ];
	memset( abtBuff, 0, 0x100 );

	for( int i = 0; i < iSecs; i++ )
	{
		switch( load_method )
		{
			default:
			case LOAD_OK:
				cf.Read( abtBuff, ( i < 3 ) ? 0x80 : iSecSize );
				break;

			case LOAD_BAD_DD_1:
				if ( i < 3 )
				{
					cf.Read( abtBuff, 0x80 );
					cf.Seek( 0x80, SEEK_CUR );
				}
				else
					cf.Read( abtBuff, 0x100 );
				break;

			case LOAD_BAD_DD_2:
				if ( i < 3 )
				{
					cf.Read( abtBuff, 0x80 );

					if ( i == 2 )
						cf.Seek( 0x180, SEEK_CUR );
				}
				else
					cf.Read( abtBuff, 0x100 );

				break;

			case LOAD_BAD_DD_3:
				if ( i < 3 )
					cf.Read( abtBuff, 0x80 );
				else
					cf.Read( abtBuff, 0x100 );

				break;
		}

		if ( !WriteSector( i + 1, abtBuff ) )
		{
			cf.Close();
			return FALSE;
		}
	}

	cf.Close();
	return TRUE;

}

#ifdef __CDISK_WRITE__

BOOL CXfd::Save( char* szOutFile, BOOL bOverWrite )
{
	CFile cf;

	if ( !bOverWrite && !access( szOutFile, F_OK ) )
	{
		sprintf( m_szLastError, "XFD: File already exists! '%s'", szOutFile );
		return FALSE;
	}

	if ( !cf.Create( szOutFile ) )
	{
		sprintf( m_szLastError, "XFD: Can't create '%s'", szOutFile );
		return FALSE;
	}

	BYTE abtBuff[ 0x100 ];

	for( WORD i = 1; i <= m_geometry.iSectors; i++ )
	{
		if ( !ReadSector( abtBuff, i ) )
			return FALSE;

		int iToWrite = ( i <= 3 ) ? 0x80: m_geometry.iBytesPerSector;

		int iWritten;

		if ( !cf.Write( abtBuff, iToWrite, &iWritten ) || ( iWritten != iToWrite ) )
		{
			sprintf( m_szLastError, "XFD: Can't write!" );
			cf.Close();
			unlink( szOutFile );
			return FALSE;
		}

	}

	cf.Close();

	return TRUE;
}

#endif //__CDISK_WRITE__