#include "stdafx.h"

#include "filemanip.h"

// Ensure there's no padding added into the struct,
// in a very non-portable way
#ifdef _WIN32
#pragma pack(push)
#pragma pack(1)
#endif

typedef struct
{
	char  identsize;		// size of ID field that follows 18 byte header (0 usually)
	char  colourmaptype;	// type of colour map 0=none, 1=has palette
	char  imagetype;		// type of image 0=none,1=indexed,2=rgb,3=grey,+8=rle packed

	short colourmapstart;	// first colour map entry in palette
	short colourmaplength;	// number of colours in palette
	char  colourmapbits;	// number of bits per palette entry 15,16,24,32

	short xstart;			// image x origin
	short ystart;			// image y origin
	short width;			// image width in pixels
	short height;			// image height in pixels
	char  bits;				// image bits per pixel 8,16,24,32
	char  descriptor;		// image descriptor bits (vh flip bits)
#ifdef _WIN32
} TGA_HEADER;
# pragma pack(pop)
#else
} __attribute__ ((packed)) TGA_HEADER;
#endif

// Convert the RGB image to 8-bit greyscale and output
void RGB_OutputGreyscaleTGA(unsigned char* image_data, int width, int height, int pitch, wxFFile& file)
{
	assert(sizeof(TGA_HEADER) == 18);

	TGA_HEADER header;

	header.identsize = 0;
	header.colourmaptype = 0;
	header.imagetype = 3;
	header.xstart = 0;
	header.ystart = 0;
	header.width = (short)width;
	header.height = (short)height;
	header.bits = 8;
	header.descriptor = 0;

	file.Write(&header, sizeof(header));

	// Start from the bottom, so things look the right way up
	image_data += height*pitch;

	for (int y = 0; y < height; ++y)
	{
		image_data -= pitch;

		for (int x = 0; x < width; ++x)
			file.Write(&image_data[x*3], 1);
	}
}

std::set<wchar_t> AnalyseChars(wxString filename)
{
	wxFFile File (filename, "rb");
	if (! File.IsOpened())
		throw "Cannot open file";

	enum { 
		UNKNOWN,
		UTF8,
		UTF16SE, // same-endian as this machine
		UTF16DE  // different-endian
	} Format = UNKNOWN;

	unsigned short BOM;
	File.Read(&BOM, 2);
	if (BOM == 0xFEFF)
		Format = UTF16SE;
	else if (BOM == 0xFFFE)
		Format = UTF16DE;
	else {
		// Make an educated guess based on the first byte
		// If it's like "_\0" it's probably little-endian,
		// if it's like "\0_" it's probably big-endian
		if ((BOM & 0xFF00) == 0)
			Format = UTF16SE;
		else if ((BOM & 0x00FF) == 0)
			Format = UTF16DE;
		else
			throw "Can't determine endianness of source file - make sure it's UTF16 and starts with a simple letter";
			// (Maybe it's UTF8, but I don't want to bother with that)
		
		File.Seek(0); // Make sure the first character is included in the list
	}

	std::set<wchar_t> Chars;
	if (Format == UTF16SE)
	{
		wchar_t c;
		while (! File.Eof())
		{
			File.Read(&c, 2);
			if (c != '\r' && c != '\n')
				Chars.insert(c);
		}
	}
	else if (Format == UTF16DE)
	{
		unsigned short c;
		while (! File.Eof())
		{
			if (File.Read(&c, 2) != 2) break; // abort if failed (i.e. eof)
			c = (c>>8)|((c&0xff)<<8); // swap bytes
			if (c != '\r' && c != '\n')
				Chars.insert(c);
		}
	}
	else
	{
		throw "Internal error - invalid file format";
	}

	return Chars;
}
