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

/* $Header: /CounterStrike/JSHELL.CPP 2     3/13/97 2:05p Steve_tall $ */
/***********************************************************************************************
 ***              C O N F I D E N T I A L  ---  W E S T W O O D  S T U D I O S               ***
 ***********************************************************************************************
 *                                                                                             *
 *                 Project Name : Command & Conquer                                            *
 *                                                                                             *
 *                    File Name : JSHELL.CPP                                                   *
 *                                                                                             *
 *                   Programmer : Joe L. Bostic                                                *
 *                                                                                             *
 *                   Start Date : April 2, 1994                                                *
 *                                                                                             *
 *                  Last Update : May 11, 1995 [JLB]                                           *
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 *   Build_Translucent_Table -- Creates a translucent control table.                           *
 *   Conquer_Build_Translucent_Table -- Builds fading table for shadow colors only.            *
 *   Fatal -- General purpose fatal error handler.                                             *
 *   Load_Alloc_Data -- Allocates a buffer and loads the file into it.                         *
 *   Load_Uncompress -- Loads and uncompresses data to a buffer.                               *
 *   Set_Window -- Sets the window dimensions to that specified.                               *
 *   Small_Icon -- Create a small icon from a big one.                                         *
 *   Translucent_Table_Size -- Determines the size of a translucent table.                     *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include	"function.h"
#include	"wwfile.h"


/***********************************************************************************************
 * Small_Icon -- Create a small icon from a big one.                                           *
 *                                                                                             *
 *    This routine will extract the specified icon from the icon data file and convert that    *
 *    icon into a small (3x3) representation. Typical use of this mini-icon is for the radar   *
 *    map.                                                                                     *
 *                                                                                             *
 * INPUT:   iconptr  -- Pointer to the icon data file.                                         *
 *                                                                                             *
 *          iconnum  -- The embedded icon number to convert into a small image.                *
 *                                                                                             *
 * OUTPUT:  Returns with a pointer to the small icon imagery. This is exactly 9 bytes long.    *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   05/11/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
void * Small_Icon(void const * iconptr, int iconnum)
{
	static unsigned char _icon[9];
	IControl_Type const * iptr = (IControl_Type const *)iconptr;
	unsigned char * data;

	if (iconptr) {
		iconnum = ((char *)((char *)iptr + iptr->Map))[iconnum];
		data = &((unsigned char *)((unsigned char *)iptr + iptr->Icons))[iconnum*(24*24)];
//		data = &iptr->Icons[iconnum*(24*24)];

		for (int index = 0; index < 9; index++) {
			int _offsets[9] = {
				4+4*24,
				12+4*24,
				20+4*24,
				4+12*24,
				12+12*24,
				20+12*24,
				4+20*24,
				12+20*24,
				20+20*24
			};
			_icon[index] = data[_offsets[index]];
		}
	}

	return(_icon);
}


/***********************************************************************************************
 * Set_Window -- Sets the window dimensions to that specified.                                 *
 *                                                                                             *
 *    Use this routine to set the windows dimensions to the coordinates and dimensions         *
 *    specified.                                                                               *
 *                                                                                             *
 * INPUT:   x     -- Window X pixel position.                                                  *
 *                                                                                             *
 *          y     -- Window Y pixel position.                                                  *
 *                                                                                             *
 *          w     -- Window width in pixels.                                                   *
 *                                                                                             *
 *          h     -- Window height in pixels.                                                  *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   The X and width values are truncated to an even 8 pixel boundary. This is       *
 *             the same as stripping off the lower 3 bits.                                     *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   01/15/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
void Set_Window(int window, int x, int y, int w, int h)
{
	WindowList[window][WINDOWWIDTH] = w;
	WindowList[window][WINDOWHEIGHT] = h;
	WindowList[window][WINDOWX] = x;
	WindowList[window][WINDOWY] = y;
}


/***********************************************************************************************
 * Fatal -- General purpose fatal error handler.                                               *
 *                                                                                             *
 *    This is a very simple general purpose fatal error handler. It goes directly to text      *
 *    mode, prints the error, and then aborts with a failure code.                             *
 *                                                                                             *
 * INPUT:   message  -- The text message to display.                                           *
 *                                                                                             *
 *          ...      -- Any optional parameters that are used in formatting the message.       *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   This routine never returns. The game exits immediately.                         *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   10/17/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
void Fatal(char const * message, ...)
{
	va_list	va;

	va_start(va, message);
	Prog_End(message, true);
	vfprintf(stderr, message, va);
	Mono_Printf(message);
	if (!RunningAsDLL) {	//PG
		Emergency_Exit(EXIT_FAILURE);
	}
}


#ifdef NEVER
void File_Fatal(char const * message)
{
	//Prog_End();
	perror(message);
	Emergency_Exit(EXIT_FAILURE);
}
#endif



/***********************************************************************************************
 * Load_Uncompress -- Loads and uncompresses data to a buffer.                                 *
 *                                                                                             *
 *    This is the C++ counterpart to the Load_Uncompress function. It will load the file       *
 *    specified into the graphic buffer indicated and uncompress it.                           *
 *                                                                                             *
 * INPUT:   file     -- The file to load and uncompress.                                       *
 *                                                                                             *
 *          uncomp_buff -- The graphic buffer that initial loading will use.                   *
 *                                                                                             *
 *          dest_buff   -- The buffer that will hold the uncompressed data.                    *
 *                                                                                             *
 *          reserved_data  -- This is an optional pointer to a buffer that will hold any       *
 *                            reserved data the compressed file may contain. This is           *
 *                            typically a palette.                                             *
 *                                                                                             *
 * OUTPUT:  Returns with the size of the uncompressed data in the destination buffer.          *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   10/17/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
long Load_Uncompress(FileClass &file, BuffType &uncomp_buff, BuffType &dest_buff, void * reserved_data)
{
	unsigned short	size;
	void	* sptr = uncomp_buff.Get_Buffer();
	void	* dptr = dest_buff.Get_Buffer();
	int	opened = false;
	CompHeaderType	header;

	/*
	**	The file must be opened in order to be read from. If the file
	**	isn't opened, then open it. Record this fact so that it can be
	**	restored to its closed state at the end.
	*/
	if (!file.Is_Open()) {
		if (!file.Open()) {
			return(0);
		}
		opened = true;
	}

	/*
	**	Read in the size of the file (supposedly).
	*/
	file.Read(&size, sizeof(size));

	/*
	**	Read in the header block. This block contains the compression type
	**	and skip data (among other things).
	*/
	file.Read(&header, sizeof(header));
	size -= sizeof(header);

	/*
	**	If there are skip bytes then they must be processed. Either read
	**	them into the buffer provided or skip past them. No check is made
	**	to ensure that the reserved data buffer is big enough (watch out!).
	*/
	if (header.Skip) {
		size -= header.Skip;
		if (reserved_data) {
			file.Read(reserved_data, header.Skip);
		} else {
			file.Seek(header.Skip, SEEK_CUR);
		}
		header.Skip = 0;
	}

	/*
	**	Determine where is the proper place to load the data. If both buffers
	**	specified are identical, then the data should be loaded at the end of
	**	the buffer and decompressed at the beginning.
	*/
	if (uncomp_buff.Get_Buffer() == dest_buff.Get_Buffer()) {
		sptr = (char *)sptr + uncomp_buff.Get_Size()-(size+sizeof(header));
	}

	/*
	**	Read in the bulk of the data.
	*/
	Mem_Copy(&header, sptr, sizeof(header));
	file.Read((char *)sptr + sizeof(header), size);

	/*
	**	Decompress the data.
	*/
	size = (unsigned int) Uncompress_Data(sptr, dptr);

	/*
	**	Close the file if necessary.
	*/
	if (opened) {
		file.Close();
	}
	return((long)size);
}


int Load_Picture(char const * filename, BufferClass& scratchbuf, BufferClass& destbuf, unsigned char * palette, PicturePlaneType )
{
	return(Load_Uncompress(CCFileClass(filename), scratchbuf, destbuf,  palette ) / 8000);
}


/***********************************************************************************************
 * Load_Alloc_Data -- Allocates a buffer and loads the file into it.                           *
 *                                                                                             *
 *    This is the C++ replacement for the Load_Alloc_Data function. It will allocate the       *
 *    memory big enough to hold the file and then read the file into it.                       *
 *                                                                                             *
 * INPUT:   file  -- The file to read.                                                         *
 *                                                                                             *
 *          mem   -- The memory system to use for allocation.                                  *
 *                                                                                             *
 * OUTPUT:  Returns with a pointer to the allocated and filled memory block.                   *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   10/17/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
void * Load_Alloc_Data(FileClass &file)
{
	void * ptr = 0;
	long size = file.Size();

	ptr = new char [size];
	if (ptr) {
		file.Read(ptr, size);
	}
	return(ptr);
}


/***********************************************************************************************
 * Translucent_Table_Size -- Determines the size of a translucent table.                       *
 *                                                                                             *
 *    Use this routine to determine how big the translucent table needs                        *
 *    to be given the specified number of colors. This value is typically                      *
 *    used when allocating the buffer for the translucent table.                               *
 *                                                                                             *
 * INPUT:   count -- The number of colors that are translucent.                                *
 *                                                                                             *
 * OUTPUT:  Returns the size of the translucent table.                                         *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   04/02/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
long Translucent_Table_Size(int count)
{
	return(256L + (256L * count));
}


/***********************************************************************************************
 * Build_Translucent_Table -- Creates a translucent control table.                             *
 *                                                                                             *
 *    The table created by this routine is used by Draw_Shape (GHOST) to                       *
 *    achieve a translucent affect. The original color of the shape will                       *
 *    show through. This differs from the fading effect, since that                            *
 *    affect only alters the background color toward a single destination                      *
 *    color.                                                                                   *
 *                                                                                             *
 * INPUT:   palette  -- Pointer to the control palette.                                        *
 *                                                                                             *
 *          control  -- Pointer to array of structures that control how                        *
 *                      the translucent table will be built.                                   *
 *                                                                                             *
 *          count    -- The number of entries in the control array.                            *
 *                                                                                             *
 *          buffer   -- Pointer to buffer to place the translucent table.                      *
 *                      If NULL is passed in, then the buffer will be                          *
 *                      allocated.                                                             *
 *                                                                                             *
 * OUTPUT:  Returns with pointer to the translucent table.                                     *
 *                                                                                             *
 * WARNINGS:   This routine is exceedingly slow. Use sparingly.                                *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   04/02/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
void * Build_Translucent_Table(PaletteClass const & palette, TLucentType const * control, int count, void * buffer)
{
	unsigned char	const *table;		// Remap table pointer.
	int			index;		// Working color index.

	if (count && control/* && palette*/) {		// palette can't be NULL... ST - 5/9/2019
		if (!buffer) {
			buffer = new char [Translucent_Table_Size(count)];
		}

		if (buffer) {
			memset(buffer, -1, 256);
			table = (unsigned char*)buffer + 256;

			/*
			**	Build the individual remap tables for each translucent color.
			*/
			for (index = 0; index < count; index++) {
				((unsigned char*)buffer)[control[index].SourceColor] = index;
				Build_Fading_Table(palette.Get_Data(), (void*)table, control[index].DestColor, control[index].Fading);
				table = (unsigned char*)table + 256;
			}
		}
	}
	return(buffer);
}


/***********************************************************************************************
 * Conquer_Build_Translucent_Table -- Builds fading table for shadow colors only.              *
 *                                                                                             *
 *    This routine will build a translucent (fading) table to remap colors into the shadow     *
 *    color region of the palette. Shadow colors are not affected by this translucent table.   *
 *    This means that a shape can be overlapped any number of times and the imagery will       *
 *    remain deterministic (and constant).                                                     *
 *                                                                                             *
 * INPUT:   palette  -- Pointer to the palette to base the translucent process on.             *
 *                                                                                             *
 *          control  -- Pointer to special control structure that specifies the                *
 *                      target color, and percentage of fade.                                  *
 *                                                                                             *
 *          count    -- The number of colors to be remapped (entries in the control array).    *
 *                                                                                             *
 *          buffer   -- Pointer to the staging buffer that will hold the translucent table     *
 *                      data. If this parameter is NULL, then an appropriate sized table       *
 *                      will be allocated.                                                     *
 *                                                                                             *
 * OUTPUT:  Returns with a pointer to the translucent table data.                              *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   06/27/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
void * Conquer_Build_Translucent_Table(PaletteClass const & palette, TLucentType const * control, int count, void * buffer)
{
	unsigned char	const *table;		// Remap table pointer.

	if (count && control) {
		if (!buffer) {
			buffer = new char [Translucent_Table_Size(count)];
		}

		if (buffer) {
			memset(buffer, -1, 256);
			table = (unsigned char*)buffer + 256;

			/*
			**	Build the individual remap tables for each translucent color.
			*/
			for (int index = 0; index < count; index++) {
				((unsigned char*)buffer)[control[index].SourceColor] = index;
				Conquer_Build_Fading_Table(palette, (void*)table, control[index].DestColor, control[index].Fading);
				table = (unsigned char*)table + 256;
			}
		}
	}
	return(buffer);
}


void * Make_Fading_Table(PaletteClass const & palette, void * dest, int color, int frac)
{
	if (dest) {
		unsigned char * ptr = (unsigned char *)dest;

		/*
		**	Find an appropriate remap color index for every color in the palette.
		**	There are certain exceptions to this, but they are trapped within the
		**	loop.
		*/
		for (int index = 0; index < PaletteClass::COLOR_COUNT; index++) {

			/*
			**	Find the color that, ideally, the working color should be remapped
			**	to in the special remap range.
			*/
			RGBClass trycolor = palette[index];
			trycolor.Adjust(frac, palette[color]);			// Try to match this color.

			/*
			**	Search through the remap range to find the color that should be remapped
			**	to. This special range is used for shadows or other effects that are
			**	not compounded if additively applied.
			*/
			*ptr++ = palette.Closest_Color(trycolor);
		}
	}
	return(dest);
}


void * Conquer_Build_Fading_Table(PaletteClass const & palette, void * dest, int color, int frac)
{
	if (dest) {
		unsigned char * ptr = (unsigned char *)dest;
//		HSVClass desthsv = palette[color];

		/*
		**	Find an appropriate remap color index for every color in the palette.
		**	There are certain exceptions to this, but they are trapped within the
		**	loop.
		*/
		for (int index = 0; index < PaletteClass::COLOR_COUNT; index++) {

			/*
			**	If this color should not be remapped, then it will be stored as a remap
			**	to itself. This is effectively no remap.
			*/
			if (index > PaletteClass::COLOR_COUNT-16 || index == 0) {
				*ptr++ = index;
			} else {

				/*
				**	Find the color that, ideally, the working color should be remapped
				**	to in the special remap range.
				*/
				RGBClass trycolor = palette[index];
				trycolor.Adjust(frac, palette[color]);			// Try to match this color.

				/*
				**	Search through the remap range to find the color that should be remapped
				**	to. This special range is used for shadows or other effects that are
				**	not compounded if additively applied.
				*/
				int best = -1;
				int bvalue = 0;
				for (int id = PaletteClass::COLOR_COUNT-16; id < PaletteClass::COLOR_COUNT-1; id++) {
					int diff = palette[id].Difference(trycolor);
					if (best == -1 || diff < bvalue) {
						best = id;
						bvalue = diff;
					}
				}
				*ptr++ = best;
			}
		}
	}
	return(dest);
}


#ifdef OBSOLETE
//int Desired_Facing8(int x1, int y1, int x2, int y2)
DirType xDesired_Facing8(int x1, int y1, int x2, int y2)
{
	int index = 0;				// Facing composite value.

	/*
	**	Figure the absolute X difference. This determines
	**	if the facing is leftward or not.
	*/
	int xdiff = x2-x1;
	if (xdiff < 0) {
		index |= 0x00C0;
		xdiff = -xdiff;
	}

	/*
	**	Figure the absolute Y difference. This determines
	**	if the facing is downward or not. This also clarifies
	**	exactly which quadrant the facing lies.
	*/
	int ydiff = y1-y2;
	if (ydiff < 0) {
		index ^= 0x0040;
		ydiff = -ydiff;
	}

	/*
	**	Determine which of the two direction offsets it bigger. The
	**	offset direction that is bigger (X or Y) will indicate which
	**	orthogonal direction the facing is closer to.
	*/
	unsigned bigger;
	unsigned smaller;
	if (xdiff < ydiff) {
		smaller = xdiff;
		bigger = ydiff;
	} else {
		smaller = ydiff;
		bigger = xdiff;
	}

	/*
	**	If on the diagonal, then incorporate this into the facing
	**	and then bail. The facing is known.
	*/
	if (((bigger+1)/2) <= smaller) {
		index += 0x0020;
		return(DirType(index));
	}

	/*
	**	Determine if the facing is closer to the Y axis or
	**	the X axis.
	*/
	int adder = (index & 0x0040);
	if (xdiff == bigger) {
		adder ^= 0x0040;
	}
	index += adder;

	return(DirType(index));
}


//int Desired_Facing256(int srcx, int srcy, int dstx, int dsty)
DirType xDesired_Facing256(int srcx, int srcy, int dstx, int dsty)
{
	int composite=0;		// Facing built from intermediate calculations.

	/*
	**	Fetch the absolute X difference. This also gives a clue as
	**	to which hemisphere the direction lies.
	*/
	int xdiff = dstx - srcx;
	if (xdiff < 0) {
		composite |= 0x00C0;
		xdiff = -xdiff;
	}

	/*
	**	Fetch the absolute Y difference. This clarifies the exact
	**	quadrant that the direction lies.
	*/
	int ydiff = srcy - dsty;
	if (ydiff < 0) {
		composite ^= 0x0040;
		ydiff = -ydiff;
	}

	/*
	**	Bail early if the coordinates are the same. This check also
	**	has the added bonus of ensuring that checking for division
	**	by zero is not needed in the following section.
	*/
	if (xdiff == 0 && ydiff == 0) return(DirType(0xFF));

	/*
	**	Determine which of the two direction offsets it bigger. The
	**	offset direction that is bigger (X or Y) will indicate which
	**	orthogonal direction the facing is closer to.
	*/
	unsigned bigger;
	unsigned smaller;
	if (xdiff < ydiff) {
		smaller = xdiff;
		bigger = ydiff;
	} else {
		smaller = ydiff;
		bigger = xdiff;
	}

	/*
	**	Now that the quadrant is known, we need to determine how far
	**	from the orthogonal directions, the facing lies. This value
	**	is calculated as a ratio from 0 (matches orthogonal) to 31
	**	(matches diagonal).
	*/
	int frac = (smaller * 32U) / bigger;

	/*
	**	Given the quadrant and knowing whether the facing is closer
	**	to the X or Y axis, we must make an adjustment toward the
	**	subsequent quadrant if necessary.
	*/
	int adder = (composite & 0x0040);
	if (xdiff > ydiff) {
		adder ^= 0x0040;
	}
	if (adder) {
		frac = (adder - frac)-1;
	}

	/*
	**	Integrate the fraction value into the quadrant.
	*/
	composite += frac;

	/*
	**	Return with the final facing value.
	*/
	return(DirType(composite & 0x00FF));
}
#endif
