//
// Created by Ben Bergkamp on 2/14/18.
//

#include "SocketReader.h"

char * GetArbitrarySizeBuffer(SSL* ssl)
	{

	int buf_size = 10240;
	int current_size = buf_size;
	char* ssl_buffer = new char[buf_size];
	char* front = ssl_buffer;
	int bytes;

	while ( ( bytes = SSL_read( ssl, front, buf_size ) ) > 0 )
	{

		current_size  += buf_size;
		char *temp = new char[current_size];
		strcpy(temp, ssl_buffer);

		front = temp + strlen(ssl_buffer);
		delete[] ssl_buffer;
		ssl_buffer = temp;
	}

	return ssl_buffer;
	}


char * GetArbitrarySizeBuffer(int s )
	{

	int buf_size = 10240;
	int current_size = buf_size;
	char* http_buff = new char[buf_size];
	char* front = http_buff;
	int bytes;

	while ( ( bytes = recv( s, front, buf_size, 0 ) ) > 0 )
		{

		current_size  += buf_size;
		char *temp = new char[current_size];
		strcpy(temp, http_buff);

		front = temp + strlen(http_buff);
		delete[] http_buff;
		http_buff = temp;
		}

	return http_buff;
	}




void SocketReader::httpRequest()
	{
	int s = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
	assert( s != -1 );

	// Get the host address.

	struct hostent *host = gethostbyname( url.Host );
	assert( host );

	struct sockaddr_in address;
	memset( &address, 0, sizeof( address ) );
	address.sin_family = AF_INET;
	address.sin_port = htons( 80 );
	memcpy( &address.sin_addr, host->h_addr, host->h_length );

	// Connect to the host.

	int connectResult = connect( s, ( struct sockaddr * )&address,
								 sizeof( address ) );
	assert( connectResult == 0 );

	// Send a GET message for the desired page.

	cout << "Socket Reader is pulling from the web" << endl;

	string getMessage = "GET ";
	getMessage += url.CompleteUrl;
	getMessage += " HTTP/1.1\r\nHost: ";
	getMessage += url.Host;
	getMessage += "\r\nConnection: close\r\n\r\n";

	cout << getMessage << endl;
	send( s, getMessage.c_str( ), getMessage.length( ), 0 );

	// Read from the socket until there's no more data.

	char HTTPbuffer[ 10240 ];
	int bytes;


	while ( ( bytes = recv( s, buffer, sizeof( buffer ), 0 ) ) > 0 )
		write( 1, buffer, bytes );

	buffer = GetArbitrarySizeBuffer(s);

	close( s );
	return;
	}


void SocketReader::httpsRequest(){

	struct hostent *host = gethostbyname( url.Host );
	assert( host );

	struct sockaddr_in address;
	memset( &address, 0, sizeof( address ) );
	address.sin_family = AF_INET;
	address.sin_port = htons( 443 );
	memcpy( &address.sin_addr, host->h_addr, host->h_length );

	// Create a TCP/IP socket.

	int s = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
	assert( s != -1 );

	// Connect the socket to the host address.

	int connectResult = connect( s, ( struct sockaddr * )&address,
										  sizeof( address ) );
	assert( connectResult == 0 );

	// Build an SSL layer and set it to read/write
	// to the socket we've connected.


	SSL_library_init( );

	SSL_CTX *ctx = SSL_CTX_new( SSLv23_method( ) );
	assert( ctx );
	SSL *ssl = SSL_new( ctx );
	assert( ssl );

	SSL_set_fd( ssl, s );

	// Establish an SSL connection.

	int sslConnectResult = SSL_connect( ssl );
	assert( sslConnectResult == 1 );

	// Send a GET message for the desired page through the SSL.

	string getMessage = "GET ";
	getMessage += url.CompleteUrl;
	getMessage += " HTTP/1.1\r\nHost: ";
	getMessage += url.Host;
	getMessage += "\r\nConnection: close\r\n\r\n";

	cout << getMessage << endl;
	SSL_write( ssl, getMessage.c_str( ), getMessage.length( ) );

	// Read from the SSL until there's no more data.

	buffer = GetArbitrarySizeBuffer(ssl);

	SSL_shutdown( ssl );
	SSL_free( ssl );
	SSL_CTX_free( ctx );
	close( s );


	}



void SocketReader::fillBuffer(  )
	{
	if ( !strcmp(url.Service , "http" ) )
			httpRequest();
	else
			httpsRequest();


	}