#ifndef DISABLE_NETWORK

#include "globals.h"
#include <SDL_net.h>
#include <string.h>
#include <math.h>
#include "blpstream.h"
#include <BlinkenLib.h>
#include "fgobjects.h"
#include "errorhandler.h"
#include "engine.h"
#include "levelhandler.h"
#include "gameengine.h"
#include "monstersprites.h"
#include "BlinkenMovie.h"

// global
BLP_Frame currentBLPFrame;
Uint32 currentBLPFrameNum;
Uint32 currentBLPMovieFrameNum;

// more or less local
UDPsocket BLPsd;
IPaddress BLPsrvadd;
UDPpacket *BLPp;
Uint32 blpLastTick;
Uint32 blpLastFrameTick;

stBlinkenMovie * pMovie;


void initBLPSocket() {
	// Reset frame number
	currentBLPFrameNum = 0;

	// hardcode fixed values of frame
	currentBLPFrame.magic[0] = (char) 0xFE;
	currentBLPFrame.magic[1] = (char) 0xED;
	currentBLPFrame.magic[2] = (char) 0xBE;
	currentBLPFrame.magic[3] = (char) 0xEF;

	currentBLPFrame.width[0] = (char) 0x00;
	currentBLPFrame.width[1] = (char) 0x12;
	currentBLPFrame.height[0] = (char) 0x00;
	currentBLPFrame.height[1] = (char) 0x08;

	// Open UDP Port
	// Get the socket

#ifdef BLPRANDOM_SOURCEPORT
	if (!(BLPsd = SDLNet_UDP_Open(0))) {
#else
	if (!(BLPsd = SDLNet_UDP_Open(blpport))) {
#endif
		fprintf(stderr, "SDLNet_UDP_Open: %s\n", SDLNet_GetError());
		exit(EXIT_FAILURE);
	}

	//   Resolve server name
	if (SDLNet_ResolveHost(&BLPsrvadd, blphost, blpport)) {
		fprintf(stderr, "SDLNet_ResolveHost(%s %d): %s\n", blphost, blpport, SDLNet_GetError());
		exit(EXIT_FAILURE);
	}

	/* Allocate memory for the packet */
	if (!(BLPp = SDLNet_AllocPacket(sizeof(currentBLPFrame)))) {
		fprintf(stderr, "SDLNet_AllocPacket: %s\n", SDLNet_GetError());
		exit(EXIT_FAILURE);
	}

}

void deInitBLPSocket() {
	SDLNet_FreePacket(BLPp);
	SDLNet_UDP_Close(BLPsd);
}

void initBLPFrames() {
	blpLastTick = BS_GetTicks() + 10;
}

void deInitBLPFrames() {
	// Nothing to do
}

void sendBLPFrame() {
	// Max 50 FPS
	Uint32 tick = BS_GetTicks();
	if(blpLastTick > tick) {
		return;
	}
	blpLastTick = tick + 20;

	// Configure the packet
	BLPp->address.host = BLPsrvadd.host;  /* Set the destination host */
	BLPp->address.port = BLPsrvadd.port;  /* And destination port */

	// --- HERE, we calculate the pixel data

	// set bg: all off
	clearBLPFrame();

	showBLPTiles();
	showBLPFgObjects();
	showBLPPixels();
	showBLPMonster();

	// paint kate
	currentBLPFrame.pixels[BLPKATE_X + (BLPKATE_Y * BLPSCR_W)] = (char) 0xff;

	// --- end of "calculate the pixel data"

	// New frame number for smoother rides
	currentBLPFrameNum++;
	if(currentBLPFrameNum > 0x00FFFFFF) {
		// Rollover a bit early just in case to avoid bugs in recievers
		currentBLPFrameNum = 0;
	}

	// Write framenum in big-endian (high byte first)
	currentBLPFrame.frameNum[3] = (char)(currentBLPFrameNum & 0xff);
	currentBLPFrame.frameNum[2] = (char)((currentBLPFrameNum >> 8) & 0xff);
	currentBLPFrame.frameNum[1] = (char)((currentBLPFrameNum >> 16) & 0xff);
	currentBLPFrame.frameNum[0] = (char)((currentBLPFrameNum >> 24) & 0xff);

	// Send packet
	memcpy(BLPp->data, &currentBLPFrame, sizeof(currentBLPFrame));
	BLPp->len = sizeof(currentBLPFrame);
	SDLNet_UDP_Send(BLPsd, -1, BLPp);

}

void clearBLPFrame() {
	for(Uint32 blp_i = 0; blp_i < 144; blp_i++) {
		currentBLPFrame.pixels[blp_i] = 0x00;
	}
}

void showBLPTiles() {
	Sint32 xpos;
	Sint32 ypos;
	Uint32 xoffs=(Uint32)round(spritex+BLP_XOFFSET)/TILESIZE;
	Uint32 yoffs=(Uint32)round(spritey+BLP_YOFFSET)/TILESIZE;

	DISPLAYLIST *tthis = lhandle.dlist;
	while(tthis) {
		xpos = (Uint32)round(tthis->x+TILESIZE/2)/TILESIZE - xoffs + BLPKATE_X;
		ypos = (Uint32)round(tthis->y)/TILESIZE - yoffs + BLPKATE_Y;
		if(xpos >= BLPSCR_M && xpos < BLPSCR_W && ypos >= BLPSCR_M && ypos < BLPSCR_H && tthis->type < 10) {
			currentBLPFrame.pixels[xpos + (ypos * BLPSCR_W)] = 0x33;
		}
		tthis = tthis->next;
	}
}

void showBLPFgObjects() {
	Sint32 xpos, xpos_to;
	Sint32 ypos, ypos_to;
	Uint32 xoffs=(Uint32)round(spritex+BLP_XOFFSET)/TILESIZE;
	Uint32 yoffs=(Uint32)round(spritey)/TILESIZE;
	char switchcolor=0x42;
	if (BS_GetTicks() % 2300 > 1200) {
		switchcolor = (char) 0xaa;
	}
	char monstercolor=0x40;
	if (BS_GetTicks() % 300 > 150) {
		monstercolor = (char) 0xf0;
	}

	for(Uint32 i = 0; i < MAX_FGOBJECTS; i++) {
		if(!fgObjs[i]) {
			continue;
		}
		SDL_Surface *tempsurface = getFGSurface(fgObjs[i]->gfxobj);
		xpos = (Uint32)round(fgObjs[i]->x+TILESIZE/2)/TILESIZE - xoffs + BLPKATE_X;
		xpos_to =  (Uint32)round(tempsurface->w/TILESIZE) + xpos - 1;
		ypos = (Uint32)round(fgObjs[i]->y)/TILESIZE - yoffs + BLPKATE_Y;
		ypos_to = (Uint32)round(tempsurface->h/TILESIZE) + ypos - 1;

		if(xpos<BLPSCR_W && xpos_to>BLPSCR_W && xpos>0)
			xpos_to=BLPSCR_W-1;
		if(xpos<0 && xpos_to<BLPSCR_W && xpos_to>0)
			xpos=0;
		if(ypos<BLPSCR_H && ypos_to>BLPSCR_H && ypos>0)
			ypos_to=BLPSCR_H-1;
		if(ypos>BLPSCR_H && ypos_to<BLPSCR_H && ypos_to>0)
			ypos=BLPSCR_H-1;
		if(ypos<0 && ypos_to<BLPSCR_H && ypos_to>0)
			ypos=0;
		if(fgObjs[i]->isVisible && xpos_to < BLPSCR_W && ypos_to < BLPSCR_H && xpos >= 0 && ypos >= 0) {

			// draw switches or other non-blocking, non-killing and non-pixel Objects as slow blinking object
			for (Sint32 x = xpos; x <= xpos_to; x++) {
				for (Sint32 y = ypos; y <= ypos_to; y++) {
					currentBLPFrame.pixels[x + (y * BLPSCR_W)] = switchcolor;
				}
			}

			// draw blocking-Objects as Wall-Tiles
			if(fgObjs[i]->isBlocking) {
				for (Sint32 x = xpos; x <= xpos_to; x++) {
					for (Sint32 y = ypos; y <= ypos_to; y++) {
						currentBLPFrame.pixels[x + (y * BLPSCR_W)] = 0x44;
					}
				}
			}

			// draw pixel-Objects as Pixels
			if(fgObjs[i]->isPixel) {
				for (Sint32 x = xpos; x <= xpos_to; x++) {
					for (Sint32 y = ypos; y <= ypos_to; y++) {
						currentBLPFrame.pixels[x + (y * BLPSCR_W)] = (char)blinkbright;
					}
				}
			}

			// draw Elevator-Objects
			if(fgObjs[i]->isElevator) {
				for (Sint32 x = xpos; x <= xpos_to; x+=2) {
					for (Sint32 y = ypos; y <= ypos_to; y++) {
						currentBLPFrame.pixels[x + (y * BLPSCR_W)] = switchcolor;
					}
				}
			}

			// draw killing-Objects as Monsters
			if(fgObjs[i]->isKilling) {
				for (Sint32 x = xpos; x <= xpos_to; x++) {
					for (Sint32 y = ypos; y <= ypos_to; y++) {
						currentBLPFrame.pixels[x + (y * BLPSCR_W)] = monstercolor;
					}
				}
			}

		}
	}
}

void showBLPPixels() {
	Sint32 xpos;
	Sint32 ypos;
	Uint32 xoffs=(Uint32)round(spritex+BLP_XOFFSET)/TILESIZE;
	Uint32 yoffs=(Uint32)round(spritey+BLP_YOFFSET)/TILESIZE;

	DISPLAYLIST *tthis = lhandle.pixels;
	while(tthis) {
		xpos = (Uint32)round(tthis->x+TILESIZE/2)/TILESIZE - xoffs + BLPKATE_X;
		ypos = (Uint32)round(tthis->y)/TILESIZE - yoffs + BLPKATE_Y;
		if(tthis->type != PIXELTYPE_NONE && xpos >= BLPSCR_M && xpos < BLPSCR_W && ypos >= BLPSCR_M && ypos < BLPSCR_H) {

			// draw "#" and "P"
			if(tthis->type == PIXELTYPE_PIXEL || tthis->type == PIXELTYPE_POWER) {
				currentBLPFrame.pixels[xpos + (ypos * BLPSCR_W)] = (char)blinkbright;
			}

			// draw "+" and "~"
			if(tthis->type == PIXELTYPE_RESPAWN) {
				currentBLPFrame.pixels[xpos + (ypos * BLPSCR_W)] = (char) 0x99;
			}

			// draw "-"
			if(tthis->type == PIXELTYPE_EXIT) {
				currentBLPFrame.pixels[xpos + (ypos * BLPSCR_W)] = (char) 0x80;
			}
			
			// draw "$" and "*"
			if(tthis->type == PIXELTYPE_POINTS || tthis->type == PIXELTYPE_LIVE) {
				currentBLPFrame.pixels[xpos + (ypos * BLPSCR_W)] = (char) 0x42;
			}

		}
		tthis = tthis->next;
	}
}

void showBLPMonster() {
	Sint32 xpos;
	Sint32 ypos;
	Uint32 xoffs=(Uint32)round(spritex+BLP_XOFFSET)/TILESIZE;
	Uint32 yoffs=(Uint32)round(spritey+BLP_YOFFSET)/TILESIZE;
	char color = (char) 0x40;

	if (BS_GetTicks() % 300 > 150) {
		color = (char) 0xf0;
	}

	MONSTERS *tthis = lhandle.monsters;
	while(tthis) {
		if(tthis->isAlive) {
			xpos = (Uint32)round(tthis->monsterx+TILESIZE/2)/TILESIZE - xoffs + BLPKATE_X;
			ypos = (Uint32)ceil(tthis->monstery)/TILESIZE - yoffs + BLPKATE_Y;
			if(xpos >= BLPSCR_M && xpos < BLPSCR_W && ypos >= BLPSCR_M  && ypos < BLPSCR_H) {
				currentBLPFrame.pixels[xpos + (ypos * BLPSCR_W)] = color;
			}
		}
		tthis = tthis->next;
	}
}

void initBLPMovie(const char* filename) {
	currentBLPMovieFrameNum = 0;
	blpLastFrameTick = 0;
	char fullfname[MAX_FNAME_LENGTH];
	/*char infname[MAX_FNAME_LENGTH];
	sprintf(fullfname, "%s", configGetPath(filename));*/
	sprintf(fullfname, "%s", filename);
	pMovie = BlinkenMovieLoad(fullfname);
	if ((pMovie) == NULL)
		printf("%s not found\n",fullfname);
	blpLastTick = BS_GetTicks();
}

bool showBLPMovie() {
	Uint32 tick = BS_GetTicks();
	if(blpLastTick > tick) {
		return false;
	}
	// Configure the packet
	BLPp->address.host = BLPsrvadd.host;  /* Set the destination host */
	BLPp->address.port = BLPsrvadd.port;  /* And destination port */
	Uint32 len, framescount;
	
	//write pMovie on top
	framescount = BlinkenMovieGetFrameCnt(pMovie);
	if (pMovie!=NULL && currentBLPMovieFrameNum<=framescount) {
		stBlinkenFrame * pFrame = 0;
		while(blpLastTick <= tick && currentBLPMovieFrameNum<=framescount) {
			pFrame = BlinkenMovieGetFrame(pMovie, currentBLPMovieFrameNum++);
			blpLastTick += BlinkenFrameGetDuration(pFrame);
		}
		len = BlinkenFrameToNetwork( pFrame, BlinkenProtoEblp , (char *)&currentBLPFrame, sizeof( currentBLPFrame ) );
		if( len > 0 ) {
			//send( udpSocket, buffer, len, 0 );
			// Send packet
			memcpy(BLPp->data, &currentBLPFrame, len);
			BLPp->len = len;
			SDLNet_UDP_Send(BLPsd, -1, BLPp);
		}

	} else {
		return false;
	}

	return true; // true = movie still running, false = movie finished*/
}

void deInitBLPMovie() {
	// This MUST stop the movie, no matter if it's finished or still running
	BlinkenMovieFree(pMovie);
}
#endif // DISABLE_NETWORK

