/*Handle Client Request Function
  Variable Definition:
  -- client_socket: socket connected to the client
  Return Value: NULL
*/
void handleClientRequest(int client_socket) {
    FILE 	*channel;						//file stream for client socket
    char 	request_line[STRING_SIZE];		//client request line
    char	*return_value;					//each line header pointer

    //Initialize request_line buffer
    memset(request_line, 0, STRING_SIZE);
    //Create an input stream from the socket
    channel = fdopen(client_socket, "r");
    if (channel == NULL) {
        dieWithSystemMessage("fdopen() failed");
    }

    //Get client Request Line (Jump the blank line)
    do {
        return_value = fgets(request_line, STRING_SIZE, channel);
    } while (syntaxChecking(return_value, BLANK_LINE));
    //Output the client_socket id and Request Line
    printf("Got a call on %d: request = %s", client_socket, request_line);
    //Get client Header Lines & Response the client request
    respondClientRequest(request_line, getHeaderLines(channel), client_socket);

    //Close client socket
    close(client_socket);
    //Close file stream
    fclose(channel);

    return;
}
bool headerLinesIncorrect(RTSP_HEADER *header, char *field_value){
	RTSP_HEADER	*node;

	for (node = header->next; node != NULL; node = node->next) {
		//Use regular expression to check header lines' syntax
		if (!syntaxChecking(node->header_line, HEADER_LINE)) {
			sprintf(field_value, "Header line: [%s] is syntacically incorrect!", node->header_line);
			return true;
		}
	}

	return false;
}
/*Respond Client Request Function
  Variable Definition:
  -- request: client request line
  -- header: client header lines
  -- client_socket: socket connected to the client
  Return Value: NULL
*/
void respondClientRequest(char *request, RTSP_HEADER *header, int client_socket) {
    char 		method[STRING_SIZE];			//method field: SETUP, PLAY, PAUSE, or TEARDOWN
    char 		url[STRING_SIZE];				//url field: for example, rtsp://localhost:5678/movie.Mjpeg
    char 		version[STRING_SIZE];			//rtsp version field: RTSP/1.0
    char		field_value[HALFBUF_SIZE];		//field value string
    u_int32		cseq_number = 0;				//cseq number

    //Initialize method, url, version, and field_value buffer
    memset(method, 0, STRING_SIZE);
    memset(url, 0, STRING_SIZE);
    memset(version, 0, STRING_SIZE);
    memset(field_value, 0, HALFBUF_SIZE);

    //Test the client RTSP Request Line
    if (!syntaxChecking(request, REQUEST_LINE)) {
        //400 Bad Request: the request could not be understood by the server
        sendBadRequest("Request Line is syntactically incorrect!", client_socket);
        return;
    }

    //Get the method, url, and rtsp version
    sscanf(request, "%s%s%s", method, url, version);
    //Decode the URL(if it has %HEX code)
    decodeURL(url);

    //Test the method
    if (methodNotAllow(method)) {
        //405 Method Not Allowed: the method field is neither "SETUP", "PLAY", "PAUSE" nor "TEARDOWN"
        sendMethodNotAllowed(method, client_socket);
        return;
    }
    //Test the Requested URL
    else if (!syntaxChecking(url, URL_FORMAT)) {
        //400 Bad Request: the request could not be understood by the server
        sendBadRequest("Requested URL is syntactically incorrect!", client_socket);
        return;
    }
    //Test the RTSP version
    else if (!syntaxChecking(version, RTSP_VERSION)) {
        //400 Bad Request: the request could not be understood by the server
        sendBadRequest("RTSP Version is syntactically incorrect!", client_socket);
        return;
    }
    //Test the RTSP version 1.0
    else if (!syntaxChecking(version, RTSP_VERSION_1)) {
        //505 RTSP Version Not Supported: the requested RTSP protocol version is not supported by server
        sendRTSPVersionNotSupported(version, client_socket);
        return;
    }

#ifdef	DEBUG
    RTSP_HEADER		*debug_header_node;

    DEBUG_START;
    fputs("RTSP request header lines:\n", stdout);
    //Output the RTSP request header lines
    for (debug_header_node = header->next; debug_header_node != NULL; debug_header_node = debug_header_node->next) {
        fputs(debug_header_node->field_name, stdout);
        fputs(": ", stdout);
        fputs(debug_header_node->field_value, stdout);
        fputc('\n', stdout);
    }
    DEBUG_END;
#endif

    //Test the Header Line
    if (headerLinesIncorrect(header, field_value)) {
        //400 Bad Request: the request could not be understood by the server
        sendBadRequest(field_value, client_socket);
        return;
    }
    //Test the "CSeq" field
    else if (fieldNotExist(header, "cseq", field_value)) {
        //400 Bad Request: the request could not be understood by the server
        sendBadRequest("CSeq field does not exist!", client_socket);
        return;
    }
    //Test the "Session" field
    else if ((!methodIsSetup(method)) && fieldNotExist(header, "session", field_value)) {
        //400 Bad Request: the request could not be understood by the server
        sendBadRequest("Session field does not exist!", client_socket);
        return;
    }
    //Test the "Transport" field
    else if (methodIsSetup(method) && fieldNotExist(header, "transport", field_value)) {
        //400 Bad Request: the request could not be understood by the server
        sendBadRequest("Transport field does not exist!", client_socket);
        return;
    }
    //Test the "Range" field
    else if ((!(fieldNotExist(header, "range", field_value)))
             && (!syntaxChecking(field_value, RANGE_FORMAT))) {
        //400 Bad Request: the request could not be understood by the server
        sendBadRequest("Range field value(npt=number[-number]) is syntactically incorrect!", client_socket);
        return;
    }
    //Test the "If-Modified-Since" field
    else if ((!(fieldNotExist(header, "if-modified-since", field_value)))
             && (!syntaxChecking(field_value, TIME_FORMAT))) {
        //400 Bad Request: the request could not be understood by the server
        sendBadRequest("If-Modified-Since field value(time format) is syntactically incorrect!", client_socket);
        return;
    }

    //Now we are sure that the request message is SYNTACTICALLY CORRECT
    //Get the Requested File or Directory's name
    pathBelowCurrentDirectory(url);
    printf("Requested File or Directory is %s\n", url);

    //Test the requested file on the server
    if (urlNotExist(url)) {
        //404 Not Found: the requested document does not exist on this server
        sendNotFound(url, client_socket);
        return;
    }
    //Test the requested url is a directory
    else if (urlIsADirectory(url)) {
        //404 Not Found: the requested document does not exist on this server
        sendNotFound(url, client_socket);
        return;
    }
    //Test the method is valid in special state
    else if (methodIsNotValidInState(method)) {
        //455 Method is not valid in this state: the method is not valid in this state
        sendMethodNotValidInThisState(method, client_socket);
        return;
    }

    //Get the RTSP Request Message information
    cseq_number = getRTSPInfo(header);

    //Test the "Session" field's value
    if (cseq_number == 0) {
        //454 Session Not Found: the session id is not equal to the server's
        sendSessionNotFound(client_socket);
        return;
    }
    //Test the protocol type
    else if (strcmp(protocol_type, PROTOCOL_TYPE) != 0) {
        //461 Unsupported Transport: the transport protocol is not supported by the server
        sendUnsupportedTransport(protocol_type, client_socket);
        return;
    }
    //Test the "If-Modified-Since" field
    else if (fieldNotExist(header, "if-modified-since", field_value)) {
        //200 OK: the request is good
        sendOK(url, method, cseq_number, client_socket);
        return;
    }
    //Test the "If-Modified-Since" field value
    else if (compareModifiedTime(url, field_value)) {
        //304 Not Modified: the request does not Modified since If-Modified-Since field
        sendNotModified(url, cseq_number, client_socket);
        return;
    }
    else {
        //200 OK: the request is good
        sendOK(url, method, cseq_number, client_socket);
        return;
    }
}
/*Respond Client Request Function
  Variable Definition:
  -- request: client request line
  -- header: client header lines
  -- client_socket: socket connected to the client
  Return Value: NULL
*/
void respondClientRequest(char *request, struct http_header *header, int client_socket){
	char 	method[STRING_SIZE];		//method field: GET or HEAD
	char 	url[STRING_SIZE];			//url field: /TestServer
	char 	version[STRING_SIZE];		//http version field: HTTP/1.1
	char	field_value[HALFBUF_SIZE];	//field value string
	bool	modified_signal;			//whether if-modified-since field exist
	
	//Initialize method, url and version buffer
	memset(method, 0, STRING_SIZE);
	memset(url, 0, STRING_SIZE);
	memset(version, 0, STRING_SIZE);
	memset(field_value, 0, HALFBUF_SIZE);
	
	//Test the client HTTP Request Line
	if (!syntaxChecking(request, 'R')){
		//400 Bad Request: the request could not be understood by the server
		sendBadRequest("Request Line is syntactically incorrect!", client_socket);
		return;
	}
	
	//Get the method, url, and http version
	// NC: i don't think this works: need   sscanf(request, "%s %s %s", method, url, version);
	sscanf(request, "%s%s%s", method, url, version);
	//Decode the URL(if it has %HEX code)
	decodeURL(url);
	//Test the method
	if ((strcmp(method, "GET") != 0) && (strcmp(method, "HEAD") != 0)){
		//405 Method Not Allowed: the method field is neither "GET" nor "HEAD"
		sendMethodNotAllowed(method, client_socket);
		return;
	}
	//Test the Requested URL
	else if (!syntaxChecking(url, 'U')){
		// NC: but, "the only legal Request-URI is the abs_path “/TestServer”. Generate a 404 Response to all other Request-URIs."
		// #17: -1
		//400 Bad Request: the request could not be understood by the server
		sendBadRequest("Requested URL is syntactically incorrect!", client_socket);
		return;
	}
	//Test the HTTP version
	else if (!syntaxChecking(version, 'V')){
		//400 Bad Request: the request could not be understood by the server
		sendBadRequest("HTTP Version is syntactically incorrect!", client_socket);
		return;
	}
	else if (!syntaxChecking(version, 'O')){
		//505 HTTP Version Not Supported: the requested HTTP protocol version is not supported by server
		sendHTTPVersionNotSupported(version, client_socket);
		return;
	}
	
	//Test the Header Line
	if (headerLinesIncorrect(header, field_value)){
		//400 Bad Request: the request could not be understood by the server
		sendBadRequest(field_value, client_socket);
		return;
	}
	//Test the "Host" field
	else if (fieldNotExist(header, "host", field_value)){
		//400 Bad Request: the request could not be understood by the server
		sendBadRequest("Host field does not exist!", client_socket);
		return;
	}
	else if (!syntaxChecking(field_value, 'I')){
		//400 Bad Request: the request could not be understood by the server
		sendBadRequest("Host field value(domain name, IP address, or port format) is syntactically incorrect!", client_socket);
		return;
	}
	//Test the "If-Modified-Since" field
	else if ((!(modified_signal = fieldNotExist(header, "if-modified-since", field_value))) && (!syntaxChecking(field_value, 'T'))){
		//400 Bad Request: the request could not be understood by the server
		sendBadRequest("If-Modified-Since field value(time format) is syntactically incorrect!", client_socket);
		return;
	}
	
	//Now we are sure that the request message is SYNTACTICALLY CORRECT
	//Get the Requested File or Directory's name
	pathBelowCurrentDirectory(url);
	printf("Requested File or Directory is %s\n", url);
// NC: but OUR server doesn't do this
	
	//Test the requested file on the server
	if (urlNotExist(url)){
		//404 Not Found: the requested document does not exist on this server
		sendNotFound(url, client_socket);
	}
	else if (urlIsADirectory(url)){
		//List the File Name of Directory
		sendFileNameInDirectory(url, method, client_socket);
	}
	else if (modified_signal){
		//200 OK: the request is good
		sendOK(url, method, client_socket);
	}
	else if (compareModifiedTime(url, field_value)){
		//304 Not Modified: the request does not Modified since If-Modified-Since field
		sendNotModified(url, client_socket);
	}
	else{
		//200 OK: the request is good
		sendOK(url, method, client_socket);
	}

	return;
}