/*
 *
 * sceneload.cpp
 *
 * 27th March 2010: Created sceneload.cpp from parts of scene.cpp
 *
 * Part of the OpenJazz project
 *
 *
 * Copyright (c) 2005-2010 Alister Thomson
 *
 * OpenJazz is distributed under the terms of
 * the GNU General Public License, version 2.0
 *
 * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

/*
 * Deals with the loading of cutscene data.
 *
 */


#include "scene/scene.h"

#include "io/file.h"
#include "io/gfx/font.h"
#include "io/gfx/video.h"

#include <string.h>

/*							
 * $0x $...	Next x + 1 bytes are 'literals'; each byte colors 1 column (Max val $3F)									
 * $4x $yy        Next x + 1 columns drawn in color yy (Max value $7E)
 * $7F $xxxx $yy	Next xxxx columns colored with color yy						
 * $8x		Next x + 1 pixels are skipped, they're already the right color (Max val $FE)
 * $FF $xxxx	Skip next xxxx pixels of picture, they're already the right color
 */														
void Scene::loadCompacted(int& size, File* f, unsigned char* pixdata, int width, int height) {
	int pixels = 0;
	unsigned char* endpixdata = pixdata + (width*height);
	unsigned char* fillstart = NULL;
	while (size > 0) {
		unsigned char header = f->loadChar();		
	
		switch (header)  {
			case 0x7F: {
				unsigned short fillWidth = f->loadShort();
				unsigned char fillColor = f->loadChar();
								
				fillstart = pixdata;
				while(fillstart+fillWidth < endpixdata) {
					memset(fillstart, fillColor, fillWidth);
					fillstart+=width;
					}
				
				pixdata+=fillWidth;				
				pixels+=fillWidth;
				size -= 3;
				}	
				break;
	
			case 0xff: {
				unsigned short skip = f->loadShort();				
				pixels+=skip;
				pixdata+=skip;
				size -= 2;
				}
	
				break;
	
			default:
				if(header&0x80) {
					unsigned short skip =(header-0x80)+1;										
					pixels+=skip;
					pixdata+=skip;
					}
				else if(header&0x40) {					
					unsigned char fillColor = f->loadChar();
					unsigned char fillWidth = ((header-0x40)+1);
					
					fillstart = pixdata;
				
					while(fillstart+fillWidth < endpixdata) {
						memset(fillstart, fillColor, fillWidth);
						fillstart+=width;
						}
					
					pixdata+=fillWidth;
					pixels+=fillWidth;
					size--;
					}
				else {
					int copyWidth = (header & 0x3f)+1;
					unsigned char color;
					
					for(int col = 0;col < copyWidth;col++) {						
						color= f->loadChar();
						if(color != 0xff){
							fillstart = pixdata;
							
							while(fillstart < endpixdata) {
								*fillstart = color;
								fillstart+=width;
								}
							}
						pixdata++;
						pixels++;
						size--;
						}
					}			
				break;
		}
	
		size--;
	}
	
	LOG("PL Compacts pixels", pixels);
}

void Scene::loadCompactedMem(int& size, unsigned char* frameData, unsigned char* pixdata, int width, int height) {
	int pixels = 0;
	unsigned char* endpixdata = pixdata + (width*height);
	unsigned char* fillstart = NULL;
	while (size > 0) {
		unsigned char header = *frameData;frameData++;		
	
		switch (header)  {
			case 0x7F: {
				unsigned short fillWidth = *frameData;frameData++;	
				fillWidth+=((unsigned short)(*frameData))<<8;frameData++;	
				unsigned char fillColor = *frameData;frameData++;	
								
				fillstart = pixdata;
				while(fillstart+fillWidth < endpixdata) {
					memset(fillstart, fillColor, fillWidth);
					fillstart+=width;
					}
				
				pixdata+=fillWidth;				
				pixels+=fillWidth;
				size -= 3;
				}	
				break;
	
			case 0xff: {
				unsigned short skip = *frameData;frameData++;	
				skip+=((unsigned short)(*frameData))<<8;frameData++;			
				pixels+=skip;
				pixdata+=skip;
				size -= 2;
				}
	
				break;
	
			default:
				if(header&0x80) {
					unsigned short skip =(header-0x80)+1;										
					pixels+=skip;
					pixdata+=skip;
					}
				else if(header&0x40) {					
					unsigned char fillColor = *frameData;frameData++;
					unsigned char fillWidth = ((header-0x40)+1);
					
					fillstart = pixdata;
				
					while(fillstart+fillWidth < endpixdata) {
						memset(fillstart, fillColor, fillWidth);
						fillstart+=width;
						}
					
					pixdata+=fillWidth;
					pixels+=fillWidth;
					size--;
					}
				else {
					int copyWidth = (header & 0x3f)+1;
					unsigned char color;
					
					for(int col = 0;col < copyWidth;col++) {						
						color= *frameData;frameData++;
						if(color != 0xff){
							fillstart = pixdata;
							
							while(fillstart < endpixdata) {
								*fillstart = color;
								fillstart+=width;
								}
							}
						pixdata++;
						pixels++;
						size--;
						}
					}			
				break;
		}
	
		size--;
	}
	
	LOG("PL Compacts pixels", pixels);
}


void Scene::loadAni (File *f, int dataIndex) {

	unsigned short int aniType = f->loadShort();// should be 0x02
	LOG("ParseAni DataLen", aniType);
	unsigned short int aniOffset = f->loadShort();// unknown, number of frames?
	LOG("ParseAni Frames?", aniOffset);
	unsigned short int type = 0;//
	int loop;

	while (type != EPlayListAniHeader) {

		type = f->loadShort();

		if (type == ESoundListAniHeader) { // SL

			/*unsigned short int offset =*/ f->loadShort();
			animations->noSounds = f->loadChar();

			for(loop = 0;loop<animations->noSounds;loop++) {

				char* soundName = f->loadString();
				LOG("Soundname ", soundName);
				strcpy(animations->soundNames[loop], soundName);
				delete[] soundName;

			}

		} else if (type == EPlayListAniHeader) {// PL			
			int pos = f->tell();
			int nextPos = f->tell();
			LOG("PL Read position", pos);
			unsigned short int len = f->loadShort();
			unsigned char* buffer = f->loadBlock(len);

			palettes = new ScenePalette(palettes);

			for (int count = 0; count < 256; count++) {

				// Palette entries are 6-bit
				// Shift them upwards to 8-bit, and fill in the lower 2 bits
				palettes->palette[count].r = (buffer[count * 3] << 2) + (buffer[count * 3] >> 4);
				palettes->palette[count].g = (buffer[(count * 3) + 1] << 2) + (buffer[(count * 3) + 1] >> 4);
				palettes->palette[count].b = (buffer[(count * 3) + 2] << 2) + (buffer[(count * 3) + 2] >> 4);
			}

			delete[] buffer;
			palettes->id = dataIndex;

			unsigned short int value = 0;
			int items = 0;
			int validValue = true;
			pos = f->tell();

			LOG("PL Read position start", pos);

			while (validValue) {

				value = f->loadShort();
				LOG("PL Read block start tag", value);
			    int size = f->loadShort();
				LOG("PL Anim block size", size);
				nextPos = f->tell();
				// next pos is intial position + size and four bytes header
				nextPos += size;

				switch (value) {

					case E_EHeader: // END MARKER

						validValue = false;

						break;

					case E11AniHeader: //11

						// Skip back size header, this is read by the surface reader
						LOG("PL 11 Background Type", 0);
						f->seek(-2, false);

						animations->background = f->loadSurface(SW, SH);

						// Use the most recently loaded palette
						video.setPalette(palettes->palette);

						break;

					case E1LAniHeader: {
						LOG("PL 1L Background Type", 0);						
						unsigned char* pixels;
						pixels = new unsigned char[SW* SH];							
						memset(pixels, 0, SW*SH);
						loadCompacted(size, f, pixels, SW, SH);
						animations->background = createSurface(pixels, SW, SH);
						delete[] pixels;
						// Use the most recently loaded palette
						video.setPalette(palettes->palette);					
						}
						break;

					case EFFAniHeader:
						{
						unsigned char* blockData = f->loadBlock(size);
						animations->addFrame(EFFAniHeader, blockData, size);											
						}
						break;

					case ERNAniHeader:
					case ERBAniHeader:					
					case ERLAniHeader:					
					case EMXAniHeader:
						break;
					case ERRAniHeader: // Reverse animation when end found
						animations->reverseAnimation = 1;
						break;

					case ERCAniHeader:
						{
						unsigned char* blockData = f->loadBlock(size);
						animations->addFrame(ERCAniHeader, blockData, size);
						}break;
					case ESquareAniHeader: // Full screen animation frame, that does n't clear the screen first.

						{		
							unsigned char* blockData = f->loadBlock(size);
							animations->addFrame(ESquareAniHeader, blockData, size);														
						}

						break;

					case ESTAniHeader: // Sound item

						{
							unsigned char soundIndex = f->loadChar();
							unsigned char soundNote = f->loadChar();
							unsigned char soundOffset = f->loadChar();
							animations->lastFrame->soundId = soundIndex;
							LOG("PL Audio tag with index", soundIndex);
							LOG("PL Audio tag play at ", soundNote);
							LOG("PL Audio tag play offset ", soundOffset);
						}

						break;

					case 0:

						{

							int longvalue = f->loadInt();

							while (longvalue == 0) {

								longvalue = f->loadInt();
								nextPos += 4;

							}

							f->seek(-4, false);
							value = longvalue;

						}

						break;

					default:

						//LOG("PL Read Unknown type", value);
						validValue = false;

						break;

				}

				pos = f->tell();
				LOG("PL Read position after block should be", nextPos);
				f->seek(nextPos, true);

				if(validValue) items++;

			}

			LOG("PL Parsed through number of items skipping 0 items", items);
			pos = f->tell();
			LOG("PL Read position after parsing anim blocks", pos);

		}

	}

}


void Scene::loadData (File *f) {

	int loop;

	for (loop = 0; loop < dataItems; loop++) {

		f->seek(dataOffsets[loop], true); // Seek to data start
		unsigned short int dataLen = f->loadShort(); // Get get the length of the datablock
		LOG("Data dataLen", dataLen);
		// AN

		if (dataLen == EAnimationData) {

			LOG("Data Type", "ANI");
			animations = new SceneAnimation(animations);
			animations->id = loop;
			loadAni(f, loop);

		} else {

			unsigned char type = f->loadChar();
			LOG("Data Type", type);

			switch (type) {

				case 3:
				case 4: // image
				case 5:
				case 6:

					{

						LOG("Data Type", "Image");
						LOG("Data Type Image index", loop);
						unsigned short int width = f->loadShort(); // get width
						unsigned short int height;

						if (type == 3) height = f->loadChar(); // Get height
						else height = f->loadShort(); // Get height

						f->seek(-2, false);
						images = new SceneImage(images);
						images->image = f->loadSurface(width, height);
						images->id = loop;

					}

					break;

				default:

					LOG("Data Type", "Palette");
					LOG("Data Type Palette index", loop);
					f->seek(-3, false);

					palettes = new ScenePalette(palettes);
					f->loadPalette(palettes->palette);
					palettes->id = loop;

					break;

			}

		}

	}

}


void Scene::loadScripts (File *f) {

	int loop;
	/*int bgIndex = 0;*/
	int textAlignment = 0;
	int textFont = 0;
	int textShadow = -1;
	
	for(loop = 0; loop < scriptItems; loop++) {

	    LOG("\nParse Script", loop);
	    int textPosX = -1;
	    int textPosY = -1;

	    int extraheight = -1;
	    SDL_Rect textRect = { 0,0,0,0 };
	    bool textRectValid = false;
		f->seek(scriptStarts[loop], true); // Seek to data start

		if (f->loadChar() == EScriptStartTag) { // Script tag

			unsigned short int scriptid = f->loadShort();
			LOG("Script id", scriptid);
			int palette = f->loadShort();
			LOG("Script default palette", palette);
			pages[loop].paletteIndex = palette;

			unsigned char type = 0;
			bool breakloop = false;
			int pos = f->tell();

			while(!breakloop && pos < dataOffsets[0]) {

				type = f->loadChar();

				switch(type) {

					case ESceneYesNo:
						{
						pages[loop].askForYesNo = 1;
						LOG("ESceneYesNo", 1);
						}break;
					case ESceneStopMusic:
						{
						pages[loop].stopMusic = 1;
						LOG("ESceneStopMusic", 1);
						}break;
					case ESceneAnimation:
						{
							pages[loop].animLoops = f->loadInt();
							pages[loop].animSpeed = f->loadShort();							
							pages[loop].animIndex = f->loadShort();
							
							LOG("ESceneAnimation loops", pages[loop].animLoops);
							LOG("ESceneAnimation speed", pages[loop].animSpeed);
							LOG("ESceneAnimation anim num", pages[loop].animIndex);							
						}
						break;

					case ESceneAnimationPlayAndContinue:
						{
						pages[loop].nextPageAfterAnim = f->loadChar();
						LOG("ESceneAnimationPlayAndContinue", pages[loop].nextPageAfterAnim);
						}

						break;

					case ESceneFadeType:

						{
							unsigned char fadein = f->loadChar();
							LOG("ESceneFadeType", fadein); 

						}

						break;

					case ESceneBackground:

						pages[loop].bgX[pages[loop].backgrounds] = f->loadShort();
						pages[loop].bgY[pages[loop].backgrounds] = f->loadShort();
						pages[loop].bgIndex[pages[loop].backgrounds] = f->loadShort();

						LOG("ESceneBackground: xpos", pages[loop].bgX[pages[loop].backgrounds]);
						LOG("ESceneBackground: ypos", pages[loop].bgY[pages[loop].backgrounds]);
						LOG("ESceneBackground: index", pages[loop].bgIndex[pages[loop].backgrounds]);

						pages[loop].backgrounds++;

						break;

					case ESceneMusic:

						// Music file name
						pages[loop].musicFile = f->loadString();
						LOG("ESceneMusic", pages[loop].musicFile);

						break;

					case ESceneSomethingElse:

						{

							unsigned char value = 0;//f->loadChar();
							LOG("ESceneSomethingElse", value);

						}

						break;

					case ESceneTextRect: // String

						textRect.x = f->loadShort();
						textRect.y = f->loadShort();
						textRect.w = f->loadShort() - textRect.x;
						textRect.h = f->loadShort() - textRect.y;
						textRectValid = true;
						LOG("Text rectangle xpos", textRect.x);
						LOG("Text rectangle ypos", textRect.y);
						LOG("Text rectangle w", textRect.w);
						LOG("Text rectangle h", textRect.h);

						break;

					case ESceneFontDefine: // Font defnition

						if (nFonts < 5) {

							fonts[nFonts].id = f->loadShort();
							char *fontname = f->loadString();

							LOG("ESceneFontDefine", fontname);
							LOG("ESceneFontDefine with id", fonts[nFonts].id);

							if (strcmp(fontname, "FONT2") == 0)
								fonts[nFonts].font = font2;
							else if (strcmp(fontname, "FONTBIG") == 0)
								fonts[nFonts].font = fontbig;
							else if (strcmp(fontname, "FONTTINY") == 0)
								fonts[nFonts].font = fontiny;
							else if (strcmp(fontname, "FONTMN1") == 0)
								fonts[nFonts].font = fontmn1;
							else if (strcmp(fontname, "FONTMN2") == 0)
								fonts[nFonts].font = fontmn2;
							else fonts[nFonts].font = font2;

							nFonts++;

							delete[] fontname;

						}

						break;

					case ESceneTextPosition:

						textPosX = f->loadShort();
						textPosY = f->loadShort();
						LOG("TextPosition x", textPosX);
						LOG("TextPosition y", textPosY);

						break;

					case ESceneTextColour:

						{

							unsigned short value = f->loadShort();
							LOG("ESceneTextColour", value);

						}

						break;

					case ESceneFontFun:

						{

							unsigned short len = f->loadShort();
							LOG("ESceneFontFun len", len);

							/*while (len) {

								unsigned char data = f->loadChar();
								len--;

							}*/

						}

						break;

					case ESceneFontIndex:

						textFont = f->loadShort();
						LOG("ESceneFontIndex", textFont);

						break;

					case ESceneTextVAdjust:

						extraheight = f->loadShort();
						LOG("ESceneTextVAdjust", extraheight);

						break;

					case ESceneTextSetting:

						{

							unsigned short value = f->loadShort();
							LOG("ESceneTextSetting", value);

						}

						break;

					case ESceneTextShadow:

						{
							char enableShadow = f->loadChar();
							if(enableShadow) {
								textShadow = f->loadChar();
							}
							else
								{
								f->loadChar(); // Skip this value since shadows are turned off
								textShadow = -1; // Turn off shadow , -1 means no shadow colour
								}
							
							LOG("ESceneTextShadow", textShadow);
						}

						break;

					case ESceneTextAlign:

						textAlignment = f->loadChar();
						LOG("ESceneTextAlign", textAlignment);

						break;

					case ESceneTextAlign2:

						{

							unsigned char a = f->loadChar();
							unsigned short b = f->loadShort();
							LOG("ESceneTextAlign2 a", a);
							LOG("ESceneTextAlign2 b", b);

						}

						break;

					case ESceneTextSomething:

						{

							unsigned char a = f->loadChar();
							unsigned short b = f->loadShort();
							LOG("ESceneTextSomething a", a);
							LOG("ESceneTextSomething b", b);

						}

						break;

					case ESceneTextLine:
					case ESceneTextBlock:

						{
							unsigned char datalen = f->loadChar();
							LOG("Text len", datalen);

							SceneText *text = pages[loop].texts + pages[loop].nTexts;

							if (datalen > 0) {

								text->text = f->loadBlock(datalen + 1);
								f->seek(-1, false);

								// Convert number placeholders
								for (int pos = 1; pos < datalen; pos++) {

									if (text->text[pos] == 0x8B) {

										if (loop >= 9)
											text->text[pos - 1] = ((loop + 1) / 10) + 53;

										text->text[pos] = ((loop + 1) % 10) + 53;

									} else if (text->text[pos] == 0x8A) {

										if (scriptItems >= 10)
											text->text[pos - 1] = (scriptItems / 10) + 53;

										text->text[pos] = (scriptItems % 10) + 53;

									}

								}

								text->text[datalen] = 0;

							} else {

								text->text = new unsigned char[1];
								text->text[0] = 0;

							}

							text->alignment = textAlignment;
							text->fontId = textFont;
							text->shadowColour = textShadow;
							
							if(textPosX != -1) {

								text->x = textPosX;
								text->y = textPosY;
								textPosX = -1;
								textPosY = -1;

							}

							if(textRectValid) {

								text->textRect = textRect;
								textRectValid = false;

							}

							if(extraheight != -1) {

								text->extraLineHeight = extraheight;
								extraheight = -1;

							}

							pages[loop].nTexts++;

						}

						break;

					case ESceneTime:

						pages[loop].pageTime = f->loadShort() & 255;
						LOG("Scene time", pages[loop].pageTime);

						break;

					case ESceneBreaker:
					case 0x3e:

						pos = f->tell();
						LOG("Parse script end at position", pos);
						LOG("Parse script end with", type);
						breakloop = true;
						f->loadChar();

						break;

					default:

						pos = f->tell();
						LOG("Parse script end at position", pos);
						LOG("Parse script breaker", type);
						breakloop = true;

						break;

				}

				pos = f->tell();

			}

		}

	}

}