// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/*

Atari 2600 SuperCharger support

*/

#include "formats/a26_cas.h"


#define A26_CAS_SIZE            8448
#define A26_WAV_FREQUENCY       44100
#define BIT_ZERO_LENGTH         10
#define BIT_ONE_LENGTH          15
#define ZEROS_ONES              2755

static const uint16_t one_wave[BIT_ONE_LENGTH] = {
	0x2AE5, 0x4E60, 0x644E, 0x68E4, 0x5B56, 0x3DFE, 0x15ED, 0xEA13, 0xC202, 0xA4AA,
	0x971C, 0x9BB2, 0xB1A0, 0xD51B, 0x0000
};

static const uint16_t zero_wave[BIT_ZERO_LENGTH] = {
	0x3DFE, 0x644E, 0x644E, 0x3DFE, 0x0000, 0xC202, 0x9BB2, 0x9BB2, 0xC202, 0x0000
};

static int a26_cas_output_wave( int16_t **buffer, int16_t wave_data, int length ) {
	int i;

	if ( buffer ) {
		for ( i = 0 ; i < length; i++ ) {
			**buffer = wave_data;
			*buffer = *buffer + 1;
		}
	}
	return length;
}

static int a26_cas_output_bit( int16_t **buffer, int bit ) {
	int size = 0;
	int bit_size = bit ? BIT_ONE_LENGTH : BIT_ZERO_LENGTH;
	const int16_t *p = bit ? (const int16_t *)one_wave : (const int16_t *)zero_wave;
	int i;

	for ( i = 0; i < bit_size; i++ ) {
		size += a26_cas_output_wave( buffer, p[i], 1 );
	}

	return size;
}

static int a26_cas_output_byte( int16_t **buffer, uint8_t byte ) {
	int     size = 0;
	int     i;

	for( i = 0; i < 8; i++, byte <<= 1 ) {
		size += a26_cas_output_bit( buffer, ( ( byte & 0x80 ) ? 1 : 0 ) );
	}
	return size;
}

static int a26_cas_clearing_tone( int16_t **buffer ) {
	int     size = 0;

	size += a26_cas_output_wave( buffer, 0, 44100 );
	return size;
}

static int a26_cas_zeros_ones( int16_t **buffer ) {
	int     size = 0;
	int     i;

	for ( i = 0; i < ZEROS_ONES; i++ ) {
		size += a26_cas_output_bit( buffer, 0 );
		size += a26_cas_output_bit( buffer, 1 );
	}
	size += a26_cas_output_bit( buffer, 0 );
	size += a26_cas_output_bit( buffer, 0 );
	return size;
}

static int a26_cas_output_contents( int16_t **buffer, const uint8_t *bytes ) {
	int     size = 0;
	int     i, pages, j;

	/* There are 8 header bytes */
	for( i = 0; i < 8; i++ ) {
		size += a26_cas_output_byte( buffer, bytes[ 0x2000 + i ] );
	}

	pages = bytes[0x2003];

	/* Output each page prefixed with a small page header */
	for ( i = 0; i < pages; i++ ) {
		size += a26_cas_output_byte( buffer, bytes[ 0x2010 + i ] );
		size += a26_cas_output_byte( buffer, bytes[ 0x2040 + i ] );
		for ( j = 0; j < 256; j++ ) {
			size += a26_cas_output_byte( buffer, bytes[ i * 256 + j ] );
		}
	}

	return size;
}

static int a26_cas_do_work( int16_t **buffer, const uint8_t *bytes ) {
	int     size = 0;

	/* Output clearing tone */
	size += a26_cas_clearing_tone( buffer );

	/* Output header tone, alternating 1s and 0s for about a second ending with two 0s */
	size += a26_cas_zeros_ones( buffer );

	/* Output the actual contents of the tape */
	size += a26_cas_output_contents( buffer, bytes );

	/* Output footer tone, alternating 1s and 0s for about a second ending with two 0s */
	size += a26_cas_zeros_ones( buffer );

	return size;
}

static int a26_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) {
	int16_t   *p = buffer;

	return a26_cas_do_work( &p, (const uint8_t *)bytes );
}

static int a26_cas_to_wav_size( const uint8_t *casdata, int caslen ) {
	return a26_cas_do_work( nullptr, casdata );
}

static const cassette_image::LegacyWaveFiller a26_legacy_fill_wave = {
	a26_cas_fill_wave,
	-1,
	0,
	a26_cas_to_wav_size,
	A26_WAV_FREQUENCY,
	0,
	0
};

static cassette_image::error a26_cassette_identify( cassette_image *cassette, cassette_image::Options *opts ) {
	uint64_t size;

	size = cassette->image_size( );
	if ( size == A26_CAS_SIZE ) {
		return cassette->legacy_identify( opts, &a26_legacy_fill_wave );
	}
	return cassette_image::error::INVALID_IMAGE;
}

static cassette_image::error a26_cassette_load( cassette_image *cassette ) {
	return cassette->legacy_construct( &a26_legacy_fill_wave );
}

static const cassette_image::Format a26_cassette_format = {
	"a26",
	a26_cassette_identify,
	a26_cassette_load,
	nullptr
};

CASSETTE_FORMATLIST_START(a26_cassette_formats)
	CASSETTE_FORMAT(a26_cassette_format)
CASSETTE_FORMATLIST_END
