/* Create an RTSP Request */
static int initializeRtspRequest(PRTSP_MESSAGE msg, char* command, char* target)
{
	char sequenceNumberStr[16];
    char clientVersionStr[16];

	// FIXME: Hacked CSeq attribute due to RTSP parser bug
	createRtspRequest(msg, NULL, 0, command, target, "RTSP/1.0",
		0, NULL, NULL, 0);
	
	sprintf(sequenceNumberStr, "%d", currentSeqNumber++);
    sprintf(clientVersionStr, "%d", rtspClientVersion);
	if (!addOption(msg, "CSeq", sequenceNumberStr) ||
		!addOption(msg, "X-GS-ClientVersion", clientVersionStr)) {
		freeMessage(msg);
		return 0;
	}

	return 1;
}
// Given an RTSP message string rtspMessage, parse it into an RTSP_MESSAGE struct msg
int parseRtspMessage(PRTSP_MESSAGE msg, char* rtspMessage, int length) {
    char* token;
    char* protocol;
    char* endCheck;
    char* target;
    char* statusStr;
    char* command;
    char* sequence;
    char flag;
    char messageEnded = 0;

    char* payload = NULL;
    char* opt = NULL;
    int statusCode = 0;
    int sequenceNum;
    int exitCode;
    POPTION_ITEM options = NULL;
    POPTION_ITEM newOpt;

    // Delimeter sets for strtok()
    char* delim = " \r\n";
    char* end = "\r\n";
    char* optDelim = " :\r\n";
    char typeFlag = TOKEN_OPTION;

    // Put the raw message into a string we can use
    char* messageBuffer = malloc(length + 1);
    if (messageBuffer == NULL) {
        exitCode = RTSP_ERROR_NO_MEMORY;
        goto ExitFailure;
    }
    memcpy(messageBuffer, rtspMessage, length);

    // The payload logic depends on a null-terminator at the end
    messageBuffer[length] = 0;

    // Get the first token of the message
    token = strtok(messageBuffer, delim);
    if (token == NULL) {
        exitCode = RTSP_ERROR_MALFORMED;
        goto ExitFailure;
    }

    // The message is a response
    if (startsWith(token, "RTSP")) {
        flag = TYPE_RESPONSE;
        // The current token is the protocol
        protocol = token;

        // Get the status code
        token = strtok(NULL, delim);
        statusCode = atoi(token);
        if (token == NULL) {
            exitCode = RTSP_ERROR_MALFORMED;
            goto ExitFailure;
        }

        // Get the status string
        statusStr = strtok(NULL, end);
        if (statusStr == NULL) {
            exitCode = RTSP_ERROR_MALFORMED;
            goto ExitFailure;
        }

        // Request fields - we don't care about them here
        target = NULL;
        command = NULL;
    }

    // The message is a request
    else {
        flag = TYPE_REQUEST;
        command = token;
        target = strtok(NULL, delim);
        if (target == NULL) {
            exitCode = RTSP_ERROR_MALFORMED;
            goto ExitFailure;
        }
        protocol = strtok(NULL, delim);
        if (protocol == NULL) {
            exitCode = RTSP_ERROR_MALFORMED;
            goto ExitFailure;
        }
        // Response field - we don't care about it here
        statusStr = NULL;
    }
    if (strcmp(protocol, "RTSP/1.0")) {
        exitCode = RTSP_ERROR_MALFORMED;
        goto ExitFailure;
    }
    // Parse remaining options
    while (token != NULL)
    {
        token = strtok(NULL, typeFlag == TOKEN_OPTION ? optDelim : end);
        if (token != NULL) {
            if (typeFlag == TOKEN_OPTION) {
                opt = token;
            }
            // The token is content
            else {
                // Create a new node containing the option and content
                newOpt = (POPTION_ITEM)malloc(sizeof(OPTION_ITEM));
                if (newOpt == NULL) {
                    freeOptionList(options);
                    exitCode = RTSP_ERROR_NO_MEMORY;
                    goto ExitFailure;
                }
                newOpt->flags = 0;
                newOpt->option = opt;
                newOpt->content = token;
                newOpt->next = NULL;
                insertOption(&options, newOpt);

                // Check if we're at the end of the message portion marked by \r\n\r\n
                // endCheck points to the remainder of messageBuffer after the token
                endCheck = &token[0] + strlen(token) + 1;

                // See if we've hit the end of the message. The first \r is missing because it's been tokenized
                if (startsWith(endCheck, "\n\r\n")) {
                    // We've encountered the end of the message - mark it thus
                    messageEnded = 1;

                    // The payload is the remainder of messageBuffer. If none, then payload = null
                    if (endCheck[3] != '\0')
                        payload = &endCheck[3];

                    break;
                }
            }
        }
        typeFlag ^= 1; // flip the flag
    }
    // If we never encountered the double CRLF, then the message is malformed!
    if (!messageEnded) {
        exitCode = RTSP_ERROR_MALFORMED;
        goto ExitFailure;
    }

    // Get sequence number as an integer
    sequence = getOptionContent(options, "CSeq");
    if (sequence != NULL) {
        sequenceNum = atoi(sequence);
    }
    else {
        sequenceNum = SEQ_INVALID;
    }
    // Package the new parsed message into the struct
    if (flag == TYPE_REQUEST) {
        createRtspRequest(msg, messageBuffer, FLAG_ALLOCATED_MESSAGE_BUFFER | FLAG_ALLOCATED_OPTION_ITEMS, command, target,
            protocol, sequenceNum, options, payload, payload ? length - (int)(messageBuffer - payload) : 0);
    }
    else {
        createRtspResponse(msg, messageBuffer, FLAG_ALLOCATED_MESSAGE_BUFFER | FLAG_ALLOCATED_OPTION_ITEMS, protocol, statusCode,
            statusStr, sequenceNum, options, payload, payload ? length - (int)(messageBuffer - payload) : 0);
    }
    return RTSP_ERROR_SUCCESS;

ExitFailure:
    if (options) {
        free(options);
    }
    if (messageBuffer) {
        free(messageBuffer);
    }
    return exitCode;
}