#include "Sockets.h"



//////////////////////// Socket wrapper class ///////////////

Socket::Socket(){
	setReadTimeout();
	fpout=0;
	fpin=0;
	host=0;  // none (can be used for standard out or in?)
	sockfd=0;
	connfd=0;
	port=0;
	clilen=0;
}


Socket::Socket(const char* ahost, int aport){

	int hlen = strlen(ahost);
	host = new char[hlen+1];
	strcpy(host, ahost);

	connfd=0;
	port = aport;

	memset((char*) &serv_addr, 0, sizeof(serv_addr));
	
	if ((host_ptr = gethostbyname(host)) == 0){
		fprintf(stderr, "error with host name\n");
		exit(1);
	}

	if (host_ptr->h_addrtype!= AF_INET && host_ptr->h_addrtype!= AF_INET6){
		fprintf(stderr, "unknown address type sorry ! \n");
		exit(1);
	}
	
	serv_addr.sin_family = host_ptr->h_addrtype; // AF_NET OR AF_NET6
	serv_addr.sin_port = htons(port);
	
	serv_addr.sin_addr.s_addr = ((struct in_addr*) host_ptr->h_addr_list[0])->s_addr;

	if ((sockfd = socket(host_ptr->h_addrtype, SOCK_STREAM, 0))<0){
		fprintf(stderr, "error opening socket\n");
		exit(1);
	}
	
	if (connect(sockfd , (struct sockaddr*) & serv_addr, sizeof(serv_addr)) <0){
		fprintf(stderr, "error- can't connect to socket\n");
		exit(1);
	}

	fpout = fdopen(sockfd , "w");	
	fpin  = fdopen(sockfd , "r");
	
	setReadTimeout();

	signal(SIGPIPE, SIG_IGN);  // ignore when SIGPIPE comes along and produces 
								// nasty broken pipe
}
///////////////


// Socket constructor mainly used for ServerSocket
Socket::Socket(int aport){
	if ((connfd = socket(AF_INET, SOCK_STREAM, 0))<0){
		fprintf(stderr, "error opening socket\n");
		exit(1);
	}

	sockfd=0;
	int optval=1; //set to turn socket option REUSE_ADDR on=1
	port = aport;
	memset((char*) &serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET; 
	serv_addr.sin_addr.s_addr=htonl(INADDR_ANY); 
	serv_addr.sin_port = htons(port); 
	
	// set reuse address option so if shutdown can restart
	setsockopt(connfd, SOL_SOCKET, SO_REUSEADDR,(void*)&optval, sizeof(optval ));
	
	if (bind(connfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr))<0){
		fprintf(stderr, "error opening socket - can't bind\n");
		exit(1);
	}
	listen(connfd, 10); // this call will not fail because all reasons for failure have been controled inside the class

	signal(SIGPIPE, SIG_IGN);  // ignore when SIGPIPE comes along and produces 
								// nasty broken pipe
}
/////////////////////



Socket::Socket(const Socket & other){  // copy constructor

	setReadTimeout();
	fpout= other.fpout;
	fpin = other.fpin;
	sockfd = other.sockfd;
	connfd = other.connfd;
	port   = other.port;
	clilen = other.clilen;
	
	if (other.host!=0){
		int tlen = strlen(other.host);
		host = new char[tlen+1];
		strcpy(host, other.host);
	}
	else host=0;  
}




int Socket::setReadTimeout(){

	to.tv_sec=100;
	to.tv_usec=0;
	int success=setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (void*)&to, sizeof(to));
	if (success==-1){
		fprintf(stderr,"RCVTIMEO UNSUPPORTED\n");
	}
return 0;
}



Socket& Socket::operator=(const Socket & other){ // should check open fd's and close if need be

	if (this!=&other){

		setReadTimeout();
		if (sockfd!=0 && other.sockfd!=sockfd){
			close(sockfd);  // if not closed, close now before now taking a new one
			fclose(fpout);
			fclose(fpin);
		}
		sockfd = other.sockfd;
		connfd = other.connfd; // leave this alone , might still need to be open for server
		fpout= other.fpout;
		fpin = other.fpin;
		port   = other.port;
		clilen = other.clilen;
		
		if (other.host!=0){
			int tlen = strlen(other.host);
			if (host!=0) delete host;
			host = new char[tlen+1];
			strcpy(host, other.host);
		}
		else host=0;  
	}
return *this;
}



int Socket::Accept(){

	sockfd = accept(connfd, (struct sockaddr*) &cli_addr, (socklen_t*)&clilen);
	fpout = fdopen(sockfd , "w");	
	fpin  = fdopen(sockfd , "r");

	setReadTimeout();
	
return sockfd;
}
////////////////////////




int Socket::Close(){

	int er = close(sockfd);
	fclose(fpin);
	fclose(fpout);
	
	if (er!=-1){
		sockfd=0;
		fpin=0;
		fpout=0;
	}

	return er;
}
/////////////////



int Socket::CloseConnector(){

	return close(connfd);
}
////////////////




int Socket::getListenFD(){
	return connfd;
}


int Socket::getConnectedFD(){
	return sockfd;
}


///////////////////////////////temporary functions copied from neil  ///////


// send until end of string, makes sure there's an endline at the end
void Socket::println(const char* msg) throw (socketerror)
{
	if(fpout==NULL)
     	throw socketerror(socketerror::NOTCONNECTED);
	int slen = strlen(msg);
	int errCode=0;
	if (msg[slen-1]!='\n'){
		errCode = fprintf(fpout,"%s\n",msg);
	}	
	else errCode = fputs(msg, fpout);
	if(errCode == EOF)
		throw socketerror(socketerror::BADWRITE);
	errCode = fflush(fpout);
	if(errCode == EOF)
		throw socketerror(socketerror::BADWRITE);
}



// read until newline, or EOF or maxlen-1
void Socket::readln(char buffer[], int maxlen) throw (socketerror)
{
	if(fpin==NULL)
                throw socketerror(socketerror::NOTCONNECTED);	
	char* errPtr  = fgets(buffer, maxlen, fpin);
	if(errPtr == NULL)
		throw socketerror(socketerror::BADREAD);

	int errCode = fflush(fpin);
	if(errCode == EOF)
		throw socketerror(socketerror::BADREAD);

	int len = strlen(buffer);
	if (buffer[len-1]=='\n') buffer[len-1]=0; // strip the endline if it exists
}





int Socket::fRead(void* buff, int size, FILE* in) throw (socketerror){

	int num_read=0;
	int num_left= size;	
	unsigned char* ptr = (unsigned char*)buff;

	while (num_left > 0){
		num_read = fread((void*)ptr, 1 , num_left, in);	
		num_left -= num_read;
		ptr += num_read;
		if (num_read <num_left){// deal with error
			if (feof(fpin)){		
				clearerr(fpin);
				throw socketerror(socketerror::EOF_ERROR);
			}
			else if (ferror(fpin)){
				clearerr(fpin);
				throw socketerror(socketerror::UNDEF_ERROR);
			}			
		}
	}

	return (size - num_left);
}


int Socket::fRead (void* buff, int size){

	return fRead(buff, size, fpin);
}




int Socket::fWrite(const void* buff, int size)throw (socketerror){

	int num_wrote=0;
	int num_left = size;
	unsigned char* ptr = (unsigned char*)buff;
	int errCode =0;

	while (num_left > 0){

		if (ferror(fpout))	throw socketerror(socketerror::EOF_ERROR);

		num_wrote = fwrite((const void*)ptr, 1 , num_left, fpout);	
		
		errCode = fflush(fpout);
		if(errCode == EOF)
		throw socketerror(socketerror::BADREAD);

		num_left -= num_wrote;
		ptr += num_wrote;
		if (num_wrote <num_left){// deal with error
			if (feof(fpin)){
				clearerr(fpin);
				throw socketerror(socketerror::EOF_ERROR);
			}
			else if (ferror(fpin)){
				clearerr(fpin);
				throw socketerror(socketerror::UNDEF_ERROR);
			}				
		}
	}

	return size;
}



int Socket::WriteFrom(FILE* in, int bytes){

	int num_left = bytes;
	int num_wrote = 0;
	int num_read = 0;
	int needed=MAXLINE;
	unsigned char* buff = new unsigned char[MAXLINE+1];
	memset(buff, 0, MAXLINE+1);
	
	while (num_left>0){
		
		if (num_left<needed) needed = num_left;
	
		num_read = fRead(buff, needed, in); // in from file
		
		if (num_read==-1){
			printf("error in WriteFrom\n");
			break;
		}

		if (num_read>0) num_wrote = fWrite (buff, num_read);
		num_left -=num_wrote;
	}

	delete[] buff;
	return (bytes - num_left);
}



int Socket::ReadTo(FILE* out, int bytes){

	int num_left = bytes;
	int num_read = 0;
	int needed=MAXLINE;
	unsigned char* buff = new unsigned char[MAXLINE+1];
	memset(buff, 0, MAXLINE+1);
	
	while (num_left>0){

		if (num_left<needed) needed = num_left;

		num_read = fRead(buff, needed, fpin); // read from socket fpin stream

		if (num_read==-1){
			printf("-1 error in ReadTo\n");
			break;
		}
		num_left -=num_read;
		if (num_read>0) fwrite(buff, 1, num_read, out); // out to file
	}
	
	delete[] buff;
	return (bytes - num_left);
}





//////////////////// ServerSocket wrapper class /////////////////////


ServerSocket::ServerSocket(int aport){

	port = aport;
	mysocket = new Socket(port);
}
////////////////////////


Socket* ServerSocket::accept()
			throw (socketerror){

	int sockfd = mysocket->Accept();
	if (sockfd<0) throw socketerror(socketerror::BADCLIENT);	
	else return new Socket(*mysocket);
}
/////////////////////




int ServerSocket::CloseListener(){  // mainly needed for use with fork() and processes

	return mysocket->CloseConnector();	// close the binded and listening socket (connfd for servers)

}
/////////////////




int ServerSocket::getListenFD(){
	return mysocket->getListenFD();
}


//int ServerSocket::Close(){	// what the hell is this needed for ? why public anyway
//	return close(sockfd);
//}
/////////////////////


///////////////////// end of ServerSocket wrapper class  //////////////////

// various helpful information
/*


////////////////////SOCKET AND SERVER SOCKET CLASS ///////////////////////////

server info --
		when iterating server used do NOT free server memory in-between each Accept connection
		only Close the connection. you still need the server side socket.. closing only 
		closes the remote side of the connection.



from man-page signal

NAME
       signal - ANSI C signal handling

SYNOPSIS
       #include <signal.h>

       void (*signal(int signum, void (*sighandler)(int)))(int);

DESCRIPTION
       The signal() system call installs a new signal handler for the signal with number signum.  The signal
       handler is set to sighandler which may be a user specified function, or either SIG_IGN or SIG_DFL.

NOTES
       If  you're  confused by the prototype at the top of this manpage, it may help to see it separated out
       thus:

       typedef void (*sighandler_t)(int);
       sighandler_t signal(int signum, sighandler_t handler);

       (Various versions of libc predefine this type; libc4 and libc5 define  SignalHandler,  glibc  defines
       sig_t and, when _GNU_SOURCE is defined, also sighandler_t.)


*/


