//
//  TeXDistributionBridge.m
//  TeXDist
//
//  Created by coder on 08/12/06.
//  Copyright 2006 __MyCompanyName__. All rights reserved.
//

#import "TeXDistributionBridge.h"
#import <unistd.h>

NSString * const TeXDistExtension = @"texdist";
NSString * const TeXDistComponentTeX = @"TeX";
NSString * const TeXDistComponentDistributions = @"Distributions";
NSString * const TeXDistComponentDefault = @".DefaultTeX";
NSString * const TeXDistComponentContents = @"Contents";
NSString * const TeXDistComponentRoot = @"Root";
NSString * const TeXDistComponentPrograms = @"Programs";

#import <Carbon/Carbon.h>
#import <Security/Security.h>
#import <sys/sysctl.h>
#import <mach/machine.h>

static AuthorizationRef TeXDist_Authorization = NULL;

@implementation TeXDistributionBridge
+ (BOOL)isI386;
{
	int cpu_type;
	size_t length = sizeof(cpu_type);
	sysctlbyname("hw.cputype", &cpu_type, &length, NULL, 0);
	return cpu_type == CPU_TYPE_I386;
}
+ (BOOL)isAlternateKeyDown;
{
    return [self isKeyDown:34];
}
+ (BOOL)isKeyDown:(char)keyNumber;
{
    long q = keyNumber/32;
    long r = keyNumber % 32;
    //keyNumber = 32 * q + r
    KeyMap theKeys;
    GetKeys ( theKeys );
#if (__LITTLE_ENDIAN__)
   UInt32 keys = CFSwapInt32BigToHost( theKeys[q].bigEndianValue );//CFByteOrder.h
#else
   UInt32 keys = theKeys[q];
#endif
    return keys&(1<<r);
}
+ (NSString *)TeXDistributionsPath;
{
	NSArray * LibraryTeXs = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSLocalDomainMask,NO);
	NSString * LibraryTeX = [LibraryTeXs lastObject];
	LibraryTeX = [LibraryTeX stringByAppendingPathComponent:TeXDistComponentTeX];
	LibraryTeX = [LibraryTeX stringByAppendingPathComponent:TeXDistComponentDistributions];
	// This part is for testing only
	// Just to deal with a local copy of the Library
	NSProcessInfo * PI = [NSProcessInfo processInfo];
	NSArray * arguments = [PI arguments];
	NSEnumerator * E = [arguments objectEnumerator];
	NSString * argument;
	while(argument = [E nextObject])
	{
		if([argument isEqual:@"--domain"]||[argument isEqual:@"-d"])
		{
			if(argument = [E nextObject])
			{
				if([argument length])
				{
					return LibraryTeX = [argument stringByAppendingPathComponent:LibraryTeX];
				}
			}
		}
	}
	return LibraryTeX;
}
+ (NSArray *)__TeXDistributions;
{
	static id TDs = nil;
	if(!TDs)
	{
		TDs = [[NSMutableArray array] retain];
		NSString * LibraryTeX = [[self class] TeXDistributionsPath];
		// what are the available libraries
		NSArray * paths = [[NSFileManager defaultManager] directoryContentsAtPath:LibraryTeX];
		NSEnumerator * E = [paths objectEnumerator];
		NSString * fileName = nil;
		id O = nil;
		while(fileName = [E nextObject])
		{
			if(O = [[[TeXDistributionBridge alloc] initWithContentsFoFile:[LibraryTeX stringByAppendingPathComponent:fileName] error:nil] autorelease])
			{
				if([[O intelPrograms] count]+[[O universalPrograms] count]+[[O powerpcPrograms] count])
				{
					[TDs addObject:O];
				}
			}
		}
		if(![TDs count])
		{
			// we should recover the texdist from the finder
			
		}
	}
	return TDs;
}
+ (void)destroyAuthorization;
{
	if(!TeXDist_Authorization)
	{
		return;
	}
	OSStatus err = AuthorizationFree(TeXDist_Authorization,kAuthorizationFlagDestroyRights);
	if(err)
	{
		NSLog(@"AuthorizationFree error:%i",err);
	}
	TeXDist_Authorization = nil;
	return;
}
+ (id)defaultTeXDistribution;
{
	NSString * distributionsPath = [[self class] TeXDistributionsPath];
	NSString * defaultPath = [distributionsPath stringByAppendingPathComponent:TeXDistComponentDefault];
	if(![[NSFileManager defaultManager] fileExistsAtPath:defaultPath])
	{
		return nil;
	}
	NSString * defaultContents = [defaultPath stringByAppendingPathComponent:TeXDistComponentContents];
	//PATH_MAX
	char * buf = (char *)NSZoneMalloc(NSDefaultMallocZone(),PATH_MAX+1);
	if(!buf)
	{
		NSLog(@"Memory management error (1)");
		return nil;
	}
	
	ssize_t size = readlink([defaultContents fileSystemRepresentation],buf,PATH_MAX);
	if(size<0)
	{
		NSLog(@"Error reading the contents of the link");
		return nil;
	}
	buf[size]='\0';
	NSString * readLink = [NSString stringWithUTF8String:buf];
	NSZoneFree(NSDefaultMallocZone(),buf);
	buf = nil;
	NSString * targetName = [readLink stringByDeletingLastPathComponent];
	targetName = [defaultPath stringByAppendingPathComponent:targetName];
	targetName = [targetName stringByStandardizingPath];
	NSString * extension = [targetName pathExtension];
	extension = [extension lowercaseString];
	if([extension isEqual:TeXDistExtension])
	{
		targetName = [targetName stringByResolvingSymlinksInPath];
		targetName = [targetName lowercaseString];
		NSArray * distributions = [self __TeXDistributions];
		NSEnumerator * E = [distributions objectEnumerator];
		TeXDistributionBridge * distribution;
		while(distribution = [E nextObject])
		{
			NSString * fileName = [distribution fileName];
			fileName = [fileName lowercaseString];
			if([fileName isEqual:targetName])
			{
				return distribution;
			}
		}
	}
	return nil;
}
+ (void)makeDefaultTeXDistribution:(TeXDistributionBridge *)distribution;
{
	// sanity
	if(![distribution isKindOfClass:self])
	{
		return;
	}
	// consistency
	if([[self defaultTeXDistribution] isEqual:distribution])
	{
		return;
	}
#if 0
//	NSView * V = [NSApp valueForKey:@"tabView"];// bah
//	NSWindow * W = [V window];
	NSBundle * B = [NSBundle bundleForClass:[self class]];
	NSBeginAlertSheet(
		NSLocalizedStringFromTableInBundle(@"DEFAULT TEX DISTRIBUTION",nil,B,""),
		nil, nil,
		NSLocalizedStringFromTableInBundle(@"CANCEL",nil,B,""),
		nil, self, NULL, @selector(makeDefaultTeXDistributionSheetDidDismiss:returnCode:distribution:),
		[distribution retain],
		NSLocalizedStringFromTableInBundle(@"CHANGE TO %@?",nil,B,""),
			[distribution prettyName]);
	return;
}
+ (void)makeDefaultTeXDistributionSheetDidDismiss:(NSWindow *)sheet returnCode:(int)returnCode distribution:(TeXDistributionBridge *)distribution;
{
	[distribution autorelease];
	if(returnCode == NSOKButton)
#endif
	{
		// sanity
		if(![distribution isKindOfClass:self])
		{
			return;
		}

		OSStatus err;

		if(!TeXDist_Authorization)
		{
			if (err = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &TeXDist_Authorization))
			{
				NSLog(@"error:%i",err);
				return;
			}
			else
			{
				NSLog(@"TeXDist_Authorization is created");
			}
		}
//NSLog(@"Autorization created");
		AuthorizationFlags authorizationFlags =
			kAuthorizationFlagDefaults|
			kAuthorizationFlagExtendRights|
			kAuthorizationFlagPreAuthorize|
			kAuthorizationFlagInteractionAllowed;

		AuthorizationItem actionItem[1];
		NSBundle * B = [NSBundle bundleForClass:self];
		NSString * path = [B pathForAuxiliaryExecutable:@"DefaultTeXDistLinker"];
		if(![path length])
		{
			NSLog(@"This is a BIG problem: the DefaultTeXDistLinker auxiliary executable is missing!");
			return;
		}
//NSLog(@"path is:%@",path);
		actionItem[0].name = kAuthorizationRightExecute;
		actionItem[0].value = (char *)[path fileSystemRepresentation];
		actionItem[0].valueLength = strlen(actionItem[0].value);
		
		AuthorizationRights authorizationRights = { 1, actionItem };

		// Request the application-specific right.

#if 1
		if(err = AuthorizationCopyRights(
			TeXDist_Authorization,         // authorization
			&authorizationRights,               // rights
			kAuthorizationEmptyEnvironment,     // environment
			authorizationFlags,                 // flags
			NULL                    // authorizedRights
				))
		{
			NSLog(@"AuthorizationCopyRights error:%i",err);
			return;
		}
#endif
//NSLog(@"AuthorizationCopyRights");
		id old = [self defaultTeXDistribution];
		[old willChangeValueForKey:@"default"];
		[distribution willChangeValueForKey:@"default"];
		NSString * distributionsPath = [self TeXDistributionsPath];
		NSString * dest = [distribution fileName];
		dest = [dest lastPathComponent];
		dest = [dest stringByAppendingPathComponent:TeXDistComponentContents];
		dest = [@".." stringByAppendingPathComponent:dest];
		NSString * src = [distributionsPath stringByAppendingPathComponent:TeXDistComponentDefault];
		char * arguments[5];
		// 	sudo ln -s ../gwTeX.texdist/Contents /Library/TeX/Distributions/.DefaultTeX
		arguments[0]="--source";
		arguments[1]=(char *)[src fileSystemRepresentation];
		arguments[2]="--destination";
		arguments[3]=(char *)[dest fileSystemRepresentation];
		arguments[4]=nil;
		#if 1
		FILE* communicationsPipe;// just to make the task synchronous to have UI react immediately
		OSStatus errStatus = AuthorizationExecuteWithPrivileges (TeXDist_Authorization, actionItem[0].value, 
		   kAuthorizationFlagDefaults, 
			arguments, 
			   &communicationsPipe);
		if(errStatus == noErr)
		{
			NSMutableData* tempData = [NSMutableData dataWithLength:512];
			while(fread([tempData mutableBytes],1,512,communicationsPipe)==512);// End of the task.
		}
//NSLog(@"errStatus:%i",errStatus);
		#else
		NSTask * task = [[[NSTask alloc] init] autorelease];
		[task setLaunchPath:path];
		[task setArguments:
			[NSArray arrayWithObjects:@"--source",@"/Users/coder/IN",@"--destination",@"DESTINATION",nil]];
		[task launch];
		#endif
		[distribution didChangeValueForKey:@"default"];
		[old didChangeValueForKey:@"default"];
	}
	return;
}
#define weAreOnIntel [[self class] isI386]
- (id)initWithContentsFoFile:(NSString *)aFileName error:(NSError **)outErrorPtr;
{
	if([[[aFileName pathExtension] lowercaseString] isEqual:TeXDistExtension])
	{
		if(self = [self init])
		{
			[fileName autorelease];
			fileName = [aFileName copy];

			architectureMatches = YES;
			lock = YES;
		}
		return self;
	}
	else
	{
		[self release];
		return nil;
	}
}
- (void)dealloc;
{
	[fileName release];
	[prettyName release];
	[dirName release];
	[statusDescription release];
	[attributedDescription release];
	[architectures release];
	[currentArchitecture release];
	[powerpcProgramsDirs release];
	[universalProgramsDirs release];
	[intelProgramsDirs release];
	[currentPrograms release];
//	[ release];
	[super dealloc];
	return;
}
- (NSArray *)powerpcPrograms;
{
	if(!powerpcProgramsDirs)
	{
		if(weAreOnIntel)
		{
			powerpcProgramsDirs = [[NSArray array] copy];
		}
		else
		{
			NSString * contentsPath = [fileName stringByAppendingPathComponent:TeXDistComponentContents];
			if(![[NSFileManager defaultManager] fileExistsAtPath:contentsPath])
			{
				contentsPath = fileName;
			}
			// Retrieveing all the binaries
			NSString * programsPath = [contentsPath stringByAppendingPathComponent:TeXDistComponentPrograms];
			NSArray * candidates = [[NSFileManager defaultManager] directoryContentsAtPath:programsPath];
#if MAC_OS_X_VERSION_10_4 <= MAC_OS_X_VERSION_MAX_ALLOWED
			NSPredicate * ppcPredicate = [NSPredicate predicateWithFormat:@"(SELF contains[c] 'ppc') OR (SELF contains[c] 'powerpc')"];
			candidates = [candidates filteredArrayUsingPredicate:ppcPredicate];
#else
			NSEnumerator * __E = [candidates objectEnumerator];
			candidates = [NSMutableArray array];
			NSString * __component;
			while(__component = [__E nextObject])
			{
				if([__component rangeOfString:@"ppc" options:NSCaseInsensitiveSearch].length)
				{
					[(id)candidates addObject:__component];
				}
				else if([__component rangeOfString:@"powerpc" options:NSCaseInsensitiveSearch].length)
				{
					[(id)candidates addObject:__component];
				}
			}
#endif
			NSString * target;
			NSString * path;
			NSEnumerator * E = [candidates objectEnumerator];
			NSMutableArray * goodCandidates = [NSMutableArray array];
			while(path = [E nextObject])
			{
				target = [programsPath stringByAppendingPathComponent:path];
				target = [target stringByResolvingSymlinksInPath];
				if([[NSFileManager defaultManager] fileExistsAtPath:target])
				{
					[goodCandidates addObject:path];
				}
			}
			powerpcProgramsDirs = [goodCandidates copy];
		}
	}
	return powerpcProgramsDirs;
}
- (NSArray *)intelPrograms;
{
	if(!intelProgramsDirs)
	{
		if(weAreOnIntel)
		{
			NSString * contentsPath = [fileName stringByAppendingPathComponent:TeXDistComponentContents];
			if(![[NSFileManager defaultManager] fileExistsAtPath:contentsPath])
			{
				contentsPath = fileName;
			}
			// Retrieveing all the binaries
			NSString * programsPath = [contentsPath stringByAppendingPathComponent:TeXDistComponentPrograms];
			NSArray * candidates = [[NSFileManager defaultManager] directoryContentsAtPath:programsPath];
#if MAC_OS_X_VERSION_10_4 <= MAC_OS_X_VERSION_MAX_ALLOWED
			NSPredicate * intelPredicate = [NSPredicate predicateWithFormat:@"(SELF contains[c] 'intel') OR (SELF contains[c] 'i386')"];
			candidates = [candidates filteredArrayUsingPredicate:intelPredicate];
#else
			NSEnumerator * __E = [candidates objectEnumerator];
			candidates = [NSMutableArray array];
			NSString * __component;
			while(__component = [__E nextObject])
			{
				if([__component rangeOfString:@"i386" options:NSCaseInsensitiveSearch].length)
				{
					[(id)candidates addObject:__component];
				}
				else if([__component rangeOfString:@"intel" options:NSCaseInsensitiveSearch].length)
				{
					[(id)candidates addObject:__component];
				}
			}
#endif
			NSString * target;
			NSString * path;
			NSEnumerator * E = [candidates objectEnumerator];
			NSMutableArray * goodCandidates = [NSMutableArray array];
			while(path = [E nextObject])
			{
				target = [programsPath stringByAppendingPathComponent:path];
				target = [target stringByResolvingSymlinksInPath];
				if([[NSFileManager defaultManager] fileExistsAtPath:target])
				{
					[goodCandidates addObject:path];
				}
			}
			intelProgramsDirs = [goodCandidates copy];
		}
		else
		{
			intelProgramsDirs = [[NSArray array] copy];
		}
	}
	return intelProgramsDirs;
}
- (NSArray *)universalPrograms;
{
	if(!universalProgramsDirs)
	{
		{
			NSString * contentsPath = [fileName stringByAppendingPathComponent:TeXDistComponentContents];
			if(![[NSFileManager defaultManager] fileExistsAtPath:contentsPath])
			{
				contentsPath = fileName;
			}
			// Retrieveing all the binaries
			NSString * programsPath = [contentsPath stringByAppendingPathComponent:TeXDistComponentPrograms];
			NSArray * candidates = [[NSFileManager defaultManager] directoryContentsAtPath:programsPath];
#if MAC_OS_X_VERSION_10_4 <= MAC_OS_X_VERSION_MAX_ALLOWED
			NSPredicate * universalPredicate = [NSPredicate predicateWithFormat:@"SELF contains[c] 'universal'"];
			candidates = [candidates filteredArrayUsingPredicate:universalPredicate];
#else
			NSEnumerator * __E = [candidates objectEnumerator];
			candidates = [NSMutableArray array];
			NSString * __component;
			while(__component = [__E nextObject])
			{
				if([__component rangeOfString:@"i386" options:NSCaseInsensitiveSearch].length)
				{
					[(id)candidates addObject:__component];
				}
				else if([__component rangeOfString:@"intel" options:NSCaseInsensitiveSearch].length)
				{
					[(id)candidates addObject:__component];
				}
			}
#endif
			NSString * target;
			NSString * path;
			NSEnumerator * E = [candidates objectEnumerator];
			NSMutableArray * goodCandidates = [NSMutableArray array];
			while(path = [E nextObject])
			{
				target = [programsPath stringByAppendingPathComponent:path];
				target = [target stringByResolvingSymlinksInPath];
				if([[NSFileManager defaultManager] fileExistsAtPath:target])
				{
					[goodCandidates addObject:path];
				}
			}
			universalProgramsDirs = [goodCandidates copy];
		}
	}
	return universalProgramsDirs;
}
- (NSString *)fileName;
{
	return fileName;
}
- (NSString *)dirName;
{
	return dirName?:(dirName = [[fileName stringByDeletingLastPathComponent] copy]);
}
- (NSString *)contents;
{
	if(!contents)
	{
		contents = [self fileName];
		contents = [contents stringByAppendingPathComponent:TeXDistComponentContents];
		NSString * target = [[NSFileManager defaultManager] pathContentOfSymbolicLinkAtPath:contents];
		contents = target? [target copy]:[contents copy];
	}
	return contents;
}
- (NSString *)prettyName;
{
	if(!prettyName)
	{
		prettyName = [[NSFileManager defaultManager] displayNameAtPath:fileName];
		prettyName = [prettyName stringByDeletingPathExtension];
		prettyName = [prettyName copy];
	}
	return prettyName;
}
- (NSImage *)lockedStatusImage;
{
	if(lock)
	{
		NSImage * I = [NSImage imageNamed:@"__LockLite"];
		if(!I)
		{
			I = [[[NSImage alloc] initWithSize:NSMakeSize(16,16)] autorelease];
			[I setName:@"__LockLite"];
			[I lockFocus];
			NSImage * i = [NSImage imageNamed:@"Lock"];
			[i setSize:NSMakeSize(16,16)];
			[i drawAtPoint:NSZeroPoint fromRect:NSMakeRect(0,0,16,16) operation:NSCompositeCopy fraction:0.5];
			[I unlockFocus];
		}
		return I;
	}
	return nil;
}
- (void)open;
{
	// when the user double clicks a line in the table view, the root distribution opens
	NSString * path = [fileName stringByAppendingPathComponent:TeXDistComponentContents];
	NSString * file = nil;
	if([[NSFileManager defaultManager] fileExistsAtPath:path])
	{
		file = [path stringByAppendingPathComponent:TeXDistComponentRoot];
	}
	else
	{
		path = fileName;
		file = [path stringByAppendingPathComponent:TeXDistComponentRoot];
	}
	if([[self class] isAlternateKeyDown])
	{
		file = nil;
	}
	else
	{
		path = file;
		file = nil;
	}
	[[NSWorkspace sharedWorkspace] selectFile:file inFileViewerRootedAtPath:path];
	return;
}
- (BOOL)canDefault;
{
	if(weAreOnIntel)
	{
		return ([universalProgramsDirs count] + [intelProgramsDirs count])>0;
	}
	else
	{
		return ([universalProgramsDirs count] + [powerpcProgramsDirs count])>0;
	}
}
- (BOOL)isDefault;
{
	return debugDefault || (self == [[self class] defaultTeXDistribution]);
}
- (void)setDefault:(BOOL)yorn;
{
	[[self class] makeDefaultTeXDistribution:self];
	return;
}
- (NSArray *)architectures;
{
	return [architectures allKeys];
}
- (void)setCurrentArchitecture:(NSString *)architecture;
{
	if(![architecture isEqual:currentArchitecture])
	{
		[self willChangeValueForKey:@"currentArchitecture"];
		[currentArchitecture release];
		currentArchitecture = [architecture copy];
		[self didChangeValueForKey:@"currentArchitecture"];
	}
	return;
}
- (NSAttributedString *)attributedDescription;
{
	if(attributedDescription)
		return attributedDescription;
				// the attributed description?
	NSBundle * bundle = [NSBundle bundleWithPath:fileName];
#if MAC_OS_X_VERSION_10_4 <= MAC_OS_X_VERSION_MAX_ALLOWED
	NSArray * candidates = [bundle pathsForResourcesOfType:nil inDirectory:nil];
	NSPredicate * descriptionPredicate = [NSPredicate predicateWithFormat:@"SELF contains[c] 'description'"];
	NSArray * descriptions = [candidates filteredArrayUsingPredicate:descriptionPredicate];
	NSString * descriptionPath = [descriptions lastObject];
	if(descriptionPath)
	{
		NSURL * theURL = [NSURL fileURLWithPath:descriptionPath];
		NSError * localError;
		if(attributedDescription = [[NSAttributedString alloc]
			initWithURL:theURL options:nil documentAttributes:nil error:&localError])
		{
			return attributedDescription;
		}
		if(localError)
		{
			attributedDescription = [[NSAttributedString alloc]
				initWithString:[localError localizedDescription]];
		}
	}
#else
	NSString * descriptionPath = [bundle pathForResource:@"Description" ofType:@"rtf"];
	if(descriptionPath)
	{
		NSURL * theURL = [NSURL fileURLWithPath:descriptionPath];
		if(attributedDescription = [[NSAttributedString alloc]
			initWithURL:theURL documentAttributes:nil])
		{
			return attributedDescription;
		}
	}
#endif
	return attributedDescription;

}
- (NSImage *)statusImage;
{
	if(!architectureMatches)
	{
		NSImage * I = [NSImage imageNamed:@"__Caution"];
		if(!I)
		{
			I = [[[NSImage alloc] initWithSize:NSMakeSize(16,16)] autorelease];
			[I setName:@"__Caution"];
			[I lockFocus];
			NSImage * i = [NSImage imageNamed:@"Caution"];
			[i setScalesWhenResized:YES];
			[i setSize:NSMakeSize(16,16)];
			[i drawAtPoint:NSZeroPoint fromRect:NSMakeRect(0,0,16,16) operation:NSCompositeCopy fraction:1.0];
			[I unlockFocus];
		}
		return I;
	}
	return nil;
}
@end
