//
//  AppDelegate.m
//  MacClient2
//
//  Created by Benoît et Kathy on 2013-05-08.
//
//

#import "AppDelegate.h"
#import "MacFreeRDP/mfreerdp.h"
#import "MacFreeRDP/mf_client.h"
#import "MacFreeRDP/MRDPView.h"
#import <freerdp/client/cmdline.h>

static AppDelegate* _singleDelegate = nil;
void AppDelegate_ConnectionResultEventHandler(void* context,
        ConnectionResultEventArgs* e);
void AppDelegate_ErrorInfoEventHandler(void* ctx, ErrorInfoEventArgs* e);
void AppDelegate_EmbedWindowEventHandler(void* context,
        EmbedWindowEventArgs* e);
void AppDelegate_ResizeWindowEventHandler(void* context,
        ResizeWindowEventArgs* e);
void mac_set_view_size(rdpContext* context, MRDPView* view);

@implementation AppDelegate

- (void)dealloc
{
	[super dealloc];
}

@synthesize window = window;


@synthesize context = context;

- (void) applicationDidFinishLaunching:(NSNotification*)aNotification
{
	int status;
	mfContext* mfc;
	_singleDelegate = self;
	[self CreateContext];
	status = [self ParseCommandLineArguments];
	mfc = (mfContext*) context;
	mfc->view = (void*) mrdpView;

	if (status < 0)
	{
		NSString* winTitle;
		winTitle = [[NSString alloc] initWithCString:"ERROR"];
		[window setTitle:winTitle];
	}
	else
	{
		NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
		NSRect screenFrame = [screen frame];

		if (context->instance->settings->Fullscreen)
		{
			context->instance->settings->DesktopWidth  = screenFrame.size.width;
			context->instance->settings->DesktopHeight = screenFrame.size.height;
		}

		PubSub_SubscribeConnectionResult(context->pubSub,
		                                 AppDelegate_ConnectionResultEventHandler);
		PubSub_SubscribeErrorInfo(context->pubSub, AppDelegate_ErrorInfoEventHandler);
		PubSub_SubscribeEmbedWindow(context->pubSub,
		                            AppDelegate_EmbedWindowEventHandler);
		PubSub_SubscribeResizeWindow(context->pubSub,
		                             AppDelegate_ResizeWindowEventHandler);
		freerdp_client_start(context);
		NSString* winTitle;

		if (mfc->context.settings->WindowTitle && mfc->context.settings->WindowTitle[0])
		{
			winTitle = [[NSString alloc] initWithCString:
			            mfc->context.settings->WindowTitle];
		}
		else
		{
			winTitle = [[NSString alloc] initWithFormat:@"%@:%u",
			            [NSString stringWithCString:mfc->context.settings->ServerHostname encoding:
			             NSUTF8StringEncoding],
			            mfc->context.settings->ServerPort];
		}

		[window setTitle:winTitle];
	}
}

- (void) applicationWillTerminate:(NSNotification*)notification
{
	NSLog(@"Stopping...\n");
	freerdp_client_stop(context);
	[mrdpView releaseResources];
	_singleDelegate = nil;
	NSLog(@"Stopped.\n");
}

- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender
{
	return YES;
}

- (int) ParseCommandLineArguments
{
	int i;
	int length;
	int status;
	char* cptr;
	NSArray* args = [[NSProcessInfo processInfo] arguments];
	context->argc = (int) [args count];
	context->argv = malloc(sizeof(char*) * context->argc);
	i = 0;

	for (NSString * str in args)
	{
		/* filter out some arguments added by XCode */
		if ([str isEqualToString:@"YES"])
			continue;

		if ([str isEqualToString:@"-NSDocumentRevisionsDebugMode"])
			continue;

		length = (int)([str length] + 1);
		cptr = (char*) malloc(length);
		strcpy(cptr, [str UTF8String]);
		context->argv[i++] = cptr;
	}

	context->argc = i;
	status = freerdp_client_settings_parse_command_line(context->settings,
	         context->argc, context->argv, FALSE);
	status = freerdp_client_settings_command_line_status_print(context->settings,
	         status, context->argc, context->argv);
	return status;
}

- (void) CreateContext
{
	RDP_CLIENT_ENTRY_POINTS clientEntryPoints;
	ZeroMemory(&clientEntryPoints, sizeof(RDP_CLIENT_ENTRY_POINTS));
	clientEntryPoints.Size = sizeof(RDP_CLIENT_ENTRY_POINTS);
	clientEntryPoints.Version = RDP_CLIENT_INTERFACE_VERSION;
	RdpClientEntry(&clientEntryPoints);
	context = freerdp_client_context_new(&clientEntryPoints);
}

- (void) ReleaseContext
{
	mfContext* mfc;
	MRDPView* view;
	mfc = (mfContext*) context;
	view = (MRDPView*) mfc->view;
	[view exitFullScreenModeWithOptions:nil];
	[view releaseResources];
	[view release];
	mfc->view = nil;
	freerdp_client_context_free(context);
	context = nil;
}


/** *********************************************************************
 * called when we fail to connect to a RDP server - Make sure this is called from the main thread.
 ***********************************************************************/

- (void) rdpConnectError : (NSString*) withMessage
{
	mfContext* mfc;
	MRDPView* view;
	mfc = (mfContext*) context;
	view = (MRDPView*) mfc->view;
	[view exitFullScreenModeWithOptions:nil];
	NSString* message = withMessage ? withMessage : @"Error connecting to server";
	NSAlert* alert = [[NSAlert alloc] init];
	[alert setMessageText:message];
	[alert beginSheetModalForWindow:[self window]
	 modalDelegate:self
	 didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:)
	 contextInfo:nil];
}


/** *********************************************************************
 * just a terminate selector for above call
 ***********************************************************************/

- (void) alertDidEnd:(NSAlert*)a returnCode:(NSInteger)rc contextInfo:(void*)ci
{
	[NSApp terminate:nil];
}


@end

/** *********************************************************************
 * On connection error, display message and quit application
 ***********************************************************************/

void AppDelegate_ConnectionResultEventHandler(void* ctx,
        ConnectionResultEventArgs* e)
{
	NSLog(@"ConnectionResult event result:%d\n", e->result);

	if (_singleDelegate)
	{
		if (e->result != 0)
		{
			NSString* message = nil;

			if (connectErrorCode == AUTHENTICATIONERROR)
			{
				message = [NSString stringWithFormat:@"%@",
				           @"Authentication failure, check credentials."];
			}

			// Making sure this should be invoked on the main UI thread.
			[_singleDelegate performSelectorOnMainThread:@selector(rdpConnectError:)
			 withObject:message waitUntilDone:FALSE];
		}
	}
}

void AppDelegate_ErrorInfoEventHandler(void* ctx, ErrorInfoEventArgs* e)
{
	NSLog(@"ErrorInfo event code:%d\n", e->code);

	if (_singleDelegate)
	{
		// Retrieve error message associated with error code
		NSString* message = nil;

		if (e->code != ERRINFO_NONE)
		{
			const char* errorMessage = freerdp_get_error_info_string(e->code);
			message = [[NSString alloc] initWithUTF8String:errorMessage];
		}

		// Making sure this should be invoked on the main UI thread.
		[_singleDelegate performSelectorOnMainThread:@selector(rdpConnectError:)
		 withObject:message waitUntilDone:TRUE];
		[message release];
	}
}

void AppDelegate_EmbedWindowEventHandler(void* ctx, EmbedWindowEventArgs* e)
{
	rdpContext* context = (rdpContext*) ctx;

	if (_singleDelegate)
	{
		mfContext* mfc = (mfContext*) context;
		_singleDelegate->mrdpView = mfc->view;

		if (_singleDelegate->window)
		{
			[[_singleDelegate->window contentView] addSubview:mfc->view];
		}

		mac_set_view_size(context, mfc->view);
	}
}

void AppDelegate_ResizeWindowEventHandler(void* ctx, ResizeWindowEventArgs* e)
{
	rdpContext* context = (rdpContext*) ctx;
	fprintf(stderr, "ResizeWindowEventHandler: %d %d\n", e->width, e->height);

	if (_singleDelegate)
	{
		mfContext* mfc = (mfContext*) context;
		mac_set_view_size(context, mfc->view);
	}
}

void mac_set_view_size(rdpContext* context, MRDPView* view)
{
	// set client area to specified dimensions
	NSRect innerRect;
	innerRect.origin.x = 0;
	innerRect.origin.y = 0;
	innerRect.size.width = context->settings->DesktopWidth;
	innerRect.size.height = context->settings->DesktopHeight;
	[view setFrame:innerRect];
	// calculate window of same size, but keep position
	NSRect outerRect = [[view window] frame];
	outerRect.size = [[view window] frameRectForContentRect:innerRect].size;
	// we are not in RemoteApp mode, disable larger than resolution
	[[view window] setContentMaxSize:innerRect.size];
	// set window to given area
	[[view window] setFrame:outerRect display:YES];

	if (context->settings->Fullscreen)
		[[view window] toggleFullScreen:nil];
}
