#include <windows.h>
#include <winsock.h>

#include "resource.h"
#include "main.h"

extern char szAppName[];
extern char username[20], password[20], title[255];
extern char host[255];
extern int port;

extern bool use_auth;

extern HWND hwnd;


// Base64 character array
//					   0         1         2         3         4         5         6
//					   01234567890123456789012345678901234567890123456789012345678901234
const char bchars[] = "=BCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+*"; 


// --------------------------------------------------------------------------------
// Start winsock
bool StartWinSock()
{
	WORD wVersionRequested = MAKEWORD(2,2);
	WSADATA wsaData;
	
	if (WSAStartup(wVersionRequested, &wsaData ) != 0 ) 
	{
		MessageBox(NULL, "ERROR: Winsock not available", szAppName, MB_OK);
		return false;
	}
	return true;
}


// --------------------------------------------------------------------------------
// Low level socket writing function - strings
void prints(SOCKET socket, char * string)
{
	int i=0;
	while (string[i] != 0)
		i++;

	if (send(socket,string,i,0) == SOCKET_ERROR)
	{	
		//MessageBox(NULL,"Socket send() error!",szAppName,MB_OK);
	}
}

// Low level socket writing function - integers
void printsi(SOCKET socket,int number)
{
	char buffer[256];
	wsprintf(buffer,"%d",number);	// Stick it in a string and call prints() 
	prints(socket,(char*)&buffer);
}


// --------------------------------------------------------------------------------
// Opens socket
bool MakeRequest(char * host, int port, char * url, bool verbose, bool auth, bool gettitle)
{
	//MessageBox(NULL,"Entering MakeRequest","wawitray",MB_OK | MB_TOPMOST);

	SOCKET ssocket = socket(AF_INET,SOCK_STREAM,0);
	if (ssocket == INVALID_SOCKET)
	{
		if (verbose)
			MessageBox(NULL, "ERROR: Couldn't create socket", szAppName, MB_OK);
		return false;
	}


	struct sockaddr_in server_address;
	ZeroMemory(&server_address,sizeof(server_address));

	server_address.sin_family = AF_INET;
	
	server_address.sin_addr.s_addr = inet_addr(host);
	server_address.sin_port = htons(port);

	if (SOCKET_ERROR == connect(ssocket,(sockaddr*)&server_address,sizeof(sockaddr_in)))
	{
		if (verbose)
			MessageBox(hwnd,"Couldn't connect to remote Winamp!\n\nCheck you have the address and port right.",szAppName,MB_OK|MB_ICONERROR);
		return false;
	}

	char method[6];
	if (gettitle)
		wsprintf(method,"GET");
	else
		wsprintf(method,"HEAD");

	char request[100];
	
	if (auth)
	{
		char b64userpass[100], userpass[100];
		wsprintf(userpass,"%s:%s",username,password);
		ToBase64(userpass,b64userpass);
		wsprintf(request,"%s %s HTTP/1.0\nAuthorisation: Basic %s\n\n",method,url,b64userpass);
	}
	else
	{
		wsprintf(request,"%s %s HTTP/1.0\nConnection: Close\n\n",method,url);
	}
	prints(ssocket,request);

	int res = GetResponse(ssocket,gettitle);

	closesocket(ssocket);

	switch(res)
	{
	case RES_OK:
		break;
	case RES_AUTH:
		if (verbose)
			MessageBox(hwnd,"Incorrect username or password!\n\nCheck the details are right in the Config window.",szAppName,MB_OK|MB_ICONERROR | MB_TOPMOST);
		break;
	case RES_NOTFOUND:
		if (verbose)
			MessageBox(hwnd,"Received a 404 Page Not Found response, which the Winamp HTTP Server does not produce!\n\nAre you sure your host and port settings are correct?",szAppName,MB_OK | MB_ICONERROR | MB_TOPMOST);
		break;
	case RES_BADREQUEST:
		if (verbose)
			MessageBox(hwnd,"Received a 400 Bad Request message, which the Winamp HTTP Server does not produce!\n\nCheck your Host and Port settings are correct.",szAppName, MB_OK | MB_ICONERROR | MB_TOPMOST);
	case RES_UNKNOWN:
	case RES_FAIL:
		if (verbose)
			MessageBox(hwnd,"Unknown HTTP Response",szAppName,MB_OK | MB_ICONWARNING | MB_TOPMOST);
		break;
	}

	//MessageBox(hwnd,"Leaving MakeRequest","wawitray",MB_OK | MB_TOPMOST);
	return true;
}


int WASend(char * command)
{
	MakeRequest(host,port,command,true,use_auth,false);
	return 1;
}




// --------------------------------------------------------------------------------
// Converts a string to Base64
void ToBase64(char * instr, char * outstr)
{
	//MessageBox(NULL,instr,"instr",MB_OK);

	int i=0;
	int o=0;
	int j = lstrlen(instr);

	bool done = false;

	while(instr[i] != 0 && done == false)
	{
		outstr[o] = bchars[(instr[i] & 0xFC) / 4];
		outstr[o+1] = bchars[((instr[i] & 0x03) * 16) + ((instr[i+1] & 0xF0) / 16)];
		if (instr[i+1] == 0)
		{
			outstr[o+2] = '=';
			outstr[o+3] = '=';
			done = true;
		}
		else
		{
			outstr[o+2] = bchars[((instr[i+1] & 0x0F) * 4) + ((instr[i+2] & 0xC0) /64)];
			if (instr[i+2] == 0)
			{
				outstr[o+3] = '=';
				done = true;
			}
			else
				outstr[o+3] = bchars[(instr[i+2] & 0x3F)];
		}

		o += 4;
		i += 3;
	}

	outstr[o] = 0;
}


// --------------------------------------------------------------------------------
// Receives return HTTP header
int GetResponse(SOCKET socket, bool gettitle)
{
	char buffer[2048],http_version[20],response[5],text[100];
	int buffer_len;
	int buffer_filled = 0;
	int buffer_left;
	bool complete = false;

	int breaks;
	if (gettitle)
		breaks = 1;
	else
		breaks = 0;
		

	while(true)
	{
		// receive data
		buffer_len = sizeof(buffer);
		buffer_left = buffer_len-buffer_filled;
		if (buffer_left <= 0)
			buffer_len = 0;
		else
			buffer_len = recv(socket,buffer + buffer_filled,buffer_len-buffer_filled,0);

		if (buffer_len != SOCKET_ERROR && buffer_len != 0)
		{
			buffer_filled += buffer_len;
			// scan for end of header
			for (int i=0;i<=buffer_filled;i++)
				if (buffer[i]=='\n' && (buffer[i+1]=='\n' || buffer[i+2]=='\n'))
				{
					if (breaks == 0)
					{
						buffer[i+1] = 0;
						//MessageBox(NULL,buffer,"gen_httpsrv debug", MB_OK);
						char * chptr = (char*)&buffer;
						
						chptr = GetAString(chptr,http_version);
						chptr = GetAString(chptr,response);
						chptr = GetAString(chptr,text);
						chptr = SkipHeader(chptr);

						if (StrComp(response,"200"))
						{
							if (gettitle)
								GetALine(chptr,title);
							return RES_OK;
						}
						if (StrComp(response,"401"))
							return RES_AUTH;
						if (StrComp(response,"404"))
							return RES_NOTFOUND;
						if (StrComp(response,"400"))
							return RES_BADREQUEST;
						
						return RES_UNKNOWN;
					}
					else
						breaks--;
				}
		}
		else
		{
			return RES_FAIL;
		}
	}
}


	

// --------------------------------------------------------------------------------
// Retrieves next token from buffer
char * GetAString(char* chptr, char* text)			// Old GetAString - only accepts single words
{
	int bptr = 0;
	int tptr = 0;
	while (chptr[bptr] == ' ' || chptr[bptr] == '\n' || chptr[bptr] == '\r')	// skip leading spaces and stuff
		bptr++;

	while (chptr[bptr] != 0 && chptr[bptr] != ' ' && chptr[bptr] != '\n' && chptr[bptr] != '\r' && bptr < 255)	// while current char is a letter 
	{
		text[tptr] = chptr[bptr];					// add letter to text[]
		bptr++;
		tptr++;
	}
	text[tptr] = 0;
	chptr = chptr + bptr;
	return chptr;					// return pointer to the end of this string
}

// --------------------------------------------------------------------------------
// Skips the rest of the HTTP header
char * SkipHeader(char * chptr)
{
	while (chptr[0] != '\n' || (chptr[1] != '\n' && chptr[2] != '\r'))
		chptr++;
	return chptr;
}
// --------------------------------------------------------------------------------
// Gets a whole line
char * GetALine(char * chptr, char * text)
{
	int bptr = 0;
	int tptr = 0;
	while (chptr[bptr] == ' ' || chptr[bptr] == '\n' || chptr[bptr] == '\r')	// skip leading spaces and stuff
		bptr++;

	while (chptr[bptr] != 0 && chptr[bptr] != '\n' && chptr[bptr] != '\r' && bptr < 255)	// while current char is a letter 
	{
		text[tptr] = chptr[bptr];					// add letter to text[]
		bptr++;
		tptr++;
	}
	text[tptr] = 0;
	chptr = chptr + bptr;
	return chptr;					// return pointer to the end of this string
}


// --------------------------------------------------------------------------------
// Strcmp() without using Clib
bool StrComp(char * string1, char * string2)
{
	int ptr=0;

	while (ToLowerCase(string1[ptr]) == ToLowerCase(string2[ptr]))		// while the strings are the same, keep going
	{
		if (string1[ptr] == 0)					// if they're the same and 0, they're identical null terminated strings
			return true;
		ptr++;
	}
	return false;								// if not the same, they're... not the same
}

// --------------------------------------------------------------------------------
// Convert a char to lower case
char ToLowerCase(char c)
{
	if (c >= 'A' && c <= 'Z')
		c += 32;
	return c;
}
