/*
 RDP event queuing 
 
 Copyright 2013 Thincast Technologies GmbH, Author: Dorian Johnson
 
 This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. 
 If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "ios_freerdp_events.h"

#pragma mark -
#pragma mark Sending compacted input events (from main thread)

// While this function may be called from any thread that has an autorelease pool allocated, it is not threadsafe: caller is responsible for synchronization
BOOL ios_events_send(mfInfo* mfi, NSDictionary * event_description)
{		
	NSData * encoded_description = [NSKeyedArchiver archivedDataWithRootObject:event_description];
	
	if ([encoded_description length] > 32000 || (mfi->event_pipe_producer == -1) )
		return FALSE;
	
	uint32_t archived_data_len = (uint32_t)[encoded_description length];
	
	//NSLog(@"writing %d bytes to input event pipe", archived_data_len);
	
	if (write(mfi->event_pipe_producer, &archived_data_len, 4) == -1)
	{
		NSLog(@"%s: Failed to write length descriptor to pipe.", __func__);
		return FALSE;
	}
	
	if (write(mfi->event_pipe_producer, [encoded_description bytes], archived_data_len) == -1)
	{
		NSLog(@"%s: Failed to write %d bytes into the event queue (event type: %@).", __func__, (int)[encoded_description length], [event_description objectForKey:@"type"]);
		return FALSE;
	}
	
	return TRUE;	
}


#pragma mark -
#pragma mark Processing compacted input events (from connection thread runloop)

static BOOL ios_events_handle_event(mfInfo* mfi, NSDictionary * event_description)
{
	NSString * event_type = [event_description objectForKey:@"type"];
	BOOL should_continue = TRUE;
	freerdp* instance = mfi->instance;
	
	if ([event_type isEqualToString:@"mouse"])
	{        
		instance->input->MouseEvent(instance->input, 
                                    [[event_description objectForKey:@"flags"] unsignedShortValue],
                                    [[event_description objectForKey:@"coord_x"] unsignedShortValue],
                                    [[event_description objectForKey:@"coord_y"] unsignedShortValue]);
	}
	else if ([event_type isEqualToString:@"keyboard"])
	{
		if ([[event_description objectForKey:@"subtype"] isEqualToString:@"scancode"])
			instance->input->KeyboardEvent(instance->input, 
                                           [[event_description objectForKey:@"flags"] unsignedShortValue],
                                           [[event_description objectForKey:@"scancode"] unsignedShortValue]);
		else if ([[event_description objectForKey:@"subtype"] isEqualToString:@"unicode"])
			instance->input->UnicodeKeyboardEvent(instance->input,
                                                  [[event_description objectForKey:@"flags"] unsignedShortValue],
                                                  [[event_description objectForKey:@"unicode_char"] unsignedShortValue]);
		else
			NSLog(@"%s: doesn't know how to send keyboard input with subtype %@", __func__, [event_description objectForKey:@"subtype"]);
	}
	else if ([event_type isEqualToString:@"disconnect"])
		should_continue = FALSE;
	else
		NSLog(@"%s: unrecognized event type: %@", __func__, event_type);
	
	return should_continue;
}

BOOL ios_events_check_fds(mfInfo* mfi, fd_set* rfds)
{	
	if ( (mfi->event_pipe_consumer == -1) || !FD_ISSET(mfi->event_pipe_consumer, rfds))
		return TRUE;
	
	uint32_t archived_data_length = 0;
	ssize_t bytes_read;
	
	// First, read the length of the blob
	bytes_read = read(mfi->event_pipe_consumer, &archived_data_length, 4);
	
	if (bytes_read == -1 || archived_data_length < 1 || archived_data_length > 32000)
	{
		NSLog(@"%s: just read length descriptor. bytes_read=%ld, archived_data_length=%u", __func__, bytes_read, archived_data_length);
		return FALSE;
	}
	
	//NSLog(@"reading %d bytes from input event pipe", archived_data_length);
	
	NSMutableData * archived_object_data = [[NSMutableData alloc] initWithLength:archived_data_length];
	bytes_read = read(mfi->event_pipe_consumer, [archived_object_data mutableBytes], archived_data_length);
	
	if (bytes_read != archived_data_length)
	{
		NSLog(@"%s: attempted to read data; read %ld bytes but wanted %d bytes.", __func__, bytes_read, archived_data_length);
		[archived_object_data release];
		return FALSE;
	}
	
	id unarchived_object_data = [NSKeyedUnarchiver unarchiveObjectWithData:archived_object_data];
	[archived_object_data release];
	
	return ios_events_handle_event(mfi, unarchived_object_data);
}

BOOL ios_events_get_fds(mfInfo* mfi, void ** read_fds, int * read_count, void ** write_fds, int * write_count)
{
	read_fds[*read_count] = (void *)(long)(mfi->event_pipe_consumer);
	(*read_count)++;
	return TRUE;
}

// Sets up the event pipe
BOOL ios_events_create_pipe(mfInfo* mfi)
{
	int pipe_fds[2];
	
	if (pipe(pipe_fds) == -1)
	{
		NSLog(@"%s: pipe failed.", __func__);
		return FALSE;
	}
	
	mfi->event_pipe_consumer = pipe_fds[0];
	mfi->event_pipe_producer = pipe_fds[1];
	return TRUE;
}

void ios_events_free_pipe(mfInfo* mfi)
{
	int consumer_fd = mfi->event_pipe_consumer, producer_fd = mfi->event_pipe_producer;
	
	mfi->event_pipe_consumer = mfi->event_pipe_producer = -1;
	close(producer_fd);
	close(consumer_fd);	
}
