#include <errno.h>
#include <math.h>
#include <network.h>
#include <ogc/lwp_watchdog.h>
#include <ogcsys.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/dir.h>
#include <unistd.h>
#include <wiiuse/wpad.h>

#define IP_ADDRESS        "130.237.152.209" // IP address to connect to
#define SOCKET_PORT       80 
#define NET_BUFFER_SIZE   1024

const char *CRLF = "\r\n";
const u32 CRLF_LENGTH = 2;

static volatile u8 reset = 0;
static lwp_t reset_thread;

s32 server;

static void reset_called() 
{
	reset = 1;
}

static void *run_reset_thread(void *arg) 
{
	while (!reset && !(WPAD_ButtonsHeld(0) & WPAD_BUTTON_HOME)) 
	{
		sleep(1);
		WPAD_ScanPads();
	}
	printf("\nShutting down...\n");
	exit(0);
}

u8 initialise_reset_button()
{
	s32 result = LWP_CreateThread(&reset_thread, run_reset_thread, NULL, NULL, 0, 80);
	if (result == 0) SYS_SetResetCallback(reset_called);
	return !result;
}

typedef s32 (*transferrer_type)
(
   s32 s, 
   void *mem, 
   s32 len
);

inline static s32 transfer_exact(s32 s, char *buf, s32 length, transferrer_type transferrer)
{
    s32 bytes_transferred = 0;
    s32 remaining = length;
    while (remaining) 
    {
        if ((bytes_transferred = transferrer(s, buf, remaining > NET_BUFFER_SIZE ? NET_BUFFER_SIZE : remaining)) > 0) 
        {
            remaining -= bytes_transferred;
            buf += bytes_transferred;
        } else if (bytes_transferred < 0) {
            return bytes_transferred;
        } else {
            return -ENODATA;
        }
    }
    return 0;
}

inline s32 write_exact(s32 s, char *buf, s32 length) 
{
    return transfer_exact(s, buf, length, (transferrer_type)net_write);
}

// Write our message to the server
static s32 send_message(s32 server, char *msg)
 {
	u32 msglen = strlen(msg);
	char msgbuf[msglen + 1];
	if (msgbuf == NULL) return -ENOMEM;
	sprintf(msgbuf, "%s", msg);
	printf("Sent message: %s", msgbuf);
	return write_exact(server, msgbuf, msglen);
}


static s32 initialise_network() {
    s32 result;
    while ((result = net_init()) == -EAGAIN);
    return result;
}

int openNetwork(void) 
{    
	if (initialise_reset_button()) 
    {
		printf("To exit, press the Home button.\n\n");
	} 
    else 
    {
		printf("Unable to start reset thread - hold down the power button to exit.\n");
	}
	
    printf("Waiting for network to initialise...\n");
    if ( initialise_network()>= 0) 
    {
        char myIP[16];
        if (if_config(myIP, NULL, NULL, true) < 0) 
        {
            printf("Error reading IP address, exiting");
			return 1;
        }
        printf("Network initialised [%s].\n",myIP);
    } 
    else 
    {
      printf("Unable to initialise network, exiting");
	  return 1;
    }
	return 0;
}

int sendRequest(void)
{
	struct sockaddr_in connect_addr;
	char buf[NET_BUFFER_SIZE];
	s32 offset = 0;
	s32 bytes_read;
	
    printf("Attempting to connect to server...\n");
		
    server = net_socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    if (server < 0) 
	{
	   printf("Error creating socket, exiting");
	   return 1;
	}
	
	memset(&connect_addr, 0, sizeof(connect_addr));
	connect_addr.sin_family = AF_INET;
	connect_addr.sin_port = SOCKET_PORT;
	connect_addr.sin_addr.s_addr= inet_addr(IP_ADDRESS);
	
	if (net_connect(server, (struct sockaddr*)&connect_addr, sizeof(connect_addr)) == -1)
    {
	   net_close(server);
	   printf("Failed to connect to the remote server.\n");
	   return 1;
	}	
	printf("Connection established.\n\n");

      // Get homebrew list
	send_message(server, "GET /wiki/Pong2 HTTP/1.0\r\nHost: www.wiibrew.org\r\n\r\n");
	
      // Reads each response line that the server replies to us with
	while (offset < (NET_BUFFER_SIZE - 1)) 
      {
		char *offset_buf = buf + offset;
		if ((bytes_read = net_read(server, offset_buf, NET_BUFFER_SIZE - 1 - offset)) < 0) 
        {
			printf("Read error %i occurred.\n", bytes_read);
			net_close(server);
			sleep(1);
			break;
		} 
        else if (bytes_read == 0) 
        {
			break; // EOF from client
		}
		offset += bytes_read;
		buf[offset] = '\0';
		
		char *next;
		char *end;
		for (next = buf; (end = strstr(next, CRLF)); next = end + CRLF_LENGTH) 
        {
			*end = '\0';
			
			if (*next) 
            {
				char *cmd_line = next;
				printf("Server Response: %s\n", cmd_line); // Each line that the server responses with is printed
			}
		}
		
		if (next != buf) 
        { 
			// some lines were processed
			offset = strlen(next);
			char tmp_buf[offset];
			memcpy(tmp_buf, next, offset);
			memcpy(buf, tmp_buf, offset);
		}         
	}
	return 0;
}

int closeNetwork(void) 
{
	net_close(server);	
	return 0;
}


