/* ScummVM - Kyrandia Interpreter
 * Copyright (C) 2003-2004 The ScummVM project
 *
 * This program 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 2
 * 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 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Header: /cvsroot/scummvm/scummvm/kyra/font.cpp,v 1.4 2004/11/14 20:11:22 sev Exp $
 *
 */

#include "stdafx.h"
#include "resource.h"
#include "common/stream.h"
#include "common/array.h"

#ifdef DUMP_FILES
#include <cstdio>
#endif

namespace Kyra {
const uint16 FontHeader_Magic1 = 0x0500;
const uint16 FontHeader_Magic2 = 0x000e;
const uint16 FontHeader_Magic3 = 0x0014;

Font::Font(uint8* buffer, uint32 size) {
	if (!buffer) {
		error("resource created without data");
	}
		
	_buffer = buffer;
	
	Common::MemoryReadStream bufferstream(buffer, size);
	
	bufferstream.read(&_fontHeader, sizeof(_fontHeader));
	
	// tests for the magic values
	if(_fontHeader._magic1 != FontHeader_Magic1 || _fontHeader._magic2 != FontHeader_Magic2 ||
		_fontHeader._magic3 != FontHeader_Magic3) {
		error("magic vars in the fontheader are corrupt\n"
			  "_magic1 = 0x%x, _magic2 = 0x%x, _magic3 = 0x%x",
			_fontHeader._magic1, _fontHeader._magic2, _fontHeader._magic3);
	}
	
	// init all the pointers
	_offsetTable = (uint16*)&buffer[bufferstream.pos()];
	_charWidth = &buffer[_fontHeader._charWidthOffset];
	_charHeight = (uint16*)&buffer[_fontHeader._charHeightOffset];
	_charBits = &buffer[_fontHeader._charBitsOffset];
	
	// now prerender =)
	preRenderAllChars(bufferstream.pos());

	// This value seems to be a version or language variable
	// Known Values
	// ------------
	// Russian Floppy:			0x1010
	// German Floppy and English/German CD:	0x1011
	debug("Font::_version = 0x%x", _fontHeader._version);
 
	delete [] _buffer;
	_buffer = 0;
	_offsetTable = 0;
	_charHeight = 0;
	_charWidth = 0;
	_charBits = 0;
}
	
Font::~Font() {
	// FIXME: Release memory of the prerendered chars
}
	
uint32 Font::getStringWidth(const char* string, char terminator) {
	uint32 strsize;
		
	for (strsize = 0; string[strsize] != terminator && string[strsize] != '\0'; ++strsize)
		;
		
	uint32 stringwidth = 0;
		
	for (uint32 pos = 0; pos < strsize; ++pos) {
		stringwidth += _preRenderedChars[string[pos]].width;
	}
		
	return stringwidth;
}
	
const uint8* Font::getChar(char c, uint8* width, uint8* height, uint8* heightadd) {
	PreRenderedChar& c_ = _preRenderedChars[c];
	
	*width = c_.width;
	*height = c_.height;
	*heightadd = c_.heightadd;
	
	return c_.c;
}
	
// splits up the String in a word	
const char* Font::getNextWord(const char* string, uint32* size) {
	uint32 startpos = 0;
	*size = 0;
	
	// gets start of the word
	for (; string[startpos] == ' '; ++startpos)
		;
				
	// not counting size
	for (*size = 0; string[startpos + *size] != ' ' && string[startpos + *size] != '\0'; ++(*size))
		;
	
	++(*size);
				
	return &string[startpos];
}
	
// Move this to Font declaration?
struct WordChunk {
	const char* _string;
	uint32 _size;
};

void Font::drawStringToPlane(const char* string,
				uint8* plane, uint16 planewidth, uint16 planeheight,
				uint16 x, uint16 y, uint8 color) {
								
	// lets do it word after word
	Common::Array<WordChunk> words;
	
	uint32 lastPos = 0;
	uint32 lastSize = 0;
	uint32 strlgt = strlen(string);
	
	while (true) {
		WordChunk newchunk;
		newchunk._string = getNextWord(&string[lastPos], &lastSize);
		newchunk._size = lastSize;
		lastPos += lastSize;	
  		
		words.push_back(newchunk);
			
		if (lastPos >= strlgt)
			break;
	}
		
	uint16 current_x = x, current_y = y;
	uint8 heighest = 0;
	
	const uint8* src = 0;
	uint8 width = 0, height = 0, heightadd = 0;
	
	// now the have alle of these words
	for (uint32 tmp = 0; tmp < words.size(); ++tmp) {
		lastSize = getStringWidth(words[tmp]._string, ' ');
			
		// adjust x position
		if (current_x + lastSize >= planewidth) {
			// hmm lets move it a bit to the left
			if (current_x == x && (int16)planewidth - (int16)lastSize >= 0) {
				current_x = planewidth - lastSize;
			} else {
				current_x = x;
				if (heighest) 
					current_y += heighest + 2;
				else // now we are using just the fist char :)
					current_y += _preRenderedChars[words[tmp]._string[0]].height;
				heighest = 0;
			}
		}
		
		// TODO: maybe test if current_y >= planeheight ?
		
		// output word :)
		for (lastPos = 0; lastPos < words[tmp]._size; ++lastPos) {
			if (words[tmp]._string[lastPos] == '\0')
				break;
				
			// gets our char :)
			src = getChar(words[tmp]._string[lastPos], &width, &height, &heightadd);
			
			// lets draw our char
			drawCharToPlane(src, color, width, height, plane, planewidth, planeheight, current_x, current_y + heightadd);
			
			current_x += width;
			heighest = MAX(heighest, height);
		}
	}
}
	
void Font::drawCharToPlane(const uint8* c, uint8 color, uint8 width, uint8 height,
				uint8* plane, uint16 planewidth, uint16 planeheight, uint16 x, uint16 y) {
	const uint8* src = c;					
	
	// blit them to the screen
	for (uint8 yadd = 0; yadd < height; ++yadd) {
		for (uint8 xadd = 0; xadd < width; ++xadd) {
			switch(*src) {					
			case 1:
				plane[(y + yadd) * planewidth + x + xadd] = color;
				break;
				
			case 2:
				plane[(y + yadd) * planewidth + x + xadd] = 14;
				break;
				
			case 3:
				plane[(y + yadd) * planewidth + x + xadd] = 0;
				break;
				
			default:
				// nothing to do now
				break;
			};
			
			++src;
		}
	}
}
	
void Font::preRenderAllChars(uint16 offsetTableOffset) {
	uint16 startOffset = _offsetTable[0];
	uint16 currentOffset = offsetTableOffset;
	uint8 currentChar = 0;
	
	for (; currentOffset < startOffset; ++currentChar, currentOffset += sizeof(uint16)) {
		// lets prerender the char :)
		
		PreRenderedChar newChar;
		
		newChar.c = new uint8[(_charHeight[currentChar] >> 8) * _charWidth[currentChar]];
		assert(newChar.c);
		memset(newChar.c, 0, sizeof(uint8) * (_charHeight[currentChar] >> 8) * _charWidth[currentChar]);
		newChar.height = (_charHeight[currentChar] >> 8);
		newChar.width = _charWidth[currentChar];
		newChar.heightadd = _charHeight[currentChar] & 0xFF;
		
		uint8* src = _buffer + _offsetTable[currentChar];
		uint8* dst = &newChar.c[0];
		uint8 index = 0;
		
#ifdef DUMP_FILES
		static char filename[32] = { 0 };
		sprintf(filename, "dumps/char%d.dmp", currentChar);
		FILE* dump = fopen(filename, "w+");
		assert(dump);
		
		fprintf(dump, "This should be a '%c'\n", currentChar);
#endif
			
		// prerender the char
		for (uint8 yadd = 0; yadd < newChar.height; ++yadd) {
			for (uint8 xadd = 0; xadd < newChar.width; ++xadd) {
				if (xadd % 2) {
					index = ((*src) & 0xF0) >> 4;
					++src;
				} else {
					index = (*src) & 0x0F;
				}
				
				switch(index) {
				case 1:
#ifdef DUMP_FILES
					fprintf(dump, "#");
#endif
					dst[yadd * newChar.width + xadd] = 1;
					break;
				
				case 2:
#ifdef DUMP_FILES
					fprintf(dump, "$");
#endif
					dst[yadd * newChar.width + xadd] = 2;
					break;
				
				case 3:
#ifdef DUMP_FILES
					fprintf(dump, "§");
#endif
					dst[yadd * newChar.width + xadd] = 3;
					break;
				
				default:
#ifdef DUMP_FILES
					fprintf(dump, "%d", index);
#endif
					break;
				};
			}
				
			if (newChar.width % 2) {
				++src;
			}
#ifdef DUMP_FILES
			fprintf(dump, "\n");
#endif
		}
			
#ifdef DUMP_FILES
		fprintf(dump, "\nThis is the created map:\n");
		// now print the whole thing again
		for (uint8 yadd = 0; yadd < newChar.height; ++yadd) {
			for (uint8 xadd = 0; xadd < newChar.width; ++xadd) {
				fprintf(dump, "%d", dst[yadd * newChar.width + xadd]);
			}
			fprintf(dump, "\n");
		}
		fclose(dump);
#endif
		
		_preRenderedChars[currentChar] = newChar;
		
		if (currentChar == 255) {
			break;
		}
	}
}
} // end of namespace Kyra

