#include "httpClient.h"

/* Constructor */
httpClient::httpClient():oasys::Thread("http proxy server"){
   cout<< "The HTTP Proxy Server is created" <<endl;
   mydtn_ = new dtnClient(this); 	
}// class::method::inheritance

/* Destructor */
httpClient::~httpClient(){
  cout<< "The HTTP Proxy Server is terminated"<<endl;

  if(mydtn_ != NULL)
  {
     delete mydtn_; 
  }

}

/* Main thread to get http data */
void httpClient::run(){

	cout<<"Open dtn handler"<<endl; 
	mydtn_->open_dtn();         	
	// get local eid as source
	source_ = mydtn_->get_local_eid(); 
	cout<<"HTTP Proxy Server Eid: ["<<source_<<"]"<<endl;
	cout<<"Run dtn client..."<<endl;
	mydtn_->start();		// Start dtn client thread

	while(1)
	{
		sleep(1);
	}

	mydtn_->close_dtn();   

}

void httpClient::get_data(char* buffer, size_t length, string dest)
{

	CURL *curl; 
	CURLcode res; 
	int connfd;
	string requestURI;
	struct MemoryStruct chunk;
	bool postflag = false;
	size_t post_length;  

	//allocates a block of bytes of memory
	chunk.memory = (char*)malloc(sizeof(int)); 
	chunk.size = 0; 

	memcpy(&connfd, buffer, sizeof(int));
	cout<<"Get Connection FD:"<<connfd<<endl;

	// copy connfd to chunk
	memcpy(chunk.memory, &connfd, sizeof(int));
	chunk.size += sizeof(int);
	cout<<"Current Chunk Size:"<<chunk.size<<endl;

	// get remaining data length
	size_t rData_length = length-sizeof(int);
	// create receive buffer
	char* rBuf = new char[rData_length];
	// allocate memory
	memset(rBuf, 0, rData_length);
	memcpy(rBuf, buffer+sizeof(int), rData_length);

	cout<<"rBuf size: "<<rData_length<<endl; 

	// initialize CURL
	curl = curl_easy_init(); 

	if(curl)
	{
		
		// get data from receive buffer
		string raw(rBuf, rData_length);
		cout<<"raw size:"<<raw.size()<<endl;
		cout<<"raw Location:"<<&raw<<endl;
		cout<<raw<<endl;

		// convert to stream
		istringstream iss(raw);
		cout<<"iss Location:"<<&iss<<endl;
		string line;
		
		char* pch;
		struct curl_slist* headers=NULL;

		// check first line for method
		getline(iss, line);
		// removing /r at the end	
		if(!line.empty() && line[line.size()-1] == '\r')
		{
			//cout<<"erase succeed"<<endl;
			line.erase(line.size()-1); 
		}
 
		// check method
		if(line.find("HEAD") != -1)
		{
			cout<<"HEAD METHOD"<<endl; 
			
		} 
		else if(line.find("GET") != -1)
		{
			cout<< "GET METHOD" << endl; 
			curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
			cout<< line << endl;
			
			// first token
			pch = strtok((char*)line.c_str(), " "); 
			// host second token
			pch = strtok(NULL, " ");
			std::string temp(pch); 
			
			// copy to common string request URI
			requestURI = temp; 
			cout<< requestURI << endl;
			temp.clear();  
			  
		}
		else if(line.find("POST") != -1)
		{
			cout<< "POST METHOD" << endl;
			postflag = true; 
			curl_easy_setopt(curl, CURLOPT_POST, 1);  
			cout<< line << endl; 

			// first token
			pch = strtok((char*)line.c_str(), " "); 
			// host second token
			pch = strtok(NULL, " ");
			std::string temp2(pch); 
			
			// copy to common string request URI
			requestURI = temp2; 
			cout<< requestURI << endl; 
			temp2.clear();
 
		}
			
		// set the CURL option
		while(getline(iss, line)) 
		{
	
		   //cout<<"Line Location:"<<&line<<endl;

		   // removing /r at the end	
		   if(!line.empty() && line[line.size()-1] == '\r')
		   {
			line.erase(line.size()-1); 
		   } 

		   // checking every line 
		   if(!line.empty())
		   {
		   	
			if(line.find("Host: ") != -1)
			{
				cout<<"Find Host in HTTP request header"<<endl;
				string host(line);
				host.erase(host.begin(), (host.begin()+6));
				cout<<"Host Name:"<<host<<endl;

				// form the completed URI
				host.append(requestURI);
				cout<<"Completed URI:"<<host<<endl;

				// set URL
				curl_easy_setopt(curl, CURLOPT_URL, (char*)host.c_str());  

				//clean URI
				requestURI.clear();
				host.clear();  
			}
			else if(line.find("Cookie: ") != -1)
			{
				cout<<"Find Cookie in HTTP request header"<<endl; 
				string cookie(line);
				cookie.erase(cookie.begin(), (cookie.begin()+8));
				cout<<"Final Cookie:" <<cookie<<endl; 

				// set cookie
				curl_easy_setopt(curl, CURLOPT_COOKIE, (char*)cookie.c_str());

				cookie.clear();
			}
			else if(line.find(": ") != -1)
			{
				cout<<"Adding ["<<line<<"] to HTTP request header"<<endl;
				// append to list
				headers = curl_slist_append(headers, (char*)line.c_str());

				// get content length in the case of post
				if((line.find("Content-Length: ") != -1) && postflag == true)
				{
					cout<<"Get Content-Length for POST message body"<<endl;
					string postLength(line); 
					postLength.erase(postLength.begin(), (postLength.begin()+16)); 
					cout<<"Message Body Size:"<<postLength<<endl; 

					// convert string to size_t
					post_length = (size_t)atoi((char*)postLength.c_str());					
					
					printf("Post Length: %d \n", post_length);

					postLength.clear();	
				}
			}
			else
			{
				cout<<"Rest Information:"<<line<<endl;
			
				continue;
			}
		   }
	
		}//end while
		
		
		// check whether is post function
		if(postflag)
		{
			
			size_t offset = rData_length-post_length;
			cout<<"The offset is:"<<offset<<endl;

			// past post data and its size to curl 
			curl_easy_setopt(curl, CURLOPT_POSTFIELDS, rBuf+offset);
			curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, post_length);

			// clear flag 
			postflag = false;
		}

		// set rest CURL options
		curl_easy_setopt(curl, CURLOPT_HEADER, 1);
		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
		// write to memory
		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
		// declare where you want to save to 
		curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&chunk);
				
		// send by curl
		res = curl_easy_perform(curl);
		// check error
		if(res != CURLE_OK)
		{
			fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
		}

		// clean the curl list and handle		
		curl_slist_free_all(headers);
		curl_easy_cleanup(curl);
		
		
		// ######TEST##########
		/*struct MemoryStruct chunk;
		int connfd;
		
		//allocates a block of bytes of memory
		chunk.memory = (char*)malloc(sizeof(int)); 
		chunk.size = 0; 

		memcpy(&connfd, buffer, sizeof(int));
		cout<<"Get Connection FD:"<<connfd<<endl;

		// copy connfd to chunk
		memcpy(chunk.memory, &connfd, sizeof(int));
		chunk.size += sizeof(int);
		cout<<"Current Chunk Size:"<<chunk.size<<endl;

		FILE *pFile; 
		
		pFile = fopen("http3.out", "r");
		
		if(pFile == NULL)
		{
			cout<<"File can't open"<<endl;
		}
		else
		{

			//obtain file size
			fseek(pFile, 0, SEEK_END);
			long lSize = ftell(pFile);
			cout<< "File Size:"<< lSize<<endl;
			rewind(pFile);

			// allocate memory to contain the whole file
			chunk.memory = (char*)realloc(chunk.memory, (chunk.size+lSize)); 
			
			if(chunk.memory == NULL)
			{
				printf("not enough memory (realloc returned NULL)\n");
			}

			chunk.size = chunk.size + lSize; 

			int result = fread(chunk.memory+sizeof(int), 1, lSize, pFile);
			if(result != lSize)
			{	
				cout<<"Reading size not match"<<endl; 
			}

			fclose(pFile);
		}*/

		cout<<"Get HTTP Response["<<chunk.size<<"]:"<<endl;
		set_dest(dest);// set new destination
		bool success = mydtn_->send(chunk.memory, chunk.size, source_, dest_); 

		if(!success)
		{
			cout<<"HTTP Response is not sending succeed"<<endl;
		}
						
		free(chunk.memory);
		iss.str("");
		if(!iss.str().empty())
		{
			cout<<"Stream iss haven't clear"<<endl;
		}
		raw.clear();
		if(!raw.empty())
		{
			cout<<"String raw haven't clear"<<endl; 
		}
		line.clear();
		if(!line.empty())
		{
			cout<<"String Line havent' clear"<<endl;
		} 
		delete[] rBuf;
		
	}
	else
	{
		cout<< "Create CURL failed"<< endl; 
		exit(1); 
	}

}

/* static callback function to call dtn to send back http response */
/*size_t httpClient::write_data(char *ptr, size_t size, size_t nmemb, void* userdata)
{	
	
	// get total size	
	size_t realsize = size*nmemb;
	// get object
  	struct MemoryStruct* mem = (struct MemoryStruct*)userdata;
 	
	// size of memory block pointerd by the mem->memory is changed to the new bytes
  	mem->memory = (char*)realloc(mem->memory, (mem->size + realsize));
  	
	if(mem->memory == NULL)
	{
		printf("not enough memory (realloc returned NULL)\n");
    		exit(1);
  	}
 
	// copy to memory structure
  	memcpy(&(mem->memory[mem->size]), ptr, realsize);
  	mem->size += realsize;
  	//mem->memory[mem->size] = 0;
 	
	cout<<"Write ["<<realsize<<"] bytes Data to chunk"<<endl;
  	return realsize;

}*/

/* static callback function to call dtn to send back http response */
size_t httpClient::write_data(char* ptr, size_t size, size_t nmemb, MemoryStruct* userdata)
{	
	
	// get total size	
	size_t realsize = size*nmemb;
	// get object
  	//struct MemoryStruct* mem = (struct MemoryStruct*)userdata;
 	
	// size of memory block pointerd by the mem->memory is changed to the new bytes
  	userdata->memory = (char*)realloc(userdata->memory, (userdata->size + realsize + 1));
  	
	if(userdata->memory == NULL)
	{
		printf("not enough memory (realloc returned NULL)\n");
    		exit(1);
  	}
 
	// copy to memory structure
  	memcpy(userdata->memory+userdata->size, ptr, realsize);
  	userdata->size += realsize;
  	userdata->memory[userdata->size] = 0;
 	
	cout<<"Write ["<<realsize<<"] bytes Data to chunk"<<endl;
  	return realsize;

}

/* Print out recevied message */
void httpClient::print_data(char* buffer, u_int length)
{
    
    u_int k;
    char s_buffer[BUFSIZE];
    
    printf("\n");
    cout<< "receiving: "; 

    for (k=0; k < length; k++)
    {
        if (buffer[k] >= ' ' && buffer[k] <= '~')
            s_buffer[k%BUFSIZE] = buffer[k];
        else
            s_buffer[k%BUFSIZE] = '.';

        if (k%BUFSIZE == 0) // new line every 16 bytes
        {
            printf("%07x ", k);
        }
        else if (k%2 == 0)
        {
            printf(" "); // space every 2 bytes
        }
                    
        printf("%02x", buffer[k] & 0xff);
                    
            // print character summary (a la emacs hexl-mode)
        if (k%BUFSIZE == BUFSIZE-1)
        {
            printf(" |  %.*s\n", BUFSIZE, s_buffer);
        }
    }
    
    // handle leftovers
    if ((length % BUFSIZE) != 0) {
        while (k%BUFSIZE != BUFSIZE-1) {
            if (k%2 == 0) {
                printf(" ");
            }
            printf("  ");
            k++;
        }
        printf("   |  %.*s\n",
               (int)length%BUFSIZE, 
               s_buffer);
    }
    //printf("\n");
}

/* Set remote id */
void httpClient::set_dest(string name){
	dest_ = name;
	cout<<"HTTP Proxy Client Eid: ["<<dest_<<"]"<<endl;  
}
