// Internal stuff here to keep public header uncluttered

// Blip_Buffer 0.4.0
#ifndef BLIP_BUFFER_IMPL2_H
#define BLIP_BUFFER_IMPL2_H

//// Compatibility

BLARGG_DEPRECATED( int const blip_low_quality  =  8; )
BLARGG_DEPRECATED( int const blip_med_quality  =  8; )
BLARGG_DEPRECATED( int const blip_good_quality = 12; )
BLARGG_DEPRECATED( int const blip_high_quality = 16; )

BLARGG_DEPRECATED( int const blip_sample_max = 32767; )

// Number of bits in raw sample that covers normal output range. Less than 32 bits to give
// extra amplitude range. That is,
// +1 << (blip_sample_bits-1) = +1.0
// -1 << (blip_sample_bits-1) = -1.0
int const blip_sample_bits = 30;

//// BLIP_READER_

//// Optimized reading from Blip_Buffer, for use in custom sample buffer or mixer

// Begins reading from buffer. Name should be unique to the current {} block.
#define BLIP_READER_BEGIN( name, blip_buffer ) \
	const Blip_Buffer::delta_t* BLARGG_RESTRICT name##_reader_buf = (blip_buffer).read_pos();\
	int name##_reader_accum = (blip_buffer).integrator()

// Gets value to pass to BLIP_READER_NEXT()
#define BLIP_READER_BASS( blip_buffer ) (blip_buffer).highpass_shift()

// Constant value to use instead of BLIP_READER_BASS(), for slightly more optimal
// code at the cost of having no bass_freq() functionality
int const blip_reader_default_bass = 9;

// Current sample as 16-bit signed value
#define BLIP_READER_READ( name )        (name##_reader_accum >> (blip_sample_bits - 16))

// Current raw sample in full internal resolution
#define BLIP_READER_READ_RAW( name )    (name##_reader_accum)

// Advances to next sample
#define BLIP_READER_NEXT( name, bass ) \
	(void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass)))

// Ends reading samples from buffer. The number of samples read must now be removed
// using Blip_Buffer::remove_samples().
#define BLIP_READER_END( name, blip_buffer ) \
	(void) ((blip_buffer).set_integrator( name##_reader_accum ))

#define BLIP_READER_ADJ_( name, offset ) (name##_reader_buf += offset)

int const blip_reader_idx_factor = sizeof (Blip_Buffer::delta_t);

#define BLIP_READER_NEXT_IDX_( name, bass, idx ) {\
	name##_reader_accum -= name##_reader_accum >> (bass);\
	name##_reader_accum += name##_reader_buf [(idx)];\
}

#define BLIP_READER_NEXT_RAW_IDX_( name, bass, idx ) {\
	name##_reader_accum -= name##_reader_accum >> (bass);\
	name##_reader_accum +=\
			*(Blip_Buffer::delta_t const*) ((char const*) name##_reader_buf + (idx));\
}

//// BLIP_CLAMP

#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
		defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
	#define BLIP_X86 1
	#define BLIP_CLAMP_( in ) in < -0x8000 || 0x7FFF < in
#else
	#define BLIP_CLAMP_( in ) (blip_sample_t) in != in
#endif

// Clamp sample to blip_sample_t range
#define BLIP_CLAMP( sample, out )\
	{ if ( BLIP_CLAMP_( (sample) ) ) (out) = ((sample) >> 31) ^ 0x7FFF; }


//// Blip_Synth

// (in >> sh & mask) * mul
#define BLIP_SH_AND_MUL( in, sh, mask, mul ) \
((int) (in) / ((1U << (sh)) / (mul)) & (unsigned) ((mask) * (mul)))

// (T*) ptr + (off >> sh)
#define BLIP_PTR_OFF_SH( T, ptr, off, sh ) \
	((T*) (BLIP_SH_AND_MUL( off, sh, -1, sizeof (T) ) + (char*) (ptr)))

template<int quality,int range>
inline void Blip_Synth<quality,range>::offset_resampled( blip_resampled_time_t time,
		int delta, Blip_Buffer* blip_buf ) const
{
#if BLIP_BUFFER_FAST
	int const half_width = 1;
#else
	int const half_width = quality / 2;
#endif
	
	Blip_Buffer::delta_t* BLARGG_RESTRICT buf = blip_buf->delta_at( time );
	
	delta *= impl.delta_factor;

	int const phase_shift = BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS;
	int const phase = (half_width & (half_width - 1)) ?
		(int) BLIP_SH_AND_MUL( time, phase_shift, blip_res - 1, sizeof (coeff_t) ) * half_width :
		(int) BLIP_SH_AND_MUL( time, phase_shift, blip_res - 1, sizeof (coeff_t) * half_width );
	
#if BLIP_BUFFER_FAST
	int left = buf [0] + delta;
	
	// Kind of crappy, but doing shift after multiply results in overflow.
	// Alternate way of delaying multiply by delta_factor results in worse
	// sub-sample resolution.
	int right = (delta >> BLIP_PHASE_BITS) * phase;
	#if BLIP_BUFFER_NOINTERP
		// TODO: remove? (just a hack to see how it sounds)
		right = 0;
	#endif
	left  -= right;
	right += buf [1];
	
	buf [0] = left;
	buf [1] = right;
#else
	
	int const fwd = -quality / 2;
	int const rev = fwd + quality - 2;
	
	coeff_t const* BLARGG_RESTRICT imp = (coeff_t const*) ((char const*) phases + phase);
	int const phase2 = phase + phase - (blip_res - 1) * half_width * sizeof (coeff_t);
	
	#define BLIP_MID_IMP imp = (coeff_t const*) ((char const*) imp - phase2);
	
	#if BLIP_MAX_QUALITY > 16
		// General version for any quality
		if ( quality != 8 && quality != 12 && quality != 16 )
		{
			buf += fwd;
			
			// left half
			for ( int n = half_width / 2; --n >= 0; )
			{
				buf [0] += imp [0] * delta;
				buf [1] += imp [1] * delta;
				imp += 2;
				buf += 2;
			}
			
			// mirrored right half
			BLIP_MID_IMP
			for ( int n = half_width / 2; --n >= 0; )
			{
				buf [0] +=   imp  [-1] * delta;
				buf [1] += *(imp -= 2) * delta;
				buf += 2;
			}
			
			return;
		}
	#endif
	
	// Unrolled versions for qualities 8, 12, and 16
	
	#if BLIP_X86
		// This gives better code for x86
		#define BLIP_ADD( out, in ) \
			buf [out] += imp [in] * delta
		
		#define BLIP_FWD( i ) {\
			BLIP_ADD( fwd     + i, i     );\
			BLIP_ADD( fwd + 1 + i, i + 1 );\
		}
		
		#define BLIP_REV( r ) {\
			BLIP_ADD( rev     - r, r + 1 );\
			BLIP_ADD( rev + 1 - r, r     );\
		}
		
		BLIP_FWD( 0 )
		BLIP_FWD( 2 )
		if ( quality > 8  ) BLIP_FWD( 4 )
		if ( quality > 12 ) BLIP_FWD( 6 )
			BLIP_MID_IMP
		if ( quality > 12 ) BLIP_REV( 6 )
		if ( quality > 8  ) BLIP_REV( 4 )
		BLIP_REV( 2 )
		BLIP_REV( 0 )
	
	#else
		// Help RISC processors and simplistic compilers by reading ahead of writes
		#define BLIP_FWD( i ) {\
			int t0 =          i0 * delta + buf [fwd     + i];\
			int t1 = imp [i + 1] * delta + buf [fwd + 1 + i];\
			i0 =           imp [i + 2];\
			buf [fwd     + i] = t0;\
			buf [fwd + 1 + i] = t1;\
		}
		
		#define BLIP_REV( r ) {\
			int t0 =      i0 * delta + buf [rev     - r];\
			int t1 = imp [r] * delta + buf [rev + 1 - r];\
			i0 =           imp [r - 1];\
			buf [rev     - r] = t0;\
			buf [rev + 1 - r] = t1;\
		}
		
		int i0 = *imp;
		BLIP_FWD( 0 )
		if ( quality > 8  ) BLIP_FWD( 2 )
		if ( quality > 12 ) BLIP_FWD( 4 )
		{
			int const mid = half_width - 1;
			int t0 =        i0 * delta + buf [fwd + mid - 1];
			int t1 = imp [mid] * delta + buf [fwd + mid    ];
			BLIP_MID_IMP
			i0           = imp [mid];
			buf [fwd + mid - 1] = t0;
			buf [fwd + mid    ] = t1;
		}
		if ( quality > 12 ) BLIP_REV( 6 )
		if ( quality > 8  ) BLIP_REV( 4 )
		BLIP_REV( 2 )
		
		int t0 =   i0 * delta + buf [rev    ];
		int t1 = *imp * delta + buf [rev + 1];
		buf [rev    ] = t0;
		buf [rev + 1] = t1;
	#endif
	
#endif
}

template<int quality,int range>
#if BLIP_BUFFER_FAST
	inline
#endif
void Blip_Synth<quality,range>::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const
{
	offset_resampled( buf->to_fixed( t ), delta, buf );
}

template<int quality,int range>
#if BLIP_BUFFER_FAST
	inline
#endif
void Blip_Synth<quality,range>::update( blip_time_t t, int amp )
{
	int delta = amp - impl.last_amp;
	impl.last_amp = amp;
	offset_resampled( impl.buf->to_fixed( t ), delta, impl.buf );
}


//// blip_eq_t

inline blip_eq_t::blip_eq_t( double t ) :
		treble( t ), kaiser( 5.2 ), rolloff_freq( 0 ), sample_rate( 44100 ), cutoff_freq( 0 ) { }
inline blip_eq_t::blip_eq_t( double t, int rf, int sr, int cf, double k ) :
		treble( t ), kaiser( k ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { }


//// Blip_Buffer

inline int  Blip_Buffer::length() const         { return length_; }
inline int  Blip_Buffer::samples_avail() const  { return (int) (offset_ >> BLIP_BUFFER_ACCURACY); }
inline int  Blip_Buffer::sample_rate() const    { return sample_rate_; }
inline int  Blip_Buffer::output_latency() const { return BLIP_MAX_QUALITY / 2; }
inline int  Blip_Buffer::clock_rate() const     { return clock_rate_; }
inline void Blip_Buffer::clock_rate( int cps )  { factor_ = clock_rate_factor( clock_rate_ = cps ); }

inline void Blip_Buffer::remove_silence( int count )
{
	// fails if you try to remove more samples than available
	assert( count <= samples_avail() );
	offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
}

#endif
