////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 RWS Inc, All Rights Reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as published by
// the Free Software Foundation
//
// 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.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
// hood.cpp
// Project: Nostril (aka Postal)
//
// This module impliments the CHood class, which embodies the background,
// the "building" sprites, the "opaque" sprites, and the attribute map.
//
// History:
//		12/25/96 MJR	Started.
//
//		01/22/97	JMI	Now uses one string m_acBaseName to create the filenames
//							for the Background Image, the Attribute Map, and the
//							Alpha & Opaque layers.
//							Also, added GUI to get this string.
//
//		01/27/97	JMI	Now fills the edit field with m_acBaseName before
//							presenting the dialog to the user in EditNew().
//
//		01/27/97	JMI	Now loads files as %s%02dAlpha/Opaque instead of
//							%sAlpha/Opaque%02d.	Removed #if block around some hack
//							I'm not sure I understood to begin with.
//
//		01/27/97	JMI	I must've introduced a copy/paste type error or something
//							when I changed the Init().  But, anyways, now opaque layers
//							don't x-ray.
//
//		01/30/97	JMI	Now uses FullPath() to construct paths and uses smaller
//							conventions for layer files to help fit into 8.3 names
//							for ISO 9660 (now uses a for alpha and o for opaque).
//							Also, positions caret at end of line of text in EditNew()'s
//							dialog's edit field.
//
//		02/03/97	JMI	Now loads realm specific assets through the resource
//							manager.
//
//		02/04/97	JMI	Changed LoadDib() call to Load() (which now supports
//							loading of DIBs).
//
//		02/04/97	JMI	Changed all resources to pointers so we can fully utilize
//							the RResMgr.
//
//		02/07/97	JMI	Now allows you to setup the world transform
//							(m_pRealm->m_scene.m_transWorld) via the EditNew/Modify
//							dialog.
//
//		02/07/97	JMI	Now calls m_scene.SetupPipeline() to update the world
//							transform (which is now the view member of the scene's
//							pipeline).
//
//		02/10/97	JMI	rspReleaseResource() now takes a ptr to a ptr.
//
//		02/13/97	JMI	Changed paths for hoods to be in hoods/ instead of bg/
//							and also now just store the import part (like "city" instead
//							of "hoods/city").
//							Also, now gets resources for lighting effects.
//
//		02/13/97	JMI	In previous update, forgot to release alpha related res's.
//
//		02/24/97	JMI	No longer sets the m_type member of the m_sprite b/c it
//							is set by m_sprite's constructor.
//
//		02/26/97	JMI	No longer skips map load if no layers loaded.
//
//		03/13/97	JMI	Load now takes a version number.
//
//		03/25/97	JMI	GetResources() now opens the hood sak with the same name as
//							the hood.
//
//		04/09/97 BRH	Added RMultiGrid for the multi layer attribute maps, but
//							haven't taken out the RAttributeMap yet so that the
//							game will continue to work until we completely switch over.
//
//		04/18/97	JMI	Now Startup() calls rspSetPalette*() and rspUpdatePalette()
//							instead of Init().
//
//		04/22/97	JMI	Now ReleaseResources() purges m_pRealm->m_resmgr after
//							releasing the hood's resources so we get them out of memory
//							right away.
//
//		05/09/97	JMI	EditNew() was not checking the result of Init() before
//							calling Startup().  Fixed.
//
//		05/13/97	JMI	Casted instances of warning C4018 (signed/unsigned mismatch)
//							to make MSVC 4.1(Alpha) happy (these seem to fall under
//							Warning Level 3 in 4.1(Alpha) but not in 4.2(Intel)).
//
//		05/15/97	JMI	As per Bill, I changed the default world X axis rotation to
//							30.
//
//		05/26/97	JMI	Now Save()s, Load()s, and EditModify()s
//							m_pRealm->m_dKillsPercentGoal.
//
//		05/29/97	JMI	Changed occurences of m_pHeightMap to m_pTerrainMap.
//							Changed occurences of m_pAttrMap to m_pLayerMap.
//							Also, removed occurences of m_pattribMap.
//
//		05/30/97	JMI	The distance between building layers was hard coded to 3.
//							Now that the distance is 4, I used a more dynamic value
//							(subtract two of the same type of building layers to get
//							this value now).
//
//		06/16/97	JMI	Now CSprite::InBlitOpaque is one of the flags set for the
//							background image when it is added to the CScene.
//
//		06/16/97	JMI	Added SetPalette() which can be used to set the hood's
//							palette at the convenience of the Realm runner.
//
//		06/25/97	JMI	Got rid of use of the obsolete CSprite::InXrayable and now
//							use CSprite::InAlpha in its place.  Also, now the opaques
//							use the CSprite::InOpaque flag.
//
//		06/28/97	JMI	Changed m_sWorldXRot to m_sRealmRotX & GetWorldRotX() and
//							GetRealmRotX(), and added m_sSceneRotX & GetSceneRotX().
//							Also, now loads and saves m_sRealmRotX.
//
//		06/29/97 MJR	Modified to use new RSpry interface (replaced STL).
//
//		06/30/97 MJR	Replaced SAFE_GUI_REF with new GuiItem.h-defined macro.
//
//		07/01/97	JMI	Added m_sNumInits.  Some things in Init() should only be
//							done once (like getting the resources, adding them to
//							the scene, and allocating the smashatorium).
//
//		07/01/97	JMI	Added m_sScaleAttribHeights indicating whether to scale
//							height values gotten via the terrain map.
//
//		07/09/97	JMI	Added setting for which side view assets to use.
//
//		07/09/97	JMI	Moved m_s2dResPathIndex from CHood to CRealm b/c of order
//							issues when loading.
//
//		07/13/97	JMI	Now only attempts to allocate the smashatorium if the
//							preceding stuff in Init() succeeded.
//
//		08/03/97	JRD	Added ability to save 3d scale with hood
//
//		08/07/97	JMI	Removed call to obsoleted CScene::UpdatePipeline()
//							(CRealm::SetupPipeline() now does it all).
//
//		08/10/97	JRD	Added shadow parameters to the hood & dialogue
//
//		08/13/97 MJR	Released toolbar resources.
//
//		08/13/97	JRD	Began adding Randy's numbers to the toolbar...
//
//		08/18/97 MJR	Changed sprintf() from %lg to %g for ANSI-correctness
//							(and so it would work on the ANSI-correct mac).
//
//		08/17/97	JMI	Added handy macro for updating hood's settings to scene's
//							pipeline, SetupPipeline().
//							Also, some spots were checking if m_dScale3d was less than
//							or equal to 0 and setting it to 0.2 if it was.  This
//							allowed the value to be 0.000000000001...0.1999999999999
//							which resulted in some wierd behavior including divide-by-
//							zeroes.
//
//		08/20/97	JMI	Now can use a listbox in the EditModify() for 2D res
//							paths as well as the original multibtn.
//
//		11/25/97	JMI	Now checks for Hood SAK on HD first and then on Hoods path.
//							Also, added Browse For Hood button and logic.
//
////////////////////////////////////////////////////////////////////////////////

#include "RSPiX.h"
#include "hood.h"
#include "game.h"
#include "realm.h"
#include "pvr.h"
//#include "aica.h"
#include <kos/img.h>
#include <kmg/kmg.h>
#include <string.h>

////////////////////////////////////////////////////////////////////////////////
// Macros/types/etc.
////////////////////////////////////////////////////////////////////////////////

#define GUI_FILE_NAME "res/editor/EditHood.gui"

// IDs from loaded GUI.
#define GUI_ID_OK 1
#define GUI_ID_CANCEL 2
#define GUI_ID_BASENAME 3
#define GUI_ID_REALMXROTATE 4
#define GUI_ID_REALMYROTATE 5
#define GUI_ID_REALMZROTATE 6
#define GUI_ID_KILLSPERCENTGOAL 7
#define GUI_ID_SCENEXROTATE 8
#define GUI_ID_SCALEATTRIBHEIGHTS 9
#define GUI_ID_2DRESPATHS 10
#define GUI_ID_3DSCALE 16
#define GUI_ID_SHADOW_ANGLE 888
#define GUI_ID_SHADOW_LENGTH 889
#define GUI_ID_SHADOW_INTENSITY 890
#define GUI_ID_BROWSE 1000

////////////////////////////////////////////////////////////////////////////////
// Externs.
////////////////////////////////////////////////////////////////////////////////
extern PVR::cRenderMgr *g_pvrRenderer;

////////////////////////////////////////////////////////////////////////////////
// Variables/data
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Function prototypes
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Constructor
// (protected).
////////////////////////////////////////////////////////////////////////////////
CHood::CHood(CRealm *pRealm)
	: CThing(pRealm, CHoodID)
{
	// Initialize ptrs to resources.
	m_pTerrainMap = NULL;
	m_pLayerMap = NULL;

	int16_t i;
	for (i = 0; i < MaxLayers; i++)
	{
		m_apspryAlphas[i] = NULL;
		m_apspryOpaques[i] = NULL;
	}

	m_pTextureAmbient = nullptr;	
	m_pImgAmbient = NULL;

	m_pimEmptyBar = NULL;
	m_pimEmptyBarSelected = NULL;
	m_pimFullBar = NULL;
	m_pimFullBarSelected = NULL;
	m_pimTopBar = NULL;

	m_pimNum = NULL;
	m_pimNumLite = NULL;
	m_pimNumLow = NULL;
	m_pimNumGone = NULL;

	// Must flag resources as not yet existing
	m_bResourcesExist = false;

	// Initialize.
	strcpy(m_acBaseName, "");

	m_sSceneRotX = 30;
	m_sRealmRotX = 45;
	m_dScale3d = 1.0;

	m_sShadowAngle = 0;
	m_dShadowLength = 1.0;
	m_sShadowIntensity = 64;

	m_sScaleAttribHeights = TRUE;

	// Let realm have quick access to us.
	pRealm->m_phood = this;

	// We want a Startup() call.
	m_sCallStartup = 1;

	// No Init() calls yet.
	m_sNumInits = 0;
}

////////////////////////////////////////////////////////////////////////////////
// Destructor
// (public).
////////////////////////////////////////////////////////////////////////////////
CHood::~CHood()
{
	Kill();

	// Clear Realm's ptr.
	m_pRealm->m_phood = NULL;
}

////////////////////////////////////////////////////////////////////////////////
// Load object (should call base class version!)
////////////////////////////////////////////////////////////////////////////////
int16_t CHood::Load(		// Returns 0 if successfull, non-zero otherwise
	RFile *pFile,			// In:  File to load from
	bool bEditMode,			// In:  True for edit mode, false otherwise
	int16_t sFileCount,		// In:  File count (unique per file, never 0)
	uint32_t ulFileVersion) // In:  Version of file format to load.
{
	int16_t sResult = 0;

	// In most cases, the base class Load() should be called.
	sResult = CThing::Load(pFile, bEditMode, sFileCount, ulFileVersion);

	if (sResult == 0)
	{
		// Load object data
		switch (ulFileVersion)
		{
		default:

		case 44:
			pFile->Read(&m_sScaleAttribHeights);
			pFile->Read(&m_sRealmRotX);
			pFile->Read(&(m_pRealm->m_dKillsPercentGoal));
			pFile->Read(m_acBaseName);
			pFile->Read(&m_sSceneRotX);
			pFile->Read(&m_dScale3d);

			// Some extra safety (to avoid crashing)
			if (m_dScale3d < 0.2)
				m_dScale3d = 0.2;
			if (m_dScale3d > 5.0)
				m_dScale3d = 5.0;

			pFile->Read(&m_sShadowAngle);
			pFile->Read(&m_dShadowLength);
			pFile->Read(&m_sShadowIntensity);

			// Some extra safety (to avoid crashing)
			if (m_dShadowLength < 0.0)
				m_dShadowLength = 0.0;
			if (m_dShadowLength > 1.5)
				m_dShadowLength = 1.5;

			break;
		case 43:
		case 42:
		case 41:
		case 40:
		case 39:
			pFile->Read(&m_sScaleAttribHeights);
			pFile->Read(&m_sRealmRotX);
			pFile->Read(&(m_pRealm->m_dKillsPercentGoal));
			pFile->Read(m_acBaseName);
			pFile->Read(&m_sSceneRotX);
			pFile->Read(&m_dScale3d);

			// Some extra safety
			if (m_dScale3d < 0.2)
				m_dScale3d = 0.2;
			if (m_dScale3d > 5.0)
				m_dScale3d = 5.0;

			break;
		case 38:
		case 37:
		case 36:
		case 35:
		case 34:
		case 33:
		case 32:
		case 31:
		case 30:
			pFile->Read(&m_sScaleAttribHeights);
			pFile->Read(&m_sRealmRotX);
			pFile->Read(&(m_pRealm->m_dKillsPercentGoal));
			pFile->Read(m_acBaseName);
			pFile->Read(&m_sSceneRotX);
			break;

		case 29:
		case 28:
			int16_t sDummy2dResPathIndex; // This is no longer stored here.
			pFile->Read(&sDummy2dResPathIndex);

		case 27:
		case 26:
			pFile->Read(&m_sScaleAttribHeights);

		case 25:
		case 24:
		case 23:
			pFile->Read(&m_sRealmRotX);

		case 22:
		case 21:
		case 20:
		case 19:
		case 18:
		case 17:
		case 16:
		case 15:
			pFile->Read(&(m_pRealm->m_dKillsPercentGoal));

		case 14:
		case 13:
		case 12:
		case 11:
		case 10:
		case 9:
		case 8:
		case 7:
		case 6:
		case 5:
		case 4:
		case 3:
		case 2:
		case 1:
			pFile->Read(m_acBaseName);
			pFile->Read(&m_sSceneRotX);
			break;
		}

		// Make sure there were no file errors or format errors . . .
		if (!pFile->Error() && sResult == 0)
		{
			sResult = Init();
		}
		else
		{
			sResult = -1;
			TRACE("CHood::Load(): Error reading from file!\n");			
		}
	}
	else
	{
		TRACE("CHood::Load(): CThing::Load() failed.\n");		
	}

	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// Startup object
////////////////////////////////////////////////////////////////////////////////
int16_t CHood::Startup(void) // Returns 0 if successfull, non-zero otherwise
{
	return 0;
}

////////////////////////////////////////////////////////////////////////////////
// Shutdown object
////////////////////////////////////////////////////////////////////////////////
int16_t CHood::Shutdown(void) // Returns 0 if successfull, non-zero otherwise
{
	return 0;
}

////////////////////////////////////////////////////////////////////////////////
// Suspend object
////////////////////////////////////////////////////////////////////////////////
void CHood::Suspend(void)
{
}

////////////////////////////////////////////////////////////////////////////////
// Resume object
////////////////////////////////////////////////////////////////////////////////
void CHood::Resume(void)
{
}

////////////////////////////////////////////////////////////////////////////////
// Update object
////////////////////////////////////////////////////////////////////////////////
void CHood::Update(void)
{
}

////////////////////////////////////////////////////////////////////////////////
// Render object
////////////////////////////////////////////////////////////////////////////////
void CHood::Render(void)
{
}

////////////////////////////////////////////////////////////////////////////////
// User callback on btn released within button.
////////////////////////////////////////////////////////////////////////////////
static void BrowseBtnUp( // Returns nothing.  Called on button released in
						 // m_hot when active.
	RGuiItem *pgui)		 // this.
{		
}

////////////////////////////////////////////////////////////////////////////////
// Init the hood
////////////////////////////////////////////////////////////////////////////////
int16_t CHood::Init(void) // Returns 0 if successfull, non-zero otherwise
{
	int16_t sResult = 0;

	// If first call . . .
	if (m_sNumInits++ == 0)
	{		
		// Get resources
		sResult = GetResources();

		if (sResult == 0)
		{
			// Set the realm's pointer to point at our attribute map.  This is a
			// shortcut to make it easier and faster for objects to access the map.
			m_pRealm->m_pTerrainMap = m_pTerrainMap;
			m_pRealm->m_pLayerMap = m_pLayerMap;

			PVR::cTexture *pTexure = g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_HOOD_BG);

			// Background & patch sprites are only things on rear-most layer
			CSprite2 *pSprite2 = new CSprite2;
			pSprite2->m_sX2 = 0;
			pSprite2->m_sY2 = 0;
			pSprite2->m_sPriority = 0;		
			pSprite2->texture = pTexure;
			pSprite2->imageWidth = pTexure->GetImageWidth();
			pSprite2->imageHeight = pTexure->GetImageHeight();
			pSprite2->m_sLayer = CRealm::LayerBg;
			pSprite2->m_sInFlags = CSprite::InDeleteOnClear | CSprite::InBlitOpaque;
			backgroundSprite = pSprite2;
	
			for (int8_t index = 1; index < 4; index++)
			{	
				int8_t id;
				CSprite2 **pSpritePatch = nullptr;
				switch (index)
				{
					case 1:
						id = PVR_TEXTURE_PATCH_1;
						pSpritePatch = &backgroundPatchSprite1;
					break;
					case 2:
						id = PVR_TEXTURE_PATCH_2;
						pSpritePatch = &backgroundPatchSprite2;
					break;
					case 3:
						id = PVR_TEXTURE_PATCH_3;
						pSpritePatch = &backgroundPatchSprite3;
					break;
				}			

				if (*pSpritePatch)
				{
					delete *pSpritePatch;					
				}

				pTexure = g_pvrRenderer->m_TextureRepository.Get(id);															
				if (pTexure != nullptr)
				{
					*pSpritePatch = new CSprite2;
					(*pSpritePatch)->m_sX2 = 0;
					(*pSpritePatch)->m_sY2 = 0;
					(*pSpritePatch)->m_sPriority = 0;		
					(*pSpritePatch)->texture = pTexure;
					(*pSpritePatch)->imageWidth = pTexure->GetImageWidth();
					(*pSpritePatch)->imageHeight = pTexure->GetImageHeight();
					(*pSpritePatch)->m_sLayer = CRealm::LayerBg;
					(*pSpritePatch)->m_sInFlags = CSprite::InDeleteOnClear | CSprite::InBlitOpaque;
				}
			}

			// Attempt to load all layers . . .
			int32_t lIndex;
			// Put the contents of the various spry's onto the other layers						
			for (lIndex = 0; lIndex < MaxLayers; lIndex++)
			{
				// If this layer exists . . .
				if (m_apspryAlphas[lIndex] != NULL)
				{
					RSpry::ListOfSprites::Pointer p = m_apspryAlphas[lIndex]->m_listSprites.GetHead();
					while (p)
					{
						RSprite *pSprite = m_apspryAlphas[lIndex]->m_listSprites.GetData(p);

						CSprite2 *pSprite2 = new CSprite2;
						pSprite2->m_sX2 = pSprite->m_sX;
						pSprite2->m_sY2 = pSprite->m_sY;
						pSprite2->m_sPriority = pSprite->m_sZ;
						pSprite2->m_ImageType = RImage::BMP8;
						pSprite2->texture = pSprite->texture;
						pSprite2->imageWidth = pSprite->texture->GetImageWidth();
						pSprite2->imageHeight = pSprite->texture->GetImageHeight();					
						pSprite2->startU = pSprite->startU;
						pSprite2->startV = pSprite->startV;
						pSprite2->endU = pSprite->endU;
						pSprite2->endV = pSprite->endV;
						pSprite2->tileId = pSprite->tileId;
						pSprite2->m_sLayer = CRealm::LayerAlpha1 + (int16_t)lIndex * (CRealm::LayerAlpha2 - CRealm::LayerAlpha1);
						pSprite2->m_sInFlags = CSprite::InDeleteOnClear | CSprite::InAlpha;						
						m_pRealm->m_scene.UpdateSprite(pSprite2);	
						p = m_apspryAlphas[lIndex]->m_listSprites.GetNext(p);
					}
				}				

				// If this layer exists . . .
				if (m_apspryOpaques[lIndex] != NULL)
				{
					RSpry::ListOfSprites::Pointer p = m_apspryOpaques[lIndex]->m_listSprites.GetHead();
					while (p)
					{
						RSprite *pSprite = m_apspryOpaques[lIndex]->m_listSprites.GetData(p);

						CSprite2 *pSprite2 = new CSprite2;
						pSprite2->m_sX2 = pSprite->m_sX;
						pSprite2->m_sY2 = pSprite->m_sY;
						pSprite2->m_sPriority = pSprite->m_sZ;
						pSprite2->m_ImageType = RImage::BMP8;
						pSprite2->texture = pSprite->texture;
						pSprite2->imageWidth = pSprite->texture->GetImageWidth();
						pSprite2->imageHeight = pSprite->texture->GetImageHeight();
						pSprite2->m_sLayer = CRealm::LayerOpaque1 + (int16_t)lIndex * (CRealm::LayerOpaque2 - CRealm::LayerOpaque1);
						pSprite2->m_sInFlags = CSprite::InDeleteOnClear | CSprite::InOpaque;
						m_pRealm->m_scene.UpdateSprite(pSprite2);

						p = m_apspryOpaques[lIndex]->m_listSprites.GetNext(p);
					}
				}
			}			
		}

// Allocate the Smashatorium:
#ifdef NEW_SMASH

		// This requires success in the previous ops . . .
		if (sResult == 0)
		{
// Jon has PROMISED me that there is an m_pRealm which is ACCURATE.
#define MAX_SMASHEE_W 72 // 40	once we deal with fat objects
#define MAX_SMASHEE_H 72 // 40	once we deal with fat objects

			// Allocate the Smashatorium:  Pick tile size greater than any normal object radius...
			m_pRealm->m_smashatorium.Destroy();
			m_pRealm->m_smashatorium.Alloc(m_pRealm->GetRealmWidth(), m_pRealm->GetRealmHeight(),
										   int16_t(MAX_SMASHEE_W * m_dScale3d), // I'm assuming this scales with size
										   int16_t(MAX_SMASHEE_H * m_dScale3d));
		}

#endif
	}

	// If successful so far . . .
	if (sResult == 0)
	{
		SetupPipeline();
	}

	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// Setup pipeline to hood's specifications.
////////////////////////////////////////////////////////////////////////////////
void CHood::SetupPipeline(void) // Returns nothing.
{
	// Some extra safety (to avoid crashing)
	if (m_dScale3d < 0.2)
		m_dScale3d = 0.2;
	if (m_dScale3d > 5.0)
		m_dScale3d = 5.0;

	// Set up world transformation matrix with CHood's little tweak on view
	// angle:

	// Create scene transform with our tweak.
	RTransform transScene; // Identity.
	transScene.Rx(m_sSceneRotX);
	// Create transform to convert from scene to realm.
	RTransform transScene2Realm; // Identity.
	transScene2Realm.Rx(m_sRealmRotX - m_sSceneRotX);
	// Re-setup with tweakage and scaling.
	m_pRealm->m_scene.SetupPipeline(&transScene, &transScene2Realm, m_dScale3d);
}


////////////////////////////////////////////////////////////////////////////////
// Setup the ambient lookup texture for the hood
////////////////////////////////////////////////////////////////////////////////
void CHood::SetupAmbientTexture(void) // Returns nothing.
{	
	m_pTextureAmbient = PVR::cTextureFactory::FromRimage(m_pImgAmbient);
	pvr_poly_cxt_txr(
		&m_AmbientPolyCxt,
		PVR_LIST_OP_POLY,
		PVR_TXRFMT_PAL8BPP | PVR_TXRFMT_8BPP_PAL(PVR::cRenderMgr::DefaultPalette),
		256,
		256,
		m_pTextureAmbient->m_pTextureTiles[0]->m_pPvrTexture,
		PVR_FILTER_BILINEAR);

	pvr_poly_compile(&m_AmbientPolyHdr, &m_AmbientPolyCxt);
}


////////////////////////////////////////////////////////////////////////////////
// Kill the hood
////////////////////////////////////////////////////////////////////////////////
int16_t CHood::Kill(void) // Returns 0 if successfull, non-zero otherwise
{
	int16_t sResult = 0;

	// Free resources
	sResult = FreeResources();

	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// Helper to load and convert SPRYs.
////////////////////////////////////////////////////////////////////////////////
inline int16_t SpryLoadConv( // Returns 0 on success.
	RResMgr *presmgr,		 // In:  ResMgr to load from.
	RSpry **ppspry,			 // Out: Ptr to SPRY resource.
	char *pszFileName,		 // In:  File/Res name of .SAY file.
	RImage::Type type)		 // In:  Destination type.
{
	int16_t sRes = 0; // Assume success.

	if (rspGetResource(
			presmgr,
			pszFileName,
			ppspry) == 0)
	{
		//	rspReleaseResource(presmgr, &ppspry);
		// If conversion specified . . .
		/*	if (type != RImage::NOT_SUPPORTED)
		{
			// Convert . . .
			if ((*ppspry)->Convert(type) == 0)
			{
				// Success.
			}
			else
			{
				TRACE("SpryLoadConv(): (*ppspry)->Convert(type) failed.\n");
				sRes = -3;
			}
		}*/
	}
	else
	{
		//		TRACE("SpryLoadConv(): Failed to load SPRY \"%s\".\n", pszFileName);
		sRes = -1;
	}

	return sRes;
}

extern int wideScreenWidth;
// todo: clean up
void BgImageToVQDimensions(char *name, uint16_t *width, uint16_t *height, uint16_t *cols, uint16_t *rows)
{
	if (strcasestr(name, "afb.bmp"))
	{
		*width = 2765;
		*height = 1196;
		*cols = 11;
		*rows = 5;
		return;
	}
		
	if (strcasestr(name, "chbridge.bmp"))
	{
		*width = 1136;
		*height = 1340;
		*cols = 5;
		*rows = 6;		
		return;
	}

	if (strcasestr(name, "bridge.bmp"))
	{
		*width = 1868;
		*height = 1340;
		*cols = 8;
		*rows = 6;		
		return;
	}

	if (strcasestr(name, "consite.bmp"))
	{
		*width = 1437;
		*height = 839;
		*cols = 6;
		*rows = 4;
		return;
	}

	if (strcasestr(name, "equake.bmp"))
	{
		*width = 1783;
		*height = 802;
		*cols = 7;
		*rows = 4;
		return;
	}

	if (strcasestr(name, "ezmart.bmp"))
	{
		*width = 950;
		*height = 808;
		*cols = 4;
		*rows = 4;
		return;
	}

	if (strcasestr(name, "farm.bmp"))
	{
		*width = 1658;
		*height = 735;
		*cols = 7;
		*rows = 3;
		return;
	}

	if (strcasestr(name, "ghetto.bmp"))
	{
		*width = 1535;
		*height = 919;
		*cols = 6;
		*rows = 4;
		return;
	}

	if (strcasestr(name, "homer.bmp"))
	{
		*width = 1674;
		*height = 1054;
		*cols = 7;
		*rows = 5;
		return;
	}

	if (strcasestr(name, "hovel.bmp"))
	{
		*width = 640;
		*height = 432;
		*cols = 3;
		*rows = 2;
		return;
	}

	if (strcasestr(name, "indust.bmp"))
	{
		*width = 1958;
		*height = 1530;
		*cols = 8;
		*rows = 6;
		return;
	}

	if (strstr(name, "mine.bmp"))
	{
		*width = 1920;
		*height = 800;
		*cols = 8;
		*rows = 4;
		return;
	}

	if (strcasestr(name, "newcity.bmp"))
	{
		*width = 1388;
		*height = 1090;
		*cols = 6;
		*rows = 5;
		return;
	}

	if (strcasestr(name, "osaka.bmp"))
	{
		*width = 1894;
		*height = 1374;
		*cols = 8;
		*rows = 6;
		return;
	}

	if (strcasestr(name, "parade.bmp"))
	{
		*width = 2560;
		*height = 1703;
		*cols = 10;
		*rows = 7;
		return;
	}

	if (strcasestr(name, "park.bmp"))
	{
		*width = 2103;
		*height = 1583;
		*cols = 9;
		*rows = 7;
		return;
	}

	if (strcasestr(name, "resort.bmp"))
	{
		*width = 1800;
		*height = 1054;
		*cols = 8;
		*rows = 5;
		return;
	}

	if (strcasestr(name, "salvage.bmp"))
	{
		*width = 1902;
		*height = 1082;
		*cols = 8;
		*rows = 5;
		return;
	}

	if (strcasestr(name, "school.bmp"))
	{
		*width = 927;
		*height = 640;
		*cols = 4;
		*rows = 3;
		return;
	}

	if (strcasestr(name, "shanty.bmp"))
	{
		*width = 1094;
		*height = 762;
		*cols = 5;
		*rows = 3;
		return;
	}

	if (strcasestr(name, "skirts.bmp"))
	{
		*width = 1773;
		*height = 933;
		*cols = 7;
		*rows = 4;
		return;
	}

	if (strcasestr(name, "tokyo.bmp"))
	{
		*width = 2388;
		*height = 1492;
		*cols = 10;
		*rows = 6;
		return;
	}

	if (strcasestr(name, "trailer.bmp"))
	{
		*width = 1406;
		*height = 1101;
		*cols = 6;
		*rows = 5;
		return;
	}

	if (strcasestr(name, "train.bmp"))
	{
		*width = 1981;
		*height = 1600;
		*cols = 8;
		*rows = 7;
		return;
	}

	if (strcasestr(name, "truck.bmp"))
	{
		*width = 1753;
		*height = 1074;
		*cols = 7;
		*rows = 5;
		return;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Get all required resources
////////////////////////////////////////////////////////////////////////////////
int16_t CHood::GetResources(void) // Returns 0 if successfull, non-zero otherwise
{
	int16_t sResult = 0;
	// If all resources were already successfully loaded, then don't do this again.
	// Note that if only some of them loaded and then an error occurred, we'll
	// end up reloading all of them, even those that loaded okay.
	if (!m_bResourcesExist)
	{
		// Free any resources that might have been loaded by a previous (unsuccessfull) attempt
		FreeResources();

		char szFileName[RSP_MAX_PATH]; // Temp storage to create filenames.
		char szBasePath[RSP_MAX_PATH]; // Temp storage of file path.

		// Create the real base file path.
		sprintf(szBasePath, "hoods/%s/%s", m_acBaseName, m_acBaseName);

		// Add .ext for SAKs.
		sprintf(szFileName, "res/hoods/%s.sak", m_acBaseName);

		// Open SAK, if available . . .
		if (m_pRealm->m_resmgr.OpenSak(FullPathCD(szFileName)) == 0)
		{
			// Using SAK from HD.
		}
		else
		{
			if (m_pRealm->m_resmgr.OpenSak(FullPathHoods(szFileName)) == 0)
			{
				// Using SAK from hoods path.
			}
			else
			{
				// Using Disk, set path.
				m_pRealm->m_resmgr.SetBasePath(g_GameSettings.m_szNoSakDir);
			}
		}

		// Background filename.
		sprintf(szFileName, "%s.bmp", szBasePath);
				
		// todo: clean up
		char fullname[100];		
		PVR::cTextureFactory::UnsetResMgr();
		g_pvrRenderer->m_TextureRepository.Delete(PVR_TEXTURE_HOOD_BG);
		g_pvrRenderer->m_TextureRepository.Delete(PVR_TEXTURE_PATCH_1);
		g_pvrRenderer->m_TextureRepository.Delete(PVR_TEXTURE_PATCH_2);
		g_pvrRenderer->m_TextureRepository.Delete(PVR_TEXTURE_PATCH_3);
		BgImageToVQDimensions(szFileName, &bgWidth, &bgHeight, &bgCols, &bgRows);
		sprintf(fullname, "/cd/res/vqtextures/%s", szFileName);
		
		PVR::cTexture *pBackgroundTexture = g_pvrRenderer->m_TextureRepository.Add(PVR_TEXTURE_HOOD_BG, PVR::cTextureFactory::FromKmg(fullname, bgCols, bgRows, bgWidth, bgHeight));

		if (g_pvrRenderer->m_pPaletteDefault)
		{
			delete g_pvrRenderer->m_pPaletteDefault;
		}
		
		char palname[100];
		sprintf(palname, "%s/pal.pal", fullname);		
		g_pvrRenderer->m_pPaletteDefault = new RPal;
		g_pvrRenderer->m_pPaletteDefault->Load(palname);
	
		// Attempt to load all layers . . .
		int32_t lIndex;
		int16_t sNumAlphaLayersLoaded = 0;
		int16_t sNumOpaqueLayersLoaded = 0;
		
		for (lIndex = 0; lIndex < MaxLayers; lIndex++)
		{
			// Make alpha layer name.
			sprintf(szFileName, "%s%02da.say", szBasePath, lIndex);
			// Load & convert . . .
			if (SpryLoadConv(&(m_pRealm->m_resmgr), m_apspryAlphas + lIndex, szFileName, RImage::BMP8) == 0)
			{
				sNumAlphaLayersLoaded++;
			}

			// Make opaque layer name.
			sprintf(szFileName, "%s%02do.say", szBasePath, lIndex);
			// Load & convert . . .
			if (SpryLoadConv(&(m_pRealm->m_resmgr), m_apspryOpaques + lIndex, szFileName, RImage::BMP8) == 0)
			{
				sNumOpaqueLayersLoaded++;
			}
		}

		// If no layers loaded . . .
		if (sNumOpaqueLayersLoaded == 0 && sNumAlphaLayersLoaded == 0)
		{
			TRACE("GetResources(): No alpha or opaque layers successfully loaded.\n");
		}
		else
		{
			TRACE("GetResources(): Successfully loaded %hd Alpha layer%s and %hd Opaque layer%s.\n",
				  sNumAlphaLayersLoaded, (sNumAlphaLayersLoaded == 1 ? "" : "s"),
				  sNumOpaqueLayersLoaded, (sNumOpaqueLayersLoaded == 1 ? "" : "s"));
		}

		sprintf(szFileName, "%s.mp1", szBasePath);

		// Load new multi layer attribute maps
		if (rspGetResource(
				&(m_pRealm->m_resmgr),
				szFileName,
				&m_pTerrainMap) != 0)
		{
			sResult = -1;
			TRACE("CHood::GetResources(): Couldn't load attribute map %s\n", szFileName);
			goto Error;
		}

		sprintf(szFileName, "%s.mp2", szBasePath);

		if (rspGetResource(
				&(m_pRealm->m_resmgr),
				szFileName,
				&m_pLayerMap) != 0)
		{
			sResult = -1;
			TRACE("CHood::GetResources(): Couldn't load attribute map %s\n", szFileName);
			goto Error;
		}
			
		sprintf(szFileName, "%s.ambient.bmp", szBasePath);		
		if (rspGetResource(
				&(m_pRealm->m_resmgr),
				szFileName,
				&m_pImgAmbient) != 0)
		{
			sResult = -1;
			TRACE("GetResources(): Failed to load: %s.\n", szFileName);			
		}
		
		// Attempt to load patch sprites
		PVR::cTextureFactory::SetResMgr(&(m_pRealm->m_resmgr));
		for (int8_t index = 1; index < 4; index++)
		{	
			sprintf(szFileName, "hoods/%s/patch%d.bmp", m_acBaseName, index);
			PVR::cTexture *pTexture = PVR::cTextureFactory::FromRimage(szFileName);
			if (pTexture != nullptr)
			{
				switch (index)
				{
				case 1:
					g_pvrRenderer->m_TextureRepository.Add(PVR_TEXTURE_PATCH_1, pTexture);
				break;
				case 2:
					g_pvrRenderer->m_TextureRepository.Add(PVR_TEXTURE_PATCH_2, pTexture);
				break;
				case 3:
					g_pvrRenderer->m_TextureRepository.Add(PVR_TEXTURE_PATCH_3, pTexture);
				break;
				}			
			}
		}
		PVR::cTextureFactory::UnsetResMgr();

		// Resources now exist
		m_bResourcesExist = true;
	}

Error:

	// Close SAK, if open.
	m_pRealm->m_resmgr.CloseSak();

	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// Free all resources
////////////////////////////////////////////////////////////////////////////////
int16_t CHood::FreeResources(void) // Returns 0 if successfull, non-zero otherwise
{
	int16_t sResult = 0;

	// Don't check whether resources exist!  Even if they were only partially
	// loaded (due to an error during the load process) we still want to clear
	// those resources that were loaded.  The stuff being done here is safe
	// regardless of whether or not the resource was actually loaded.

	//todo release bg texture

	int32_t lIndex;
	
	bool purge = m_pRealm->m_flags.bPurgeResOnShutdown;
	purge = false;
	for (lIndex = 0; lIndex < MaxLayers; lIndex++)
	{
		if (purge)
		{
			if (m_apspryAlphas[lIndex] != NULL)
				rspReleaseAndPurgeResource(&(m_pRealm->m_resmgr), &(m_apspryAlphas[lIndex]));

			if (m_apspryOpaques[lIndex] != NULL)
				rspReleaseAndPurgeResource(&(m_pRealm->m_resmgr), &(m_apspryOpaques[lIndex]));

			if (m_pTerrainMap != NULL)
			{
				rspReleaseAndPurgeResource(&(m_pRealm->m_resmgr), &m_pTerrainMap);
				// Clear Realm's map ptr
				m_pRealm->m_pTerrainMap = NULL;
			}

			if (m_pLayerMap != NULL)
			{
				rspReleaseAndPurgeResource(&(m_pRealm->m_resmgr), &m_pLayerMap);
				// Clear Realm's map ptr
				m_pRealm->m_pLayerMap = NULL;
			}
			
			if (m_pImgAmbient)
			{				
				rspReleaseAndPurgeResource(&(m_pRealm->m_resmgr), &m_pImgAmbient);
			}

			if (m_pimEmptyBar)
				rspReleaseAndPurgeResource(&(m_pRealm->m_resmgr), &m_pimEmptyBar);

			if (m_pimEmptyBarSelected)
				rspReleaseAndPurgeResource(&(m_pRealm->m_resmgr), &m_pimEmptyBarSelected);
			if (m_pimFullBar)
				rspReleaseAndPurgeResource(&(m_pRealm->m_resmgr), &m_pimFullBar);
			if (m_pimFullBarSelected)
				rspReleaseAndPurgeResource(&(m_pRealm->m_resmgr), &m_pimFullBarSelected);
			if (m_pimTopBar)
				delete m_pimTopBar;			
		}
		else
		{
			if (m_apspryAlphas[lIndex] != NULL)
				rspReleaseResource(&(m_pRealm->m_resmgr), &(m_apspryAlphas[lIndex]));

			if (m_apspryOpaques[lIndex] != NULL)
				rspReleaseResource(&(m_pRealm->m_resmgr), &(m_apspryOpaques[lIndex]));

			if (m_pTerrainMap != NULL)
			{
				rspReleaseResource(&(m_pRealm->m_resmgr), &m_pTerrainMap);
				// Clear Realm's map ptr
				m_pRealm->m_pTerrainMap = NULL;
			}

			if (m_pLayerMap != NULL)
			{
				rspReleaseResource(&(m_pRealm->m_resmgr), &m_pLayerMap);
				// Clear Realm's map ptr
				m_pRealm->m_pLayerMap = NULL;
			}

			if (m_pImgAmbient)
			{			
				rspReleaseResource(&(m_pRealm->m_resmgr), &m_pImgAmbient);
			}

			if (m_pimEmptyBar)
				rspReleaseResource(&(m_pRealm->m_resmgr), &m_pimEmptyBar);

			if (m_pimEmptyBarSelected)
				rspReleaseResource(&(m_pRealm->m_resmgr), &m_pimEmptyBarSelected);
			if (m_pimFullBar)
				rspReleaseResource(&(m_pRealm->m_resmgr), &m_pimFullBar);
			if (m_pimFullBarSelected)
				rspReleaseResource(&(m_pRealm->m_resmgr), &m_pimFullBarSelected);
			if (m_pimTopBar)
				delete m_pimTopBar;
		}
	}

	if (m_pTextureAmbient != nullptr)
	{	
		delete m_pTextureAmbient;
	}

	if (backgroundSprite != nullptr)
	{
		delete backgroundSprite;
	}

	if (backgroundPatchSprite1 != nullptr)
	{
		delete backgroundPatchSprite1;
	}

	if (backgroundPatchSprite2 != nullptr)
	{
		delete backgroundPatchSprite2;
	}

	if (backgroundPatchSprite3 != nullptr)
	{
		delete backgroundPatchSprite3;
	}

	// Resources no longer exist for sure.
	m_bResourcesExist = false;

	// Let's get these out of memory right away.
	m_pRealm->m_resmgr.Purge();


	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// Set the hood's palette.
////////////////////////////////////////////////////////////////////////////////
void CHood::SetPalette(void) // Returns nothing.
{
	//todo: clean up
	ASSERT(g_pvrRenderer->m_pPaletteDefault != NULL);
	ASSERT(g_pvrRenderer->m_pPaletteDefault->m_type == RPal::PDIB);

	rspSetPaletteEntries(
		0,							  //10,
		256,						  // 236,
		g_pvrRenderer->m_pPaletteDefault->Red(0),	  //10
		g_pvrRenderer->m_pPaletteDefault->Green(0), //10
		g_pvrRenderer->m_pPaletteDefault->Blue(0),  //10
		g_pvrRenderer->m_pPaletteDefault->m_sPalEntrySize);

	// This part could be left up to the caller...?  should it be?
	rspUpdatePalette();	
}

////////////////////////////////////////////////////////////////////////////////
// EOF
////////////////////////////////////////////////////////////////////////////////
