//
// Copyright 2020 Electronic Arts Inc.
//
// TiberianDawn.DLL and RedAlert.dll and corresponding source code 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 3 of the License, or (at your option) any later version.

// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 
// in the hope that it will be useful, but with permitted additional restrictions 
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 
// distributed with this program. You should have received a copy of the 
// GNU General Public License along with permitted additional restrictions 
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection

/***************************************************************************
 **   C O N F I D E N T I A L --- W E S T W O O D   A S S O C I A T E S   **
 ***************************************************************************
 *                                                                         *
 *                 Project Name : WWLIB												*
 *                                                                         *
 *                    File Name : PALETTE.C											*
 *                                                                         *
 *                   Programmer : BILL STOKES										*
 *                                                                         *
 *                   Start Date : 6/20/91												*
 *                                                                         *
 *                  Last Update : August 2, 1994   [SKB]                   *
 *                                                                         *
 *-------------------------------------------------------------------------*
 * Note: This module contains dependencies upon the video system,				*
 * specifically Get_Video_Mode().														*
 *-------------------------------------------------------------------------*
 * Functions:                                                              *
 *   Set_Palette -- sets the current palette											*
 *   Set_Palette_Color -- Set a color number in a palette to the data.     *
 *	  Fade_Palette_To -- Fades the current palette into another					*
 *   Determine_Bump_Rate -- determines desired bump rate for fading        *
 *   Bump_Palette -- increments the palette one step, for fading           *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*
********************************* Includes **********************************
*/
//#include <mem.h>

#include "palette.h"
#include "timer.h"
#include "wwstd.h"


/*
********************************* Constants *********************************
*/

/*
********************************** Globals **********************************
*/
extern "C" extern unsigned char  CurrentPalette[];		/* in pal.asm */

/*
******************************** Prototypes *********************************
*/

PRIVATE void __cdecl Determine_Bump_Rate(void *palette, int delay, short *ticks, short *rate);
PRIVATE BOOL __cdecl Bump_Palette(void *palette1, unsigned int step);

/*
******************************** Code *********************************
*/

/***************************************************************************
 * Set_Palette -- sets the current palette											*
 *                                                                         *
 * INPUT:                                                                  *
 *		void *palette		- palette to set												*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		none																						*
 *                                                                         *
 * WARNINGS:                                                               *
 *                                                                         *
 * HISTORY:                                                                *
 *   04/25/1994 SKB : Created.                                             *
 *   04/27/1994 BR : Converted to 32-bit                                   *
 *=========================================================================*/
void __cdecl Set_Palette(void *palette)
{

	#if(IBM)
		Set_Palette_Range(palette);
	#else
		Copy_Palette(palette,CurrentPalette);
   	LoadRGB4(&Main_Screen->ViewPort,palette,32L);
	   LoadRGB4(AltVPort,palette,32L);
	#endif

}	/* end of Set_Palette */


/***************************************************************************
 * Set_Palette_Color -- Set a color number in a palette to the data.       *
 *                                                                         *
 *                                                                         *
 * INPUT:                                                                  *
 *		void *palette	- palette to set color in										*
 *		int color		- which color index to set										*
 *		void *data		- RGB data for color												*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		none																						*
 *                                                                         *
 * WARNINGS:                                                               *
 *                                                                         *
 * HISTORY:                                                                *
 *   04/25/1994 SKB : Created.                                             *
 *   04/27/1994 BR : Converted to 32-bit                                   *
 *=========================================================================*/
void __cdecl Set_Palette_Color(void *palette, int color, void *data)
{
	/*
	---------------------- Return if 'palette' is NULL -----------------------
	*/
	if (!palette) return;

	/*
	------------------- Change the color & set the palette -------------------
	*/
	#if(IBM)
		memcpy(&((unsigned char *)palette)[color * RGB_BYTES], data, RGB_BYTES);
		Set_Palette_Range(palette);
	#else
		palette[color] = *(unsigned short*)data;
		Set_Palette(palette);
	#endif

}	/* end of Set_Palette */


/***************************************************************************
 *	Fade_Palette_To -- Fades the current palette into another					*
 *                                                                         *
 * This will allow the palette to fade from current palette into the 		*
 * palette that was passed in.  This can be used to fade in and fade out.	*
 *                                                                         *
 * INPUT:      																				*
 *    char  *palette1 - this is the palette to fade to.           			*
 *		unsigned int delay		 -	fade with this timer count down							*
 *		void *callback  - user-defined callback function							*
 *                                                                         *
 * OUTPUT:     none                                                        *
 *                                                                         *
 * WARNINGS:   none                                                        *
 *                                                                         *
 * HISTORY:                                                                *
 *   06/20/1991  BS : Created.                                             *
 *=========================================================================*/
void Fade_Palette_To(void *palette1, unsigned int delay, void (*callback)() )
{
	BOOL	changed;	// Flag that palette has changed this tick.
	short	jump;		// Gun values to jump per palette set.
	unsigned long	timer;		// Tick count timer used for timing.
	short	ticksper;	// The ticks (fixed point) per bit jump.
	int	tickaccum;


	extern void (*cb_ptr)(void);	// callback function pointer

//	(void *)cb_ptr = callback;
	cb_ptr = callback;

	/*
	---------------------- Return if 'palette1' is NULL ----------------------
	*/
	if (!palette1)
		return;

	/*
	--------------------------- Get the bump rate ----------------------------
	*/
	Determine_Bump_Rate(palette1, delay, &ticksper, &jump);

	tickaccum = 0;							// init accumulated elapsed time
	timer = TickCount.Time(); 				// timer = current time
	do {
		changed = FALSE;

		tickaccum += ticksper;			// tickaccum = time of next change * 256
		timer += (tickaccum >> 8);		// timer = time of next change (rounded)
		tickaccum &= 0x0FF;				// shave off high byte, keep roundoff bits

		changed = Bump_Palette(palette1, jump);	// increment palette

		/*
		.................. Wait for time increment to elapse ..................
		*/
		if (changed) {
			while (TickCount.Time() < (int)timer) {
				/*
				................. Update callback while waiting .................
				*/
				if (callback) {
#if LIB_EXTERNS_RESOLVED
					Sound_Callback();		// should be removed!
#endif
					(*cb_ptr)();
				}
			}
		}

#if LIB_EXTERNS_RESOLVED
		Sound_Callback();		// should be removed!
#endif
		if (callback) {
			(*cb_ptr)();
		}
	} while (changed);

}	/* end of Fade_Palette_To */


/***************************************************************************
 * Determine_Bump_Rate -- determines desired bump rate for fading          *
 *                                                                         *
 * INPUT:                                                                  *
 *		unsigned char *palette	- palette to fade to												*
 *		int delay		- desired time delay in 60ths of a second					*
 *		short *ticks		- output: loop ticks per color jump							*
 *		short *rate		- output: color gun increment rate							*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		none																						*
 *                                                                         *
 * WARNINGS:                                                               *
 *                                                                         *
 * HISTORY:                                                                *
 *   04/27/1994 BR : Converted to 32-bit                                   *
 *   08/02/1994 SKB : Made private                                         *
 *=========================================================================*/
PRIVATE void __cdecl Determine_Bump_Rate(void *palette, int delay, short *ticks,
	short *rate)
{
	int gun1;		// Palette 1 gun value.
	int gun2;		// Palette 2 gun value.
	int diff;		// Maximum color gun difference.
	int tp;			// Temporary tick accumulator.
	int index;		// Color gun working index.
	long t;			// Working tick intermediate value.
	int adiff;		// Absolute difference between guns.

	/*
	------------------------ Find max gun difference -------------------------
	*/
	diff = 0;
	for (index = 0; index < PALETTE_BYTES; index++) {
		gun1 = ((unsigned char *)palette)[index];
		gun2 = CurrentPalette[index];
		adiff = ABS(gun1-gun2);
		diff = MAX(diff, adiff);
	}

	/*------------------------------------------------------------------------
	ticks = (total time delay ) / (max gun diff)
	The value is computed based on (delay * 256), for fixed-point math;
	the lower bits represent the leftover from the division;  'ticks' is
	returned still shifted, so the low bits can be used to accumulate the
	time more accurately; the caller must shift the accumulated value down
	8 bits to determine the actual elapsed time!
	------------------------------------------------------------------------*/
	t = ((long)delay) << 8;
	if (diff) {
		t /= diff;
		t = MIN((long)t, (long)0x7FFF);
	}
	*ticks = (short)t;

	/*------------------------------------------------------------------------
	Adjust the color gun rate value if the time to fade is faster than can
	reasonably be performed given the palette change, ie if (ticks>>8)==0,
	and thus less than 1/60 of a second
	------------------------------------------------------------------------*/
	tp = *ticks;
	*rate = 1;
	while (*rate <= diff && *ticks < 256) {
		*ticks += tp;
		*rate += 1;
	}

}	/* end of Determine_Bump_Rate */


/***************************************************************************
 * Bump_Palette -- increments the palette one step, for fading             *
 *                                                                         *
 * INPUT:                                                                  *
 *		palette1		- palette to fade towards											*
 *		step			- max step amount, determined by Determine_Bump_Rate		*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		FALSE = no change, TRUE = changed												*
 *                                                                         *
 * WARNINGS:                                                               *
 *                                                                         *
 * HISTORY:                                                                *
 *   04/27/1994 BR : Created.                                              *
 *   08/02/1994 SKB : Made private                                         *
 *=========================================================================*/
#if(IBM)
PRIVATE BOOL __cdecl Bump_Palette(void *palette1, unsigned int step)
{
	BOOL	changed=FALSE;				// Flag that palette has changed this tick.
	int	index;						// Index to DAC register gun.
	int	gun1,gun2;					// Palette 1 gun value.
	unsigned char	palette[PALETTE_BYTES];	// copy of current palette

	/*
	---------------------- Return if 'palette1' is NULL ----------------------
	*/
	if (!palette1)
		return (FALSE);


	/*
	------------------------ Copy the current palette ------------------------
	*/
	memcpy(palette, CurrentPalette, 768);

	/*
	----------------------- Loop through palette bytes -----------------------
	*/
	for (index = 0; index < PALETTE_BYTES; index++) {
		gun1 = ((unsigned char *)palette1)[index];
		gun2 = palette[index];

		/*
		............. If the colors match, go on to the next one ..............
		*/
		if (gun1 == gun2) continue;

		changed = TRUE;

		/*
		.................. Increment current palette's color ..................
		*/
		if (gun2 < gun1) {
			gun2 += step;
			gun2 = MIN(gun2, gun1);		// make sure we didn't overshoot it
		}
		/*
		.................. Decrement current palette's color ..................
		*/
		else {
			gun2 -= step;
			gun2 = MAX(gun2, gun1);		// make sure we didn't overshoot it
		}

		palette[index] = (unsigned char)gun2;
	}

	/*
	----------------- Set current palette to the new palette -----------------
	*/
	if (changed) {
		Set_Palette(&palette[0]);
	}

	return (changed);

}	/* end of Bump_Palette */

#else

	/* This is already implemented in asm on the Amiga */

#endif

void (*cb_ptr)(void);	// callback function pointer

/**************************** End of palette.cpp ***************************/


