// BlinkenSisters - Hunt for the Lost Pixels
//     Bringing back the fun of the 80s
//
// (C) 2005-07 Rene Schickbauer, Wolfgang Dautermann
//
// See LICENSE for licensing information

#include "globals.h"

#ifndef DISABLE_NETWORK
#include <SDL_net.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "httpclient.h"
#include "errorhandler.h"
#include "memops.h"
#include "drawprimitives.h"
#include "blending.h"
#include "bsscreen.h"


HTTPClient::HTTPClient(const char *server, Uint32 port, bool debug, const char * localProxyurl)  : 
		/* Add useless initialization list here to make the stupid fools happy who wrote the C++ OO specs */
		m_debug(debug), m_ip(0), m_sd(0), oldpercent(0), downloadbg(0), progressbar(0), m_extbuffer(""), m_size(0)
{
	// I *REFUSE* TO OMMIT A *PROPER* VARIABLE INITALIZATION FOR THE ONE ABOVE - CODE SHOULD BE READABLE. IF YOUR COMPILER HAS PROBLEMS WITHOUT
	// THE LIST ABOVE, IT SHOULD BE THE BROWSER THAT GETS FIXED - NOT THE SPECS
	m_debug = debug;
	m_size = 0;
	m_extbuffer = 0;
	m_ip = new IPaddress();
	m_sd = 0;
	//printDebug("Starting new instance");

	downloadbg = BS_IMG_Load_DisplayFormat(configGetPath("downloadbg.png"),DIE_ON_FILE_ERROR);
	progressbar = BS_IMG_Load_DisplayFormat(configGetPath("progressbar.bmp"),DIE_ON_FILE_ERROR);


	sprintf(m_hostname, "%s", server);
	//sprintf(m_hostname, "217.79.181.111");

	printDebug("Resolving host");
	printDebug(m_hostname);
	if (SDLNet_ResolveHost(m_ip, m_hostname, port))
	{
		(*m_ip).host=0 ; /* set m_ip to 0.0.0.0 for basic the networking-test: okay if DNS resolve works */
		fprintf(stderr, "SDLNet_ResolveHost: %s\n", SDLNet_GetError());
		displaymessage(displaymessage_ERROR, "Network error. Host not found", 1000);
		return ; // resolve does not work - Error-status: IP is still 0.0.0.0
	} else {
		printDebug("Got Host address");
	}

	/* proxy server defined? */
	if (localProxyurl != NULL) {
		char proxyhost[255] ; /* rfc2181: A FQDN is limited to 255 chars */
		Uint16 proxyport ;
		sscanf(localProxyurl, "http://%[^:]:%hu", proxyhost, &proxyport);
		SDLNet_ResolveHost(m_ip, proxyhost, proxyport);
	}
}

HTTPClient::~HTTPClient() {
	printDebug("Deleting instance");
	if(m_extbuffer) {
		printDebug("Freeing extbuffer");
		free(m_extbuffer);
	}
	if(m_ip) {
		printDebug("Freeing m_ip");
		delete(m_ip);
	}
	if(m_sd) {
		printDebug("Freeing m_sd");
		SDLNet_TCP_Close(m_sd);
	}

	SDL_FreeSurface(downloadbg);
	SDL_FreeSurface(progressbar);
}

#ifdef SATISFY_CPP_WITH_USELESS_CONSTRUCTORS
// Now two dummy functions for the fools of the C++ consortium who require this, if it's used or not...
// Calling this functions will exit(1) the program, just to shut up the g++ compiler
HTTPClient::HTTPClient(const HTTPClient& bullshit) :
		/* Add useless initialization list here to make the stupid fools happy who wrote the C++ OO specs */
		m_debug(false), m_ip(0), m_sd(0), oldpercent(0), downloadbg(0), progressbar(0), m_extbuffer(""), m_size(0) {
	fprintf(stderr, "You called the copy constructor for HTTPClient... go away\n");
	exit(1);
}
HTTPClient& HTTPClient::operator= (const HTTPClient& bullshit) {
	fprintf(stderr, "You called the assignment operator for HTTPClient... go away\n");
	exit(1);	
}
// End of useless functions
#endif // SATISFY_CPP_WITH_USELESS_CONSTRUCTORS


bool HTTPClient::get(char* relative_url) {
	printDebug("getting url:");
	printDebug(relative_url);
	int contentlength = 0;

	char line[1000];

	/* Open a connection with the IP provided (listen on the host's port) */
	if (!(m_sd = SDLNet_TCP_Open(m_ip)))
	{
		fprintf(stderr, "SDLNet_TCP_Open: %s\n", SDLNet_GetError());
		return false;
	}

	sprintf(line, "GET http://%s%s HTTP/1.1", HTTPSERVER, relative_url);
	if(m_debug) {printf("%s \n",line);}
	if(!sendLine(line)) {
		return false;
	}
	sprintf(line, "Host: %s", m_hostname);
	if(!sendLine(line)) {
		return false;
	}

	if(!sendLine("User-Agent: Blinkensisters Client V" VERSION)) {
		return false;
	}

        if(!sendLine("")) { /* send empty line */
                return false;
        }

	if(!getLine(line)) { // Get Status string
		return false;
	}
	if(! (strstr(line, "HTTP/1.1 200") || strstr(line, "HTTP/1.0 200") )) {
		fprintf(stderr, "No code 200 from server, got '%s' instead\n", line);
		return false;
	}

	sprintf(line, "----");
	while(line[0] != 0) { // Read all header to empty line
		getLine(line);
		char* deli = strchr(line, ' ');
		if(deli) {
			*deli = 0;
			deli++;
			if(strcmp(line, "Content-Length:") == 0) {
				contentlength = atoi(deli);
				if(m_debug) {
					printf("Data segment length: %d\n", contentlength);
				}
			}
		}
	}

	if(!getData(contentlength)) {
		fprintf(stderr, "Could not read complete data segment!");
		return false;
	}

	SDLNet_TCP_Close(m_sd);
	m_sd = 0;

	return true;
}

bool HTTPClient::sendLine(const char* line) {
	char buffer[1000];
	sprintf(buffer, "%s", line);
	strcat(buffer, "\r\n");

	if(m_debug) {
		printf("-> %s\n", line);
	}

	int len = strlen(buffer);
	if (SDLNet_TCP_Send(m_sd, (void *)buffer, len) < len)
	{
		fprintf(stderr, "SDLNet_TCP_Send: %s\n", SDLNet_GetError());
		return false;
	}

	return true;
}

bool HTTPClient::getLine(char* line) {
	char buffer[1000];
	memset(buffer,0,sizeof(buffer));
	char *ptr = buffer;
	int flen = 0;
	bool done = false;

	int len;
	while(!done) {
		len = SDLNet_TCP_Recv(m_sd, ptr, 1);
		if(len < 0)
		{
			fprintf(stderr, "SDLNet_TCP_Recv: %s\n", SDLNet_GetError());
			return false;
		} else if(len == 0) {
			SDL_Delay(1);
		} else {
			if(*ptr == '\n') {
				done = true;
			}
			ptr++;
			flen++;
		}
	}

	if(flen < 2) {
		fprintf(stderr, "Invalid line recieved!");
	}
	ptr--;ptr--;*ptr = 0; // Remove "\r\n"
	sprintf(line, "%s", buffer);

	if(m_debug) {
		printf("<- %s\n", line);
	}

	return true;
}

bool HTTPClient::getData(int length)
{
	showProgress(0);
	oldpercent = 101;
	if(m_extbuffer) {
		free(m_extbuffer);
	}
	m_extbuffer = (char*)malloc(length);
	if(!m_extbuffer) {
		fprintf(stderr, "Failed on malloc!\n");
		return false;;
	}
	char *ptr = m_extbuffer;

	m_size = 0;
	int fsize = length;

	int len;
	while(length > 0) {
		len = SDLNet_TCP_Recv(m_sd, ptr, length);
		if(len < 0)
		{
			fprintf(stderr, "SDLNet_TCP_Recv: %s\n", SDLNet_GetError());
			if(m_extbuffer) {
				printDebug("Freeing extbuffer");
				free(m_extbuffer);
			}
			m_size = 0;
			return false;
		} else if(len == 0) {
			SDL_Delay(1);
		} else {
			length -= len;
			ptr+= len;
			m_size += len;

			Uint32 mproz = (m_size * 100) / fsize;
			showProgress(mproz);
		}
	}

	return true;
}

void HTTPClient::setDebug(bool debug) {
	m_debug = debug;
}

void HTTPClient::printDebug(const char* txt) {
	if(m_debug) {
		printf("sdl_http: %s\n", txt);
	}
}

bool HTTPClient::save(char* fname) {
	if(!m_size || !m_extbuffer) {
		// No file
		return false;
	}
	FILE* ofh = fopen(fname, "wb");
	if(!ofh) {
		DIE(ERROR_FILE_WRITE, fname);
	}
	fwrite(m_extbuffer, m_size, 1, ofh);
	fclose(ofh);
	return true;
}

/* return true (everything okay), if DNS resolving did work (m_ip != 0.0.0.0), false otherwise (DNS resolve did not work, there is a networking problem. This is only a basic test */
bool HTTPClient::networkstatus() {
	if ((*m_ip).host != 0) { return true  ; } else {return false ; } ;
}

void HTTPClient::showProgress(Uint32 percent) {
	// Minimize calls
	if(percent == oldpercent) {
		return;
	}
	oldpercent = percent;

	// Lock Surface if needed
	if (SDL_MUSTLOCK(gScreen))
		if (SDL_LockSurface(gScreen) < 0)
			return;

	if (SDL_MUSTLOCK(downloadbg))
		if (SDL_LockSurface(downloadbg) < 0)
			return;

	if (SDL_MUSTLOCK(progressbar))
		if (SDL_LockSurface(progressbar) < 0)
			return;



	Uint32 bigpitch = gScreen->pitch;
	Uint32 bigpitchBG = downloadbg->pitch;

	long fc_src_long = (long)downloadbg->pixels;
	long fc_dst_long = (long)gScreen->pixels;

	for(Uint32 j=0; j < SCR_HEIGHT; j++) {
		fc_src = (fastcopy *)fc_src_long;
		fc_dest = (fastcopy *)fc_dst_long;
		*fc_dest = *fc_src;
		fc_dst_long += bigpitch;
		fc_src_long += bigpitchBG;
	}

	// Progress BG
	blend_darkenRect(20, 250, 600, 50, 0x00a0a0a0);

	// Progress bar
	if(percent) {
		blend_darkenRect(20, 250, percent*6, 50, 0x00808080);
		//drawrect(20, 250, percent*6, 50, 0x909090);
	}

	Uint32 scrpitch = gScreen->pitch / 4;
	Uint32 tilpitch = progressbar->pitch / 4;

	Uint32 color;
	for(Uint32 ybg = 0; ybg < 50; ybg++) {
		for(Uint32 xbg = 0; xbg < (percent*6); xbg++) {
			color = ((Uint32*)progressbar->pixels)[xbg + ybg * tilpitch];

			if(color != 0x0000ff00) {
				((Uint32*)gScreen->pixels)[xbg + 20 + (250 + ybg) * scrpitch] = color;
			}

		}
	}


	// Progress outline
	drawrect(20, 250, 600, 1, 0xd0d0d0);
	drawrect(20, 250, 1, 50, 0xd0d0d0);
	drawrect(20, 300, 600, 1, 0xd0d0d0);
	drawrect(619, 250, 1, 50, 0xd0d0d0);





	// Unlock Surface if needed
	if (SDL_MUSTLOCK(progressbar))
		SDL_UnlockSurface(progressbar);
	if (SDL_MUSTLOCK(downloadbg))
		SDL_UnlockSurface(downloadbg);
	if (SDL_MUSTLOCK(gScreen))
		SDL_UnlockSurface(gScreen);
	BS_Flip(gScreen); /* Update whole screen */

}

#endif // DISABLE_NETWORK


