// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010  Winch Gate Property Limited
//
// This source file has been modified by the following contributors:
// Copyright (C) 2014-2019  Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "stdafx.h"
#include "resource.h"
#include "View.h"
#include "SelectionTerritoire.h"
#include "select_rotation.h"
#include "Browse.h"
#include "popup.h"
#include <direct.h>
//#include "ListGroup.h"
//#include "ViewPopup.h"
//#include "pic/readpic.h"

using namespace std;
using namespace NL3D;

extern CTileBank tileBank2;

BEGIN_MESSAGE_MAP(CTView, CStatic)
	//{{AFX_MSG_MAP(CTView)
	ON_WM_DROPFILES()
	ON_WM_PAINT()
	ON_WM_LBUTTONDBLCLK()
	ON_WM_LBUTTONDOWN()
	ON_WM_RBUTTONDOWN()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

CFont *normal_font = NULL;
//int SortTile = 0; int sortMode = 1; //or, and
__int64 flagGroupSort = 0; int showNULL = 0;
//ViewPopup *theViewPopup = 0;

bool zouille ()
{
	return (GetAsyncKeyState(VK_F2)&(1<<15)) != 0;
};

// Rotate a buffer
void rotateBuffer (uint &Width, uint &Height, std::vector<NLMISC::CBGRA>& Tampon)
{
	// Make a copy
	std::vector<NLMISC::CBGRA> copy=Tampon;

	// Rotate
	for (uint y=0; y<Width; y++)
	{
		// Line offset
		uint tmp=y*Width;
		for (uint x=0; x<Width; x++)
		{
			Tampon[y+(Width-x-1)*Height]=copy[x+tmp];
		}
	}

	// New size
	uint tmp=Width;
	Width=Height;
	Height=tmp;
}

static bool loadPic(const string &path, std::vector<NLMISC::CBGRA> &tampon, uint &width, uint &height)
{
	try
	{
		NLMISC::CIFile file;
		if (file.open(path.c_str()))
		{
			NLMISC::CBitmap bitmap;
			bitmap.load(file);
			width = bitmap.getWidth();
			height = bitmap.getHeight();
			tampon.resize(width * height);
			bitmap.convertToType(NLMISC::CBitmap::RGBA);
			for (uint y = 0; y < height; ++y)
			{
				for (uint x = 0; x < width; ++x)
				{
					NLMISC::CRGBA c = bitmap.getPixelColor(x, y, 0);
					c.R = (c.R * c.A) / 255;
					c.G = (c.G * c.A) / 255;
					c.B = (c.B * c.A) / 255;
					tampon[(y * width) + x] = c;
				}
			}
			return true;
		}
	}
	catch (const NLMISC::Exception& ) { }
	return false;
}

/////////////////////////////////////////////////////////////////////////////
// CTView
//Attention : windows veut que le buffer image commence du bas vers le haut
int _LoadBitmap(const std::string& path,LPBITMAPINFO BitmapInfo, std::vector<NLMISC::CBGRA>&Tampon, std::vector<NLMISC::CBGRA>* Alpha, int rot)
{
	//vector<NLMISC::CBGRA> Tampon;
	uint Width;
	uint Height;
	if (loadPic(path, Tampon, Width, Height))
	{
		BitmapInfo->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
		BitmapInfo->bmiHeader.biWidth=Width;
		BitmapInfo->bmiHeader.biHeight=-(int)Height;
		BitmapInfo->bmiHeader.biPlanes=1;
		BitmapInfo->bmiHeader.biBitCount=32;
		BitmapInfo->bmiHeader.biCompression=BI_RGB;
		BitmapInfo->bmiHeader.biSizeImage=0;
		BitmapInfo->bmiHeader.biXPelsPerMeter=1;
		BitmapInfo->bmiHeader.biYPelsPerMeter=1;
		BitmapInfo->bmiHeader.biClrUsed=0;
		BitmapInfo->bmiHeader.biClrImportant=0;
		BitmapInfo->bmiColors->rgbBlue=0;
		BitmapInfo->bmiColors->rgbRed=0;
		BitmapInfo->bmiColors->rgbGreen=0;

		while (rot)
		{
			// Rotate the buffer
			rotateBuffer (Width, Height, Tampon);
			rot--;
		}

		if ((Alpha)&&(Alpha->size()==Tampon.size()))
		{
			// Pre mul RGB componates by Alpha one
			int nPixelCount=(int)(Width*Height);
			for (int p=0; p<nPixelCount; p++)
			{
				// Invert alpha ?
				int alpha=(*Alpha)[p].A;
				Tampon[p].R=(uint8)(((int)Tampon[p].R*alpha)>>8);
				Tampon[p].G=(uint8)(((int)Tampon[p].G*alpha)>>8);
				Tampon[p].B=(uint8)(((int)Tampon[p].B*alpha)>>8);
			}
		}

		return 1;
	}
	else
		return 0;
}






//TileInfo
TileInfo::TileInfo()
{
	nightLoaded = 0;
	alphaLoaded = 0;
	loaded = 0;
	Selected = 0; 
}



//TileList
TileList::TileList()
{
	last_id = 0; 
	_tileSet = -1;

	// Add 48 transitions
	int i;
	for (i=0; i<CTileSet::count; i++)
	{
		TileInfo info;
		info.id = i;
	
		theListTransition.push_back (info);
	}

	// Add 16 displacements
	for (i=0; i<CTileSet::CountDisplace; i++)
	{
		TileInfo info;
		info.id = i;
	
		theListDisplacement.push_back (info);
	}
}


int TileList::addTile128 ()
{

	int index;
	tileBank2.getTileSet (_tileSet)->addTile128 (index, tileBank2);
	nlassert (index==(sint)theList128.size());

	TileInfo info;
	info.id = index;
	theList128.push_back (info);

	return index;
}

int TileList::addTile256 ()
{
	int index;
	tileBank2.getTileSet (_tileSet)->addTile256 (index, tileBank2);
	nlassert (index==(sint)theList256.size());

	TileInfo info;
	info.id = index;
	theList256.push_back (info);

	return index;
}

bool RemovePath (std::string& path, const std::string &absolutePathToRemplace);

int TileList::setTile128 (int tile, const std::string& name, NL3D::CTile::TBitmap type)
{
	// Remove the absolute path from the path name
	std::string troncated=name;
	if (RemovePath (troncated, tileBank2.getAbsPath ().c_str()))
	{
		vector<NLMISC::CBGRA> tampon;
		uint Width;
		uint Height;
		if (!loadPic(tileBank2.getAbsPath ()+troncated, tampon, Width, Height))
		{
			std::string msg = tileBank2.getAbsPath() + troncated + "\nContinue ?";

			return (int)(MessageBox(NULL, nlUtf8ToTStr(msg), _T("Can't load bitmap."), MB_YESNO | MB_ICONEXCLAMATION) == IDYES);
		}
		else
		{
			CTileBorder border;
			border.set (Width, Height, tampon);

			CTileSet::TError error;
			int pixel=-1;
			int composante=4;
			if (type == CTile::additive)
				error=CTileSet::ok;
			else
				error=tileBank2.getTileSet(_tileSet)->checkTile128 (type, border, pixel, composante);
			if ((error!=CTileSet::ok)&&(error!=CTileSet::addFirstA128128)&&!zouille ())
			{
				static const char* comp[]={"Red", "Green", "Blue", "Alpha", ""};
				std::string msg = NLMISC::toString("%s\nPixel: %d (%s)\nContinue ?", CTileSet::getErrorMessage (error), pixel, comp[composante]);

				return (int)(MessageBox(NULL, nlUtf8ToTStr(msg), _T("Can't add tile"), MB_YESNO | MB_ICONEXCLAMATION) == IDYES);
			}
			else
			{
				if (error==CTileSet::addFirstA128128)
					tileBank2.getTileSet(_tileSet)->setBorder (type, border);

				tileBank2.getTileSet(_tileSet)->setTile128 (tile, troncated, type, tileBank2);
				switch (type)
				{
				case CTile::diffuse:
					theList128[tile].loaded=0;
					break;
				case CTile::additive:
					theList128[tile].nightLoaded=0;
					break;
				case CTile::alpha:
					theList128[tile].alphaLoaded=0;
					break;
				}
				theList128[tile].Load (tileBank2.getTileSet(_tileSet)->getTile128(tile), NULL);
			}
		}
	}
	else
	{
		// Error: bitmap not in the absolute path..
		std::string msg = NLMISC::toString("The bitmap %s is not in the absolute path %s.\nContinue ?", name.c_str(), tileBank2.getAbsPath ().c_str());
		return (int)(MessageBox(NULL, nlUtf8ToTStr(msg), _T("Load error"), MB_YESNO | MB_ICONEXCLAMATION) == IDYES);
	}

	return 1;
}

int TileList::setTile256 (int tile, const std::string& name, NL3D::CTile::TBitmap type)
{
	// Remove the absolute path from the path name
	std::string troncated=name;
	if (RemovePath (troncated, tileBank2.getAbsPath ().c_str()))
	{
		vector<NLMISC::CBGRA> tampon;
		uint Width;
		uint Height;
		if (!loadPic(tileBank2.getAbsPath ()+troncated, tampon, Width, Height))
		{
			std::string msg = tileBank2.getAbsPath() + troncated + "\nContinue ?";
			return (int)(MessageBox(NULL, nlUtf8ToTStr(msg), _T("Can't load bitmap."), MB_YESNO | MB_ICONEXCLAMATION) == IDYES);
		}
		else
		{
			CTileBorder border;
			border.set (Width, Height, tampon);
			
			CTileSet::TError error;
			int pixel=-1;
			int composante=4;

			if (type == CTile::additive)
				error=CTileSet::ok;
			else
				error=tileBank2.getTileSet(_tileSet)->checkTile256 (type, border, pixel, composante);
			if ((error!=CTileSet::ok)&&!zouille())
			{
				static const char* comp[] = { "Red", "Green", "Blue", "Alpha", "" };
				std::string msg = NLMISC::toString("%s\nPixel: %d (%s)\nContinue ?", CTileSet::getErrorMessage (error), pixel, comp[composante]);
				return (int)(MessageBox(NULL, nlUtf8ToTStr(msg), _T("Can't add tile"), MB_YESNO | MB_ICONEXCLAMATION) == IDYES);
			}
			else
			{
				tileBank2.getTileSet(_tileSet)->setTile256 (tile, troncated, type, tileBank2);
						switch (type)
				{
				case CTile::diffuse:
					theList256[tile].loaded=0;
					break;
				case CTile::additive:
					theList256[tile].nightLoaded=0;
					break;
				case CTile::alpha:
					theList256[tile].alphaLoaded=0;
					break;
				}
				theList256[tile].Load (tileBank2.getTileSet(_tileSet)->getTile256(tile), NULL);
			}
		}
	}
	else
	{
		// Error: bitmap not in the absolute path..
		std::string msg = NLMISC::toString("The bitmap %s is not in the absolute path %s.\nContinue ?", name.c_str(), tileBank2.getAbsPath ().c_str());
		return (int)(MessageBox(NULL, nlUtf8ToTStr(msg), _T("Load error"), MB_YESNO | MB_ICONEXCLAMATION) == IDYES);
	}

	return 1;
}

int TileList::setTileTransition (int tile, const std::string& name, NL3D::CTile::TBitmap type)
{
	// Remove the absolute path from the path name
	std::string troncated=name;
	if (RemovePath (troncated, tileBank2.getAbsPath ().c_str()))
	{
		// No alpha, use setTileTransitionAlpha
		nlassert (CTile::alpha!=type);

		vector<NLMISC::CBGRA> tampon;
		uint Width;
		uint Height;
		if (!loadPic(tileBank2.getAbsPath ()+troncated, tampon, Width, Height))
		{
			std::string msg = tileBank2.getAbsPath() + troncated + "\nContinue ?";
			return (int)(MessageBox(NULL, nlUtf8ToTStr(msg), _T("Can't load bitmap."), MB_YESNO | MB_ICONEXCLAMATION) == IDYES);
		}
		else
		{
			CTileBorder border;
			border.set (Width, Height, tampon);
			
			CTileSet::TError error;
			int pixel=-1;
			int composante=4;
			if (type == CTile::additive)
				error=CTileSet::ok;
			else
				error=tileBank2.getTileSet(_tileSet)->checkTile128 (type, border, pixel, composante);
			if ((error!=CTileSet::ok)&&(error!=CTileSet::addFirstA128128)&&!zouille ())
			{
				static const char* comp[]={"Red", "Green", "Blue", "Alpha", ""};
				std::string msg = NLMISC::toString("%s\nPixel: %d (%s)\nContinue ?", CTileSet::getErrorMessage(error), pixel, comp[composante]);
				return MessageBox(NULL, nlUtf8ToTStr(msg), _T("Can't add tile"), MB_YESNO | MB_ICONEXCLAMATION) == IDYES;
			}
			else
			{
				if (error==CTileSet::addFirstA128128)
					tileBank2.getTileSet(_tileSet)->setBorder (type, border);
				tileBank2.getTileSet(_tileSet)->setTileTransition ((CTileSet::TTransition)tile, troncated, type, tileBank2, border);
				switch (type)
				{
				case CTile::diffuse:
					theListTransition[tile].loaded=0;
					break;
				case CTile::additive:
					theListTransition[tile].nightLoaded=0;
					break;
				case CTile::alpha:
					theListTransition[tile].alphaLoaded=0;
					break;
				}
				theListTransition[tile].Load (tileBank2.getTileSet(_tileSet)->getTransition(tile)->getTile(), 
					&theListTransition[tile].alphaBits);
			}
		}
	}
	else
	{
		// Error: bitmap not in the absolute path..
		std::string msg = NLMISC::toString("The bitmap %s is not in the absolute path %s.", name.c_str(), tileBank2.getAbsPath ().c_str());
		MessageBox(NULL, nlUtf8ToTStr(msg), _T("Load error"), MB_OK | MB_ICONEXCLAMATION);
	}

	return 1;
}

int TileList::setDisplacement (int tile, const std::string& name)
{
	// Remove the absolute path from the path name
	std::string troncated=name;
	if (RemovePath (troncated, tileBank2.getAbsPath ().c_str()))
	{
		// load it
		if (!troncated.empty())
		{
			// not loaded
			theListDisplacement[tile].loaded=0;

			if (!_LoadBitmap(tileBank2.getAbsPath() + troncated, &theListDisplacement[tile].BmpInfo, theListDisplacement[tile].Bits, NULL, 0))
			{
				std::string msg = tileBank2.getAbsPath() + troncated;
				MessageBox(NULL, nlUtf8ToTStr(msg), _T("Can't load file"), MB_OK | MB_ICONEXCLAMATION);
			}
			else
			{
				// Check the size
				if ((theListDisplacement[tile].BmpInfo.bmiHeader.biWidth!=32)||(-theListDisplacement[tile].BmpInfo.bmiHeader.biHeight!=32))
				{
					// Error message
					MessageBox(NULL, _T("Invalid size: displacement map must be 32x32 8 bits."), nlUtf8ToTStr(troncated),
						MB_OK|MB_ICONEXCLAMATION);

					// Free the bitmap
					theListDisplacement[tile].Bits.resize (0);
				}
				else
				{
					// Loaded
					theListDisplacement[tile].loaded=1;

					// change the file name of the displacement map
					tileBank2.getTileSet(_tileSet)->setDisplacement ((CTileSet::TDisplacement)tile, troncated, tileBank2);
				}
			}
		}
	}
	else
	{
		// Error: bitmap not in the absolute path..
		std::string msg = NLMISC::toString("The bitmap %s is not in the absolute path %s.", name.c_str(), tileBank2.getAbsPath ().c_str());
		MessageBox(NULL, nlUtf8ToTStr(msg), _T("Load error"), MB_OK | MB_ICONEXCLAMATION);
	}

	return 1;
}

int TileList::setTileTransitionAlpha (int tile, const std::string& name, int rot)
{
	// Remove the absolute path from the path name
	std::string troncated=name;
	if (RemovePath (troncated, tileBank2.getAbsPath ().c_str()))
	{
		vector<NLMISC::CBGRA> tampon;
		uint Width;
		uint Height;
		if (!loadPic(tileBank2.getAbsPath ()+troncated, tampon, Width, Height))
		{
			std::string msg = tileBank2.getAbsPath() + troncated + "\nContinue ?";
			return MessageBox(NULL, nlUtf8ToTStr(msg), _T("Can't load bitmap."), MB_YESNO | MB_ICONEXCLAMATION) == IDYES;
		}
		else
		{
			CTileBorder border;
			border.set (Width, Height, tampon);

			// rotate the border
			int rotBis=rot;
			while (rotBis)
			{
				border.rotate ();
				rotBis--;
			}
			
			CTileSet::TError error;
			int indexError;
			int pixel=-1;
			int composante=4;
			if (((error=tileBank2.getTileSet(_tileSet)->checkTileTransition ((CTileSet::TTransition)tile, CTile::alpha, border, indexError,
				pixel, composante))!=CTileSet::ok)&&!zouille ())
			{
				std::string msg;
				if ((error==CTileSet::topInterfaceProblem)||(error==CTileSet::bottomInterfaceProblem)||(error==CTileSet::leftInterfaceProblem)
					||(error==CTileSet::rightInterfaceProblem)||(error==CTileSet::topBottomNotTheSame)||(error==CTileSet::rightLeftNotTheSame)
					||(error==CTileSet::topInterfaceProblem))
				{
					static const char* comp[]={"Red", "Green", "Blue", "Alpha", ""};
					if (indexError!=-1)
						msg = NLMISC::toString("%s\nIncompatible with tile nb %d\nPixel: %d (%s)", CTileSet::getErrorMessage (error), indexError,
							pixel, comp[composante]);
					else
						msg = NLMISC::toString("%s\nIncompatible with the 128x128 tile\nPixel: %d (%s)", CTileSet::getErrorMessage (error),
							pixel, comp[composante]);
				}
				else
				{
					msg = NLMISC::toString("%s\nIncompatible filled tile", CTileSet::getErrorMessage(error));
				}

				msg += "\nContinue ?";
				
				return MessageBox(NULL, nlUtf8ToTStr(msg), _T("Can't add tile"), MB_YESNO | MB_ICONEXCLAMATION) == IDYES;
			}
			else
			{
				tileBank2.getTileSet(_tileSet)->setTileTransitionAlpha ((CTileSet::TTransition)tile, troncated, tileBank2, border, rot);
				theListTransition[tile].alphaLoaded=0;
				theListTransition[tile].Load (tileBank2.getTileSet(_tileSet)->getTransition(tile)->getTile(), NULL);
			}
		}
	}
	else
	{
		// Error: bitmap not in the absolute path..
		TCHAR msg[512];
		_stprintf(msg, _T("The bitmap %s is not in the absolute path %s."), nlUtf8ToTStr(name), nlUtf8ToTStr(tileBank2.getAbsPath()));
		MessageBox (NULL, msg, _T("Load error"), MB_OK|MB_ICONEXCLAMATION);
	}

	return 1;
}

void TileList::removeTile128 (int index)
{
	tileBank2.getTileSet (_tileSet)->removeTile128 (index, tileBank2);
	theList[0].erase (theList[0].begin()+index);
	for (int i=0; i<(sint)theList[0].size(); i++)
	{
		theList[0][i].id=i;
	}
}

void TileList::removeTile256 (int index)
{
	tileBank2.getTileSet (_tileSet)->removeTile256 (index, tileBank2);
	theList[1].erase (theList[1].begin()+index);
	for (int i=0; i<(sint)theList[1].size(); i++)
	{
		theList[1][i].id=i;
	}
}

void TileList::clearTile128 (int index, CTile::TBitmap bitmap)
{
	switch (bitmap)
	{
	case CTile::diffuse:
		theList128[index].loaded=0;
		theList128[index].Bits.resize(0);
		break;
	case CTile::additive:
		theList128[index].nightLoaded=0;
		theList128[index].nightBits.resize(0);
		break;
	case CTile::alpha:
		theList128[index].alphaLoaded=0;
		theList128[index].alphaBits.resize(0);
		break;
	}
	tileBank2.getTileSet (_tileSet)->clearTile128 (index, bitmap, tileBank2);
}

void TileList::clearTile256 (int index, CTile::TBitmap bitmap)
{
	switch (bitmap)
	{
	case CTile::diffuse:
		theList256[index].loaded=0;
		theList256[index].Bits.resize(0);
		break;
	case CTile::additive:
		theList256[index].nightLoaded=0;
		theList256[index].nightBits.resize(0);
		break;
	case CTile::alpha:
		theList256[index].alphaLoaded=0;
		theList256[index].alphaBits.resize(0);
		break;
	}
	tileBank2.getTileSet (_tileSet)->clearTile256 (index, bitmap, tileBank2);
}

void TileList::clearTransition (int index, CTile::TBitmap bitmap)
{
	switch (bitmap)
	{
	case CTile::diffuse:
		theListTransition[index].loaded=0;
		theListTransition[index].Bits.resize(0);
		break;
	case CTile::additive:
		theListTransition[index].nightLoaded=0;
		theListTransition[index].nightBits.resize(0);
		break;
	case CTile::alpha:
		theListTransition[index].alphaLoaded=0;
		theListTransition[index].alphaBits.resize(0);
		break;
	}
	tileBank2.getTileSet (_tileSet)->clearTransition ((CTileSet::TTransition)index, bitmap, tileBank2);
}

void TileList::clearDisplacement (int index)
{
	// Clear the displacement map filename
	tileBank2.getTileSet (_tileSet)->clearDisplacement ((CTileSet::TDisplacement)index, tileBank2);
	theListDisplacement[index].loaded=0;
	theListDisplacement[index].Bits.resize(0);
}

tilelist::iterator TileList::GetFirst(int n)
{
	//UpdateLF();
	return theList[n].begin();
}

tilelist::iterator TileList::GetLast(int n)
{
	//UpdateLF();
	return theList[n].end();
}

int TileList::GetSize(int n)
{
	//UpdateLF();
	return (int)theList[n].size();
}

void TileInfo::Delete ()
{
	loaded=0;
	nightLoaded=0;
	alphaLoaded=0;
}


tilelist::iterator TileList::Get(int i, int n)
{
	return theList[n].begin()+i;
}

const std::string& TileInfo::getRelativeFileName (CTile::TBitmap type, int index)
{
	return tileBank2.getTile (index)->getRelativeFileName (type);
}

bool TileInfo::Load (int index, std::vector<NLMISC::CBGRA>* Alpha)
{
	bool bRes=true;
	if (!loaded && !getRelativeFileName (CTile::diffuse, index).empty())
	{
		if (!_LoadBitmap(tileBank2.getAbsPath() + getRelativeFileName (CTile::diffuse, index), &BmpInfo, Bits, Alpha, 0))
		{
			bRes=false;
			MessageBox(NULL, nlUtf8ToTStr(tileBank2.getAbsPath() + getRelativeFileName(CTile::diffuse, index)), _T("Can't load file"), MB_OK | MB_ICONEXCLAMATION);
		}
		else
			loaded=1;
	}
	if (!nightLoaded && !getRelativeFileName (CTile::additive, index).empty())
	{
		if (!_LoadBitmap(tileBank2.getAbsPath() + getRelativeFileName (CTile::additive, index), &nightBmpInfo, nightBits, Alpha, 0))
		{
			bRes=false;
			MessageBox(NULL, nlUtf8ToTStr(tileBank2.getAbsPath() + getRelativeFileName(CTile::additive, index)), _T("Can't load file"), MB_OK | MB_ICONEXCLAMATION);
		}
		else
			nightLoaded=1;
	}
	if (!alphaLoaded && !getRelativeFileName (CTile::alpha, index).empty())
	{
		if (!_LoadBitmap(tileBank2.getAbsPath() + getRelativeFileName (CTile::alpha, index), &alphaBmpInfo, alphaBits, NULL, 
			tileBank2.getTile (index)->getRotAlpha ()))
		{
			bRes=false;
			MessageBox(NULL, nlUtf8ToTStr(tileBank2.getAbsPath() + getRelativeFileName(CTile::alpha, index)), _T("Can't load file"), MB_OK | MB_ICONEXCLAMATION);
		}
		else
			alphaLoaded=1;
	}
	return bRes;
}

void TileList::Reload(int first, int count, int n) //recharge en memoire une tranche de tiles
{
	for (int i=first; i<count; i++)
	{
		switch (n)
		{
		case 0:
			theList[n][i].Load (tileBank2.getTileSet(_tileSet)->getTile128 (i), NULL);
			break;
		case 1:
			theList[n][i].Load (tileBank2.getTileSet(_tileSet)->getTile256 (i), NULL);
			break;
		case 2:
			{
				int index=tileBank2.getTileSet(_tileSet)->getTransition (i)->getTile();
				if (index!=-1)
					theList[n][i].Load (index, &theListTransition[i].alphaBits);
			}
			break;
		}
	}
}

//CTView
CTView::CTView()
{
	sizetile_x = SIZE_SMALL; sizetile_y = SIZE_SMALL;
	sizetext_y = 14;
	spacing_x = SPACING_SMALL_X; spacing_y = SPACING_SMALL_Y; 
	Zoom=3; Texture = 1; Sort = 1; InfoTexte = 1;
	count_ = 0; ViewTileMode = 0;
	scrollpos = 0; lastVBarPos = 0;
	smEdgeList = 0; bPopup = 0;
	//theViewPopup = 0;
}

CTView::~CTView()
{
}

void CTView::Init(int _land, int n)
{	
	InfoList._tileSet=_land;
	UpdateSize(n);
	pipo_buffer = (void *)new char[sizetile_x * sizetile_y * 3];
	bmp = new CBitmap;
	bmp->CreateBitmap(sizetile_x,sizetile_y,1,24,pipo_buffer);
	pImList = new CImageList;
	pImList->Create(sizetile_x,sizetile_y,ILC_COLOR24,0,1);
	pImList->Add(bmp,(CBitmap*)NULL);
	TCHAR *defautpath = ((SelectionTerritoire*)GetParent()->GetParent())->DefautPath.GetBuffer(256);
	count_=1;
}

void CTView::Delete()
{
	count_=0; pImList = 0;
}

int CTView::GetNbTileLine(void)
{
	RECT rect; GetClientRect(&rect);
	return ( max ( 1, int( rect.right - rect.left - spacing_x ) / ( sizeicon_x + spacing_x ) ) );
}

int CTView::GetNbTileColumn(void)
{
	RECT rect; GetClientRect(&rect);
	int deb;
	if (scrollpos<spacing_y) deb = -scrollpos;
	else deb = (scrollpos - spacing_y)%(sizeicon_y + spacing_y);
	if (deb>sizeicon_y) deb -=sizeicon_y + spacing_y;
	int ret= ((rect.bottom - rect.top /*- spacing_y*/ + deb)/(sizeicon_y + spacing_y)) +1 ;
	return max (1, ret);
}

void CTView::GetVisibility(int &First,int &Last, int n) //retourne l'indice du premier et du dernier item visible dans la fenetre
{
	int y;
	int i = GetNbTileLine(); int j = GetNbTileColumn();
	if (scrollpos<spacing_y) y = 0;
	else y = (scrollpos - spacing_y)%(sizeicon_y + spacing_y);
	First = ((scrollpos - spacing_y)/(sizeicon_y + spacing_y));
	if (y>sizeicon_y) First++;
	First *= i;
	Last = First + i*j -1;
	if (InfoList.GetSize(n)>0 && Last>=InfoList.GetSize(n)) Last = InfoList.GetSize(n)-1;
	if (First>Last) First = Last;
}

int CTView::GetIndex(LPPOINT pt, int n) //retourne l'index d'un incone a partir de sa position dans le fenetre
//si le curseur n'est pas sur un icon, retourne -1
{
	POINT pts = *pt;
	pts.y += scrollpos;
	RECT rect; GetClientRect(&rect);
	int i = GetNbTileLine();
	int lf = (rect.right - rect.left) - (GetNbTileLine()*(spacing_x+sizeicon_x) + spacing_x);
	if (pts.x > (rect.right - rect.left - lf)) return -1;
	if (pts.x<0) return -1;
	if (pts.y<spacing_y) return -1;
	int y = (pts.y - spacing_y)%(spacing_y + sizeicon_y);
	if (y>sizeicon_y) return -1;
	int il = (pts.y - spacing_y)/(spacing_y + sizeicon_y);
	int x = (pts.x - spacing_x)%(spacing_x + sizeicon_x);
	if (x>sizeicon_x) return -1;
	int ic = (pts.x - spacing_x)/(spacing_x + sizeicon_x);
	int ret = ic + il*i;
	if (ret<InfoList.GetSize(n) && ret>=0) return ret;
	else return -1;
}

POINT CTView::GetPos(int i) //fonction inverse de GetIndex
{
	POINT ret;
	int nl = max (1, GetNbTileLine());
	ret.x = (i%nl)*(spacing_x + sizeicon_x) + spacing_x;
	ret.y = (i/nl)*(spacing_y + sizeicon_y) + spacing_y - scrollpos;	
	return ret;
}

void CTView::UpdateSelection(LPRECT rect_,int mode, int n) //rect : coordonnees du rectangle de selection dans la fenetre parent
{	
	RECT client,*rect; GetWindowRect(&client);
	rect = new RECT; memcpy(rect,rect_,sizeof(RECT));
	//on se met dans le repere de CTView
	POINT pt; pt.x = rect->left; pt.y = rect->top;
	GetParent()->ClientToScreen(&pt);
	ScreenToClient(&pt);
	rect->left = pt.x;
	rect->top = pt.y;	
	pt.x = rect->right; pt.y = rect->bottom;
	GetParent()->ClientToScreen(&pt);
	ScreenToClient(&pt);
	rect->right = pt.x;
	rect->bottom = pt.y;
	rect->top += scrollpos; rect->bottom += scrollpos;

	if (rect->left<300)
	{
		int toto = 0;
	}

	//on clip
	if (rect->left<0) rect->left = 0;
	if (rect->top<scrollpos) rect->top = scrollpos;
	int lf = (client.right - client.left) - (GetNbTileLine()*(spacing_x+sizeicon_x) + spacing_x);
	if (rect->right>(client.right-client.left-spacing_x-lf)) 
	{
		rect->right = client.right - client.left - spacing_x - lf;
	}
	if ((rect->bottom-scrollpos)>(client.bottom-client.top)) rect->bottom=client.bottom-client.top+scrollpos;	
	
	if (rect->left<spacing_x) rect->left = spacing_x;
	if ((rect->top-scrollpos)<spacing_y) rect->top = spacing_y + scrollpos;
	int rx = (rect->left - spacing_x)%(spacing_x + sizeicon_x);
	rect->left -= rx;
	if (rx>=sizeicon_x)
	{
		rect->left += sizeicon_x + spacing_x;
		if (rect->left>=(client.right - client.left)) rect->left = (client.right - client.left) - spacing_x;
	}
	int ry = (rect->top - spacing_y)%(spacing_y + sizeicon_y);
	rect->top -= ry;
	if (ry>=sizeicon_y)
	{
		rect->top += sizeicon_y + spacing_y;
		if ((rect->top-scrollpos)>=(client.bottom - client.top)) rect->top = (client.bottom - client.top) - spacing_y + scrollpos;
	}

	
	if (rect->right<spacing_x || (rect->bottom-scrollpos)<spacing_y) return;
	rx = (rect->right - spacing_x)%(spacing_x + sizeicon_x);
	rect->right += (sizeicon_x - rx -1 );
	ry = (rect->bottom - spacing_y)%(spacing_y + sizeicon_y);
	rect->bottom += (sizeicon_y - ry -1 );

	if (rect->bottom<rect->top || rect->left>rect->right) return;
	pt; pt.x = rect->left; pt.y = rect->top; pt.y -=scrollpos;
	int index = GetIndex(&pt, n);
	tilelist::iterator p = InfoList.GetFirst(n);	
	CDC *pDC = GetDC();
	if (index==-1 && !(mode&MK_SHIFT))
	{
		for (int i = 0;i<InfoList.GetSize(n); i++)
		{
			if (p->Selected==1)
			{
				p->Selected = 0;
				DrawTile(p,pDC,0,n);
			}
			p++;
		}
		if (pDC) 
			::ReleaseDC(*this,*pDC);
		return;
	}
	for (int i = 0;i<index;i++) 
	{
		if (p==InfoList.GetLast(n))
		{
			if (pDC!=NULL) 
				::ReleaseDC(*this,*pDC);
			return;
		}
		if (p->Selected==1) 
		{
			p->Selected = 0;
			DrawTile(p,pDC,0,n);
		}
		else if (p->Selected&4)
		{
			if (p->Selected) p->Selected=2;
			else p->Selected = 3;
			DrawTile(p,pDC,0,n);
		}
		p++;
	}
	int nbline = GetNbTileLine();
	int incd = index%nbline;
	for (int j = rect->top;j<=rect->bottom;j+=sizeicon_y + spacing_y)
	{
		int k = 0;
		if (j!=rect->top)
		{
			for (;k<incd;k++) 
			{
				if (p==InfoList.GetLast(n))
				{
					if (pDC!=NULL) 
						::ReleaseDC(*this,*pDC);
					return;
				}
				if (p->Selected==1)
				{
					p->Selected = 0; 
					DrawTile(p,pDC,0,n);
				}				
				else if (p->Selected&4)
				{
					if (p->Selected&3) p->Selected=2;
					else p->Selected = 3;
					int k = !(p->Selected&1);
					DrawTile(p,pDC,0,n);
				}
				p++;
			}
		}
		else k = incd;
		for (int i = rect->left;i<=rect->right;i+=sizeicon_x + spacing_x)
		{
			if (p==InfoList.GetLast(n)) 
			{
				if (pDC!=NULL) 
					::ReleaseDC(*this,*pDC);
				return;
			}
			if (!(mode&MK_CONTROL))
			{
				if (p->Selected!=1)
				{
					p->Selected = 1;
					DrawTile(p,pDC,0,n);
				}
			}
			else
			{				
				if ((p->Selected&4)==0)
				{
					int k = p->Selected;
					p->Selected = (p->Selected&3)?4:5;				
					k = p->Selected;
					DrawTile(p,pDC,0,n);
				}
			}
			p++;
			k++;
		}
		for (;k<nbline;k++) 
		{
			if (p==InfoList.GetLast(n)) 
			{
				if (pDC!=NULL) 
					::ReleaseDC(*this,*pDC);
				return;
			}
			if (p->Selected==1) 
			{
				p->Selected = 0; 
				DrawTile(p,pDC,0,n);
			} 
			else if (p->Selected&4)
			{
				if (p->Selected&3) p->Selected=2;
				else p->Selected = 3;
				DrawTile(p,pDC,0,n);
			}
			p++;
		}
	}
	for (;p!=InfoList.GetLast(n);p++) 
	{
		if (p->Selected==1) 
		{
			p->Selected = 0;
			DrawTile(p,pDC,0,n);
		}
		else if (p->Selected&4)
		{
			if (p->Selected&3) p->Selected=2;
			else p->Selected = 3;
			DrawTile(p,pDC,0,n);
		}
	}
	if (pDC!=NULL) 
		::ReleaseDC(*this,*pDC);
}
		
void CTView::DeleteTile(tilelist::iterator p)
{
	p->loaded = 0;
	p->nightLoaded = 0;
	p->alphaLoaded = 0;
}

void CTView::UpdateBar(int iFirst,int iLast, int n)
{
	int i = GetNbTileColumn(); 
	int j = max (1, GetNbTileLine());
	int nbline = InfoList.GetSize(n)/j+1;
	int posline = iFirst/j;
	SCROLLINFO scr;
	scr.fMask = SIF_ALL ^ SIF_POS;
	scr.nMin = 0; scr.nMax = SCROLL_MAX; 
	if (nbline==0) {scr.nPage = SCROLL_MAX; scr.nPos = 0;}
	else 
	{
		scr.nPage = (SCROLL_MAX*i)/nbline;
	}
	GetParent()->SetScrollInfo(SB_VERT,&scr);
}
	



int CTView::IsSelected(int i)
{
	return 0;
}

void CTView::UpdateSize(int n)
{
	spacing_tile_text = 3;
	if (Zoom==1) {sizetile_x = sizetile_y = SIZE_SMALL; spacing_x = SPACING_SMALL_X; spacing_y = SPACING_SMALL_Y;}
	if (Zoom==2) {sizetile_x = sizetile_y = SIZE_NORMAL; spacing_x = SPACING_NORMAL_X; spacing_y = SPACING_NORMAL_Y;}
	if (Zoom==3) {sizetile_x = sizetile_y = SIZE_BIG; spacing_x = SPACING_BIG_X; spacing_y = SPACING_BIG_Y;}
	if (n==1)
	{
		sizetile_x *= 2;
		sizetile_y *= 2;
	}
	sizeicon_x = sizetile_x; sizeicon_y = sizetile_y + sizetext_y + spacing_tile_text;
}

int debug = 0;

void CTView::OnPaint() 
{
	Browse *parent = (Browse*)this->GetParent();
	CPaintDC dc(this); // device context for painting
	// TODO: Add your message handler code here
	RECT rect; GetClientRect(&rect);	
	CBrush brush (GetSysColor(COLOR_3DFACE));
	
	
	dc.FillRect(&rect,&brush);
	
	if (InfoList.GetSize(parent->m_128x128)==0) return;
	
	debug=(debug+1)&1;

	if (debug==1)
	{
		debug = 1;
	}

	CRgn clip;
	clip.CreateRectRgn(rect.left,rect.top,rect.right,rect.bottom);
	dc.SelectClipRgn(&clip);
	
	CRgn update;
	if (GetUpdateRgn(&update)==NULLREGION)
	{
		::ReleaseDC(*this,dc);
		return;
	}

	GetVisibility(iFV, iLV, parent->m_128x128);
	UpdateBar(iFV, iLV, parent->m_128x128);
	if (!normal_font) 
	{
		normal_font = new CFont;
		normal_font->CreateFont(-10,0,0,0,FW_THIN,false,false,false,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FIXED_PITCH,NULL);
	}
	
	tilelist::iterator p = InfoList.GetFirst(parent->m_128x128);
	int i;
	for (i=0;i<iFV;i++) p++;
	for (i=iFV;i<=iLV;i++) 
		DrawTile(p++,&dc,0, parent->m_128x128);
	::ReleaseDC (*this, dc);
}

void CTView::OnDropFiles(HDROP hDropInfo) 
{
	// TODO: Add your message handler code here and/or call default
	//first : on verifie s'il les tiles doivent etre inseres
	if (!lockInsertion)
	{
		Browse *parent = (Browse*)this->GetParent();
		TCHAR FileName[256];
		int count=DragQueryFile(hDropInfo, 0xffffffff, FileName, 256); //count = files number in drop queue
		

		POINT pos;
		DragQueryPoint(hDropInfo,&pos); //retrieve cursor position
		CDC *pDC = GetDC();
		for (int i=0;i<count;i++) 
		{
			DragQueryFile(hDropInfo,i,FileName,256);
			switch (parent->m_128x128)
			{
			case 0:
				{
					int index=InfoList.addTile128 ();
					if (InfoList.setTile128 (index, NLMISC::tStrToUtf8(FileName), Texture==1?CTile::diffuse:(Texture==2?CTile::additive:CTile::alpha)))
					{
						tilelist::iterator it = InfoList.GetLast(parent->m_128x128);
						it--;
					}
					else
					{
						InfoList.removeTile128 (index);
					}
				}
				break;
			case 1:
				{
					int index=InfoList.addTile256 ();
				    if (InfoList.setTile256(index, NLMISC::tStrToUtf8(FileName), Texture == 1 ? CTile::diffuse : (Texture == 2 ? CTile::additive : CTile::alpha)))
					{
						tilelist::iterator it = InfoList.GetLast(parent->m_128x128);
						it--;
					}
					else
						InfoList.removeTile256 (index);
				}
				break;
			case 2:
				{
				}
				break;
			}
		}
		
		int iFV,iLV;
		GetVisibility(iFV, iLV, parent->m_128x128);
		UpdateBar(iFV, iLV, parent->m_128x128);
		Invalidate ();
	}
	CStatic::OnDropFiles(hDropInfo);
}

void CTView::DrawTile(tilelist::iterator i,CDC *pDC,int clear, int n)
{
	RECT rect; GetClientRect(&rect);	
	CBrush brush (GetSysColor(COLOR_3DFACE));
	if (InfoList.GetSize(n)==0) 
	{
		return;
	}

	LPBITMAPINFO bmpinf;
	std::string pth; 
	std::vector<NLMISC::CBGRA> *bits;
	int loaded;

	switch (n)
	{
	case 0:
		pth = i->getRelativeFileName ((CTile::TBitmap)(Texture-1), tileBank2.getTileSet (InfoList._tileSet)->getTile128 (i->id));
		break;
	case 1:
		pth = i->getRelativeFileName ((CTile::TBitmap)(Texture-1), tileBank2.getTileSet (InfoList._tileSet)->getTile256 (i->id));
		break;
	case 2:
		{
			int index=tileBank2.getTileSet (InfoList._tileSet)->getTransition (i->id)->getTile();
			if (index!=-1)
				pth = i->getRelativeFileName ((CTile::TBitmap)(Texture-1), index);
			else
				pth.clear();
		}
		break;
	case 3:
		pth = tileBank2.getDisplacementMap (tileBank2.getTileSet (InfoList._tileSet)->getDisplacementTile ((CTileSet::TDisplacement)i->id));
		break;
	}

	switch(Texture)
	{
		case 1:
			bmpinf = &(i->BmpInfo);
			bits = &i->Bits;
			loaded = i->loaded;
			break;
		case 2:
			bmpinf = &(i->nightBmpInfo);
			bits = &i->nightBits;
			loaded = i->nightLoaded;
			break;
		case 3:
			bmpinf = &(i->alphaBmpInfo);
			bits = &i->alphaBits;
			loaded = i->alphaLoaded;
			break;
	}

	CRgn clip;
	clip.CreateRectRgn(rect.left,rect.top,rect.right,rect.bottom);
	pDC->SelectClipRgn(&clip);
	
	// Select a font
	CFont *pOldFont=pDC->SelectObject(normal_font);
	
	POINT pt;
	pt = GetPos(i->id);
	RECT rect_txt; 
	rect_txt.top = pt.y;
	rect_txt.bottom = pt.y + sizeicon_y + spacing_y;
	rect_txt.left = pt.x; rect_txt.right = pt.x + sizeicon_x + spacing_x;

	// Turn every other pixel to black
	COLORREF clrBk = pDC->SetBkColor( GetSysColor(COLOR_3DFACE) );
	COLORREF clrText = pDC->SetTextColor( RGB(0,0,0) );
	
	if (clear) pDC->FillRect(&rect_txt,&brush);
	
	if (!loaded)
	{
		pDC->FillSolidRect( pt.x, pt.y, sizetile_x, sizetile_y, GetSysColor(COLOR_3DFACE) );
		pDC->MoveTo (pt.x,pt.y);
		pDC->LineTo (pt.x+sizetile_x,pt.y+sizetile_y);
		pDC->MoveTo (pt.x+sizetile_x,pt.y);
		pDC->LineTo (pt.x,pt.y+sizetile_y);
		pDC->MoveTo (pt.x,pt.y);
		pDC->LineTo (pt.x+sizetile_x,pt.y);
		pDC->LineTo (pt.x+sizetile_x,pt.y+sizetile_y);
		pDC->LineTo (pt.x,pt.y+sizetile_y);
		pDC->LineTo (pt.x,pt.y);

		pDC->MoveTo (pt.x+1,pt.y);
		pDC->LineTo (pt.x+sizetile_x,pt.y+sizetile_y-1);
		pDC->MoveTo (pt.x,pt.y+1);
		pDC->LineTo (pt.x+sizetile_x-1,pt.y+sizetile_y);
		
		pDC->MoveTo (pt.x+sizetile_x-1,pt.y);
		pDC->LineTo (pt.x,pt.y+sizetile_y-1);
		pDC->MoveTo (pt.x+sizetile_x,pt.y+1);
		pDC->LineTo (pt.x+1,pt.y+sizetile_y);
	}
	else
	{
		StretchDIBits(pDC->m_hDC,pt.x,pt.y,
			sizetile_x,sizetile_y,0,0,
			bmpinf->bmiHeader.biWidth,
			-bmpinf->bmiHeader.biHeight,
			&*bits->begin(),bmpinf,DIB_RGB_COLORS,SRCCOPY);
	}
			
	std::string Name;
	if (InfoTexte==2)
	{
		Name = NLMISC::CFile::getFilenameWithoutExtension(pth);
	}
	else if (InfoTexte==3)
	{
		__int64 mask = 1;
	}
	else if (InfoTexte==1)
	{
		Name = NLMISC::toString("%d", i->id);
	}

	rect_txt.top = pt.y + sizetile_y + spacing_tile_text;
	rect_txt.bottom += rect_txt.top + sizetext_y;
	rect_txt.left -= spacing_x;

#ifdef _UNICODE
	ucstring tmp;
	tmp.fromUtf8(Name);
#else
	std::string tmp = Name;
#endif

	pDC->DrawText((LPCTSTR)tmp.c_str(), (int)tmp.length(), &rect_txt,DT_CENTER | DT_SINGLELINE);

	// Restore the device context
	pDC->SetBkColor( clrBk );
	pDC->SetTextColor( clrText );

	if (i->Selected&3)
	{
		CRect rc;
		rc.left = pt.x; rc.top = pt.y; rc.right = rc.left + sizetile_x; rc.bottom = rc.top + sizetile_y;
		ShadeRect(pDC,rc);
	}

	// Invalidate flag button
	Browse *parent = (Browse*)this->GetParent();
	parent->UpdateFlags ();

	// Release the font
	pDC->SelectObject(pOldFont);
}

void CTView::ShadeRect( CDC *pDC, CRect& rect )

{
     // Bit pattern for a monochrome brush with every
     // other pixel turned off
     WORD Bits[8] = { 0x0055, 0x00aa, 0x0055, 0x00aa,
                      0x0055, 0x00aa, 0x0055, 0x00aa };

	CBitmap bmBrush;
	CBrush brush; // (GetSysColor(COLOR_3DFACE));

	// Need a monochrome pattern bitmap
	bmBrush.CreateBitmap( 8, 8, 1, 1, &Bits );

	// Create the pattern brush
	brush.CreatePatternBrush( &bmBrush );

	CBrush *pOldBrush = pDC->SelectObject( &brush );

	// Turn every other pixel to black
	COLORREF clrBk = pDC->SetBkColor( GetSysColor(COLOR_3DFACE) );
	COLORREF clrText = pDC->SetTextColor( RGB(0,0,0) );
	// 0x00A000C9 is the ROP code to AND the brush with the destination
	pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), 
		 (DWORD)0x00A000C9);                     //DPa - raster code

	pDC->SetBkColor( clrBk );
	pDC->SetTextColor( clrText );
	clrBk = pDC->SetBkColor( RGB(0,0,0) );
	clrText = pDC->SetTextColor( GetSysColor(COLOR_HIGHLIGHT) );
	// 0x00FA0089 is the ROP code to OR the brush with the destination
	pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), 
		 (DWORD)0x00FA0089);                     //DPo - raster code

	// Restore the device context
	pDC->SelectObject( pOldBrush );
	pDC->SetBkColor( clrBk );
	pDC->SetTextColor( clrText );
}


//code modifie des sources des MFC =)))
void CTView::DrawDragRect(CDC *pDC,LPCRECT lpRect, SIZE size,
	LPCRECT lpRectLast, SIZE sizeLast, CBrush* pBrush, CBrush* pBrushLast)
{
	// first, determine the update region and select it
	CRgn rgnNew;
	CRgn rgnOutside, rgnInside;
	CRect rect;
	if (lpRect)
	{
		rgnOutside.CreateRectRgnIndirect(lpRect);
		rect = *lpRect;
		rect.InflateRect(-size.cx, -size.cy);
		rect.IntersectRect(rect, lpRect);
		rgnInside.CreateRectRgnIndirect(rect);
		rgnNew.CreateRectRgn(0, 0, 0, 0);
		rgnNew.CombineRgn(&rgnOutside, &rgnInside, RGN_XOR);
	}

	CBrush* pBrushOld = NULL;
	if (pBrush == NULL)
		pBrush = CDC::GetHalftoneBrush();
	if (pBrushLast == NULL)
		pBrushLast = pBrush;

	CRgn rgnLast, rgnUpdate;
	if (lpRectLast != NULL/* && lpRect!=NULL*/)
	{
		// find difference between new region and old region
		rgnLast.CreateRectRgn(0, 0, 0, 0);
		if (lpRect==NULL) rgnOutside.CreateRectRgnIndirect(lpRectLast);
		else rgnOutside.SetRectRgn(lpRectLast);
		rect = *lpRectLast;
		rect.InflateRect(-sizeLast.cx, -sizeLast.cy);
		rect.IntersectRect(rect, lpRectLast);
		if (lpRect==NULL) rgnInside.CreateRectRgnIndirect(rect);
		else rgnInside.SetRectRgn(rect);
		rgnLast.CombineRgn(&rgnOutside, &rgnInside, RGN_XOR);

		// only diff them if brushes are the same
		if (pBrush->m_hObject == pBrushLast->m_hObject)
		{
			rgnUpdate.CreateRectRgn(0, 0, 0, 0);
			rgnUpdate.CombineRgn(&rgnLast, &rgnNew, RGN_XOR);
		}
	}
	if (!lpRect || (pBrush->m_hObject != pBrushLast->m_hObject && lpRectLast != NULL))
	{
		// brushes are different -- erase old region first
		pDC->SelectClipRgn(&rgnLast);
		pDC->GetClipBox(&rect);
		pBrushOld = pDC->SelectObject(pBrushLast);
		pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATINVERT);
		pDC->SelectObject(pBrushOld);
	}
	// draw into the update/new region
	if (lpRect)
	{
		pDC->SelectClipRgn(rgnUpdate.m_hObject != NULL ? &rgnUpdate : &rgnNew);
		pDC->GetClipBox(&rect);
		pBrushOld = pDC->SelectObject(pBrush);
		pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATINVERT);
		pDC->SelectObject(pBrushOld);
	}
	pDC->SelectClipRgn(NULL);
}

void CTView::RemoveSelection(int n)
{
	for (tilelist::iterator p = InfoList.theList[n].begin();p!=InfoList.theList[n].end();++p)
	{
		p->Selected = 0;
	}
}

void CTView::InsertItemInCtrlList(tilelist::iterator iFirst,tilelist::iterator iLast)
{
/*	int iItem = InfoList.GetSize();
	for (tilelist::iterator i=iFirst;i!=iLast;++i)
	{
		char num[10];
		sprintf(num,"%d",iItem);
		InsertItem(iItem,num,0);
		SetItemData(iItem++,(DWORD)(*i));
	}*/
}



LRESULT CTView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
	// TODO: Add your specialized code here and/or call the base class
	if (message==WM_MOUSEMOVE)
	{
		MousePos.x = LOWORD(lParam);
		MousePos.y = HIWORD(lParam);
	}
	if (message==WM_DRAWITEM)
	{
		int toto=0;
	}
	if (message==WM_CLOSE) Delete();
	if (message==WM_COMMAND) //The user had selected an item on popup menu
	{
		Browse *parent = (Browse*)this->GetParent();
		int id=LOWORD(wParam);
		if (id==ID_MENU_SUPR_TILE)
		{
			for (int i=0; i<InfoList.GetSize(parent->m_128x128); i++)
			{
				if (InfoList.Get(i, parent->m_128x128)->Selected)
				{
					switch (parent->m_128x128)
					{
					case 0:
						InfoList.removeTile128 (i);
						break;
					case 1:
						InfoList.removeTile256 (i);
						break;
					default:
						nlassert (0); // no!
					}
				}
			}
			bPopup = 0;
		}
		else if (id==ID_MENU_SUPR_BITMAP)
		{
			for (int i=0; i<InfoList.GetSize(parent->m_128x128); i++)
			{
				if (InfoList.Get(i, parent->m_128x128)->Selected)
				{
					switch (parent->m_128x128)
					{
					case 0:
						InfoList.clearTile128 (i, (CTile::TBitmap)(Texture-1));
						break;
					case 1:
						InfoList.clearTile256 (i, (CTile::TBitmap)(Texture-1));
						break;
					case 2:
						InfoList.clearTransition (i, (CTile::TBitmap)(Texture-1));
						break;
					case 3:
						InfoList.clearDisplacement (i);
						break;
					default:
						nlassert (0); // no!
					}
				}
			}
			bPopup = 0;
		}
		else if ((id==ID_MENU_ADD)||(id==ID_MENU_REPLACE))
		{
			_chdir (LastPath.c_str());
			CFileDialog load(true, NULL, nlUtf8ToTStr(LastPath), OFN_ENABLESIZING | OFN_ALLOWMULTISELECT, _T("PNG Bitmap (*.png)|*.png|Targa bitmap (*.tga)|*.tga|All files (*.*)|*.*||"), NULL);
			load.m_ofn.lpstrFile = new TCHAR[10000]; // buffer contains filenames list
			load.m_ofn.lpstrFile[0] = 0;
			// with 10 KB we should be large enough...
			// if all files are exceeding 10000 characters, insert would be skipped
			load.m_ofn.nMaxFile = 10000-1;
			if (load.DoModal()==IDOK)
			{
				// Ok ?
				int ok=1;

				//AfxMessageBox ("toto");
				POSITION p = load.GetStartPosition(); //la doc dit que p=NULL quand il n'y a pas de selection : c'est faux, genial les MFC
				while (p)
				{
					CString str = load.GetNextPathName(p);

					LastPath = NLMISC::CFile::getPath(NLMISC::tStrToUtf8(str));

					if (!str.IsEmpty())
					{
						int index=0;
						std::string pathname = NLMISC::tStrToUtf8(str);

						// Add mode, to the end of the list
						if (id==ID_MENU_ADD)
						{
							// Index of the new tile
							int index;

							// Index in the list
							switch (parent->m_128x128)
							{
							case 0:
								// Add a 128 tile
								index=InfoList.addTile128 ();

								// Set the tile
								if ((ok=InfoList.setTile128 (index, pathname, Texture==1?CTile::diffuse:(Texture==2?CTile::additive:CTile::alpha)))==0)
									// If prb, remove it
									InfoList.removeTile128 (index);

								break;
							case 1:
								// Add a 128 tile
								index=InfoList.addTile256 ();

								// Set the tile
								if ((ok=InfoList.setTile256 (index, pathname, Texture==1?CTile::diffuse:(Texture==2?CTile::additive:CTile::alpha)))==0)
									// If prb, remove it
									InfoList.removeTile256 (index);

								break;
							case 2:
								nlassert (0);		// no, can't add transition
								break;
							}
						}
						else
						{
							// Must be a replace mode.
							nlassert (id==ID_MENU_REPLACE);

							// For each tile
							for (int index=0; index<InfoList.GetSize(parent->m_128x128); index++)
							{
								// If selected
								if (InfoList.Get(index, parent->m_128x128)->Selected)
								{
									switch (parent->m_128x128)
									{
									case 0:
										// Set the 128 tile
										ok=InfoList.setTile128 (index, pathname, Texture==1?CTile::diffuse:(Texture==2?CTile::additive:CTile::alpha));
										break;
									case 1:
										// Set the 256 tile
										ok=InfoList.setTile256 (index, pathname, Texture==1?CTile::diffuse:(Texture==2?CTile::additive:CTile::alpha));
										break;
									case 2:
										// Alpha texture ?
										if (Texture!=3)
										{
											ok=InfoList.setTileTransition (index, pathname, Texture==1?CTile::diffuse:(Texture==2?CTile::additive:CTile::alpha));
										}
										// Alpha!
										else
										{
											// Select rotation
											SelectRotation selectRotation;
											if (selectRotation.DoModal()==IDOK)
											{
												// Set the alpha tile with the good rotation
												ok=InfoList.setTileTransitionAlpha (index, pathname, selectRotation.RotSelected);
											}
										}
										break;
									case 3:
										// Displacement
										ok=InfoList.setDisplacement (index, pathname);
										break;
									default:
										nlassert (0); // no!
									}
								}

								// Reload last inserted tile
								InfoList.Reload(index, 1, parent->m_128x128);
								InfoList.Get(index, parent->m_128x128)->Selected = 0;

								// Stop ?
								if (!ok)
									break;
							}
						}
					}
					// Stop ?
					if (!ok)
						break;
				}
			}
			delete load.m_ofn.lpstrFile;
			//InfoList.Sort();
			bPopup = 0;
		}
		else if (id==12)
		{
			ViewTileMode = 1;
			Browse *parent = (Browse*) this->GetParent();
			parent->SendMessage(WM_PAINT,0,0);
			bPopup = 0;
		}
		this->RedrawWindow();
	}
	return CStatic::WindowProc(message, wParam, lParam);
}
	

void CTView::OnRButtonDown(UINT nFlags, CPoint point) 
{	
	// TODO: Add your message handler code here and/or call default
	if (!lockInsertion)
	{
		Browse *parent = (Browse*)this->GetParent();
		RECT wndpos; CMenu popup;
		GetParent()->GetWindowRect(&wndpos);
		popup.CreatePopupMenu();

		int c = 0;
		for (tilelist::iterator p = InfoList.GetFirst(parent->m_128x128);p!=InfoList.GetLast(parent->m_128x128);++p)
		{
			if (p->Selected) c++;
		}		
				
		if (!ViewTileMode)
		{
			popup.AppendMenu(parent->m_128x128<2 ? MF_STRING : MF_STRING | MF_GRAYED, ID_MENU_ADD, _T("Add..."));
			popup.AppendMenu(c>0 ? MF_STRING : MF_STRING | MF_GRAYED, ID_MENU_REPLACE, _T("Replace..."));
			popup.AppendMenu(c>0 ? MF_STRING : MF_STRING | MF_GRAYED, ID_MENU_SUPR_BITMAP, _T("Del bitmap"));
			popup.AppendMenu((c>0 && parent->m_128x128<2) ? MF_STRING : MF_STRING | MF_GRAYED, ID_MENU_SUPR_TILE, _T("Del tile"));
		}
		else
		{
		}
		bPopup = 1;
		popup.TrackPopupMenu(TPM_LEFTALIGN,MousePos.x+wndpos.left,MousePos.y+wndpos.top,GetParent(),NULL);
	}
	CStatic::OnRButtonDown(nFlags, point);
}

void CTView::OnLButtonDown(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	SendMessage(WM_PAINT,0,0);
	CStatic::OnLButtonDown(nFlags, point);
}


void CTView::OnLButtonDblClk(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
}


