//
//  TeXDistController.m
//  TeXDist
//
//  Created by coder on 08/12/06.
//  Copyright 2006-2011 Laurens'Tribune. All rights reserved.
//

#import "TeXDistController.h"
#import "TeXDistArchitecture.h"
#import "TeXDistributionBridge.h"
#import "TeXDistributionUtils.h"
#import "TeXDistCommon.h"
#import "TeXDistAuthentication.h"

@implementation TeXDistArchitecture(Control)
- (BOOL)isValid;
{
    return NOT [self.destinationPath isEqual:[TeXDistUtils path_texbin]];
}
@end

@interface NSBundle(TeXDist)
@property (readonly) NSDictionary * infoDictionary4TeXDist;
@end

@implementation NSBundle(TeXDist)
- (NSDictionary *)infoDictionary4TeXDist;
{
    return [NSDictionary dictionaryWithContentsOfFile:
        [self.resourcePath.stringByDeletingLastPathComponent stringByAppendingPathComponent:@"Info.plist"]];
}
@end

@implementation TeXDistribution
@dynamic isActive;
@dynamic softLinkDestination;
@dynamic texbinPath;
- (BOOL)canBeActive;
{
	return self.architectures.count>0;
}
- (BOOL)isActive;//KVC from the UI
{
	return (self == [TeXDistController sharedController].activeDistribution);
}
- (void)setIsActive:(BOOL)yorn;//KVC from the UI
{
	if (yorn) {
        [TeXDistController sharedController].activeDistribution = self;
    }
	return;
}
- (id)initWithContentsOfURL:(NSURL *)aFileURL andReturnError:(NSError **)RORef;
{
    if ((self = [super initWithContentsOfURL:aFileURL andReturnError:RORef])) {
        NSString * aPath = aFileURL.path;
        NSBundle * B = [NSBundle bundleWithPath:aPath];
        NSDictionary * D = B.infoDictionary4TeXDist;
        id candidate = nil;
        if ((candidate = [D objectForKey:@"LSMinimumSystemVersion"])
            && (candidate = [TeXDistSystemVersion systemVersionWithString:candidate error:RORef])
                && [self validateMinimumSystemVersion:&candidate error:RORef]) {
            self.minimumSystemVersion = candidate;
        }
        if ((candidate = [D objectForKey:@"LSMaximumSystemVersion"])
            && (candidate = [TeXDistSystemVersion systemVersionWithString:candidate error:RORef])
                && [self validateMaximumSystemVersion:&candidate error:RORef]) {
            self.maximumSystemVersion = candidate;
        }
        if ((candidate = [D objectForKey:@"LSArchitecturePriority"])
                && [self validateArchitecturePriority:&candidate error:RORef]) {
            self.architecturePriority = candidate;
        }
        if ((candidate = [D objectForKey:@"LSMinimumSystemVersionByArchitecture"])
                && [self validateMinimumSystemVersionsByArch:&candidate error:RORef]) {
            self.minimumSystemVersionsByArch = candidate;
        }
        if ((candidate = [D objectForKey:@"LSMaximumSystemVersionByArchitecture"])
                && [self validateMaximumSystemVersionsByArch:&candidate error:RORef]) {
            self.maximumSystemVersionsByArch = candidate;
        }
        NSString * programsPath = self.fileURL.programsPath4TeXDist;
        NSMutableArray * candidates = [NSMutableArray array];
        NSString * arch = nil;
        NSString * resolvedArchPath = nil;
        TeXDistArchitecture * TDA = nil;
        for (arch in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:programsPath error:RORef]) {
            if ([[TeXDistUtils CPUNames] containsObject:arch]) {
                //  Not all the foo.texdist/Contents/Programs files are accepted
                resolvedArchPath = [programsPath stringByAppendingPathComponent:arch].stringByResolvingSymlinksInPath;
                //  Attempt to list the content of the directory, ignore on error
                for (NSString * bin in [TeXDistUtils expectedPrograms]) {
                    NSString * binPath = [resolvedArchPath stringByAppendingPathComponent:bin];
                    NSString * resolvedBinPath = [binPath stringByResolvingSymlinksInPath];
                    if ([[NSFileManager defaultManager] isExecutableFileAtPath:resolvedBinPath]) {
                        NSString * theName = resolvedArchPath.lastPathComponent;
                        if ([[TeXDistUtils CPUNames] containsObject:theName] OR (theName = arch)) {
                            TDA = [[[TeXDistArchitecture alloc] initWithName:theName source:arch destinationPath:resolvedArchPath distributionName:self.displayName] autorelease];
                            TeXDistLog(2,@"New candidate: %@",TDA);
                            [candidates addObject:TDA];
                            break;// the for loop above
                        }
                    }
                }
            }
        }
        NSMutableArray * architectures = [NSMutableArray array];
        if (candidates.count) {
            TeXDistLog(3,@"TeXDist: list of known architectures %@",[TeXDistUtils CPUNames]);
            for (arch in [TeXDistUtils CPUNames]) {
                NSEnumerator * E = [[candidates.copy autorelease] objectEnumerator];
                while ((TDA = E.nextObject)) {
                    if ([arch isEqual:TDA.name]) {
                        TeXDistSystemVersion * TDSV = [self.minimumSystemVersionsByArch objectForKey:TDA.name]?
                            :[TeXDistSystemVersion firstSystemVersion];
                        TDA.minimumSystemVersion = [self.minimumSystemVersion compare:TDSV] == NSOrderedAscending?
                            TDSV:self.minimumSystemVersion;
                        TDSV = [self.maximumSystemVersionsByArch objectForKey:TDA.name]?
                            :[TeXDistSystemVersion lastSystemVersion];
                        TDA.maximumSystemVersion = [self.maximumSystemVersion compare:TDSV] == NSOrderedAscending?
                            self.maximumSystemVersion:TDSV;
                        if ([TDA.minimumSystemVersion compare:[TeXDistSystemVersion runningSystemVersion]] != NSOrderedDescending) {
                            if ([TDA.maximumSystemVersion compare:[TeXDistSystemVersion runningSystemVersion]] ==NSOrderedDescending) {
                                [architectures addObject:TDA];
                            } else {
                                TeXDistLog(3,@"TeXDist Architecture OUTDATED (too old) %@",TDA.longDescription);
                            }
                        } else {
                            TeXDistLog(3,@"TeXDist Architecture OUTDATED (too recent) %@",TDA.longDescription);
                        }
                        [candidates removeObject:TDA];
                        TeXDistArchitecture * tda = nil;
                        while ((tda = E.nextObject)) {
                            if ([arch isEqual:tda.name]) {
                                [TDA addName:tda.name];
                                [candidates removeObject:tda];
                                TeXDistLog(3,@"TeXDist Architecture IGNORED (duplicate) %@",tda.longDescription);
                            }
                        }
                    }
                }
            }
            if (candidates.count) {
                TeXDistLog(2,@"TeXDist Architectures IGNORED (unknown) %@",candidates);
            }
        }
        self.architectures = architectures;
        if (self.architectures.count) {
            NSString * texbin = [self.fileURL.path stringByAppendingPathComponent:[TeXDistUtils texbinRelativePath]];
            NSString * destination = [[NSFileManager defaultManager]
                                      destinationOfSymbolicLinkAtPath:texbin error:RORef];
            if (NOT(self.selectedArchitecture = self.activeArchitecture = [self architectureWithName:destination])) {
                self.selectedArchitecture = self.activeArchitecture = [self.architectures objectAtIndex:0];
            }
            TeXDistLog(2,@"%@",self.longDescription);
            for (TDA in self.architectures) {
                TeXDistLog(2,@"%@",TDA.longDescription);
            }
        }
    }
    return self;
}
#   define TeXDistRunAlertPanel(TITLE,FORMAT,...)\
    do {\
        if ([TeXDistController sharedController].noUserInterface) {\
            TeXDistLog(TeXDistLogLevelAlways,@"%@",TITLE);\
            TeXDistLog(TeXDistLogLevelAlways,FORMAT,__VA_ARGS__,nil);\
        } else {\
            NSRunAlertPanel(IL8N(TITLE), IL8N(FORMAT), nil,nil,nil,__VA_ARGS__);\
        }\
    } while (NO)
- (BOOL)updateActiveArchitectureAndReturnError:(NSError **)RORef;
{
    self.selectedArchitecture = self.activeArchitecture = nil;
    if (self.architectures.count) {
        TeXDistArchitecture * firstArchitecture = [self.architectures objectAtIndex:0];
        NSString * texbin = [self.fileURL.path stringByAppendingPathComponent:[TeXDistUtils texbinRelativePath]];
        NSString * destination = [[NSFileManager defaultManager]
                                  destinationOfSymbolicLinkAtPath:texbin error:RORef];
        if (NOT destination) {
            //  This is an old texdist
            //  We do not try to update the texdist bundle
            if (self.architectures.count == 1) {
                self.selectedArchitecture = firstArchitecture;
                return NO;
            }
        }
        if (NOT(self.activeArchitecture = [self architectureWithName:destination])) {
            destination = firstArchitecture.name;
            TeXDistRunAlertPanel(@"WRONG ARCHITECTURE", @"UPDATING %@ TO USE A CORRECT ARCHITECTURE",self.displayName);
            if ([[TeXDistAuthentication sharedInstance] createSymbolicLinkAtPath:texbin withDestinationPath:destination error:RORef]) {
                destination = [[NSFileManager defaultManager]
                               destinationOfSymbolicLinkAtPath:texbin error:RORef];
                if (NOT(self.activeArchitecture = [self architectureWithName:destination])) {
                    self.selectedArchitecture = firstArchitecture;
                }
            } else {
                TeXDistRunAlertPanel(@"FAILURE", @"THE LAST OPERATION FAILED",nil);
                return NO;
            }
        }
        return YES;
    }
    return NO;
}
- (void)open;
{
	// when the user double clicks a line in the table view, the root distribution opens
	NSString * file = [self.fileURL.path stringByAppendingPathComponent:[TeXDistUtils rootRelativePath]];
	if (NOT [[NSFileManager defaultManager] fileExistsAtPath:file] || [TeXDistUtils isAlternateKeyDown])
	{
		file = nil;
	}
	NSString * folder = [file stringByDeletingLastPathComponent];
	[[NSWorkspace sharedWorkspace] selectFile:file inFileViewerRootedAtPath:folder];
	return;
}
- (NSString *)softLinkDestination;
{
    NSString * softLinkDestination = @"..";
    softLinkDestination = [softLinkDestination stringByAppendingPathComponent:self.fileURL.path.lastPathComponent];
    return [softLinkDestination stringByAppendingPathComponent:[TeXDistUtils contentsRelativePath]];
}
- (NSString *)texbinPath;
{
    return [self.fileURL.path stringByAppendingPathComponent:[TeXDistUtils texbinRelativePath]];
}
- (NSString *)texbinDestination;
{
    return [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath:self.texbinPath error:NULL];
}
- (NSString *)longDescription;
{
    return [NSString stringWithFormat:@"< TeX Distribution: %#x\n"
    @"  Display name: %@\n"
    @"  File URL: %@\n"
    @"  Programs/texbin destination: %@\n"
    @"  Active architecture: %@\n"
    @"  Selected architecture: %@\n"
    @"  Available architectures \n%@\n>",
    self,self.displayName,self.fileURL,self.texbinDestination,self.activeArchitecture,self.selectedArchitecture,self.architectures];
}
@end

@interface TeXDistController()
@property (readwrite,copy) NSArray * distributions;
@property (readwrite,retain) TeXDistribution * _activeDistribution;
@property (readwrite) BOOL noUserInterface;
- (BOOL)updateDistributionsAndReturnError:(NSError **)RORef;
- (BOOL)updateActiveDistributionAndReturnError:(NSError **)RORef;
@end

@implementation TeXDistController
@synthesize distributions = iVarDistributions;
@synthesize _activeDistribution = iVarActiveDistribution;
@synthesize noUserInterface = iVarNoUserInterface;
static TeXDistController * sharedTeXDistControllerInstance = nil;
+ (TeXDistController *)sharedController;
{
	return sharedTeXDistControllerInstance?:(sharedTeXDistControllerInstance=[[TeXDistController alloc] init]);
}
- (TeXDistController *) init;
{
    if (sharedTeXDistControllerInstance) {
        [self dealloc];
        return sharedTeXDistControllerInstance;
    }
    if ((self = [super init])) {
        self.noUserInterface = NO;
        for (NSString * argument in [NSProcessInfo processInfo].arguments) {
            if ([argument isEqual:kTeXDistNoUserInterfaceArgument]) {
                self.noUserInterface = YES;
            }
        }
        [self addObserver:self forKeyPath:@"_activeDistribution" options:(NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew) context:NULL];
        sharedTeXDistControllerInstance = self;
        [self resetAndReturnError:NULL];
    }
    return self;
}
- (void)dealloc;
{
    [self removeObserver:self forKeyPath:@"_activeDistribution"];
    self.distributions = nil;
    self._activeDistribution = nil;
    [super dealloc];
    return;
}
- (void)reportError:(NSError *)ROR;
{
    if (ROR) {
        TeXDistLog(TeXDistLogLevelAlways,@"Reported ERROR: %@",ROR);
    }
}
- (BOOL)updateDistributionsAndReturnError:(NSError **)RORef;
{
    self.distributions = nil;
    NSMutableArray * distributions = [NSMutableArray array];
    NSURL * LibraryTeX = [TeXDistUtils distributionsURL];
    // what are the available libraries
    NSArray * paths = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:LibraryTeX.path error:RORef];
    for (NSString * fileName in paths) {
       if ([fileName.pathExtension isEqual:[TeXDistUtils texdistExtension]]) {
            NSURL * url = [NSURL URLWithString:[fileName stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] relativeToURL:LibraryTeX];
            TeXDistribution * TDB = [[[TeXDistribution alloc] initWithContentsOfURL:url andReturnError:RORef] autorelease];
            if (TDB.canBeActive) {
                [distributions addObject:TDB];
            } else {
                TeXDistLog(2,@"TeXDist Distribution IGNORED (No architectures): %@",TDB);\
            }
        }
    }
    self.distributions = distributions;
    if (self.distributions.count == 0) {
        TeXDist_RETURN_ERROR(kTeXDistNoDistributions,kTeXDistNoDistributionsMessage);
        return NO;
    }
    return YES;
}
- (BOOL)updateActiveDistributionAndReturnError:(NSError **)RORef;
{
    self._activeDistribution = nil;
    //  Now what about the texbin link
    //  We really need to update the texbin soft link in only one situation:
    //  if there is an ambiguity.
    //  But if the texbin points to something acceptable, no need to change anything.
    NSFileManager * DFM = [NSFileManager defaultManager];
    NSString * texbinDestination = [DFM destinationOfSymbolicLinkAtPath:[TeXDistUtils path_texbin] error:RORef];
	NSString * defaultTeXPath = [TeXDistUtils activeDistributionPath];// namely /Library/TeX/Distributions/.DefaultTeX/
	if (NOT [DFM fileExistsAtPath:defaultTeXPath]
        AND NOT[[TeXDistAuthentication sharedInstance] createDirectoryAtPath:defaultTeXPath withIntermediateDirectories:YES attributes:nil error:RORef]) {
        TeXDist_RETURN_ERROR(kTeXDistNoDefaultTeX,kTeXDistNoDefaultTeXMessage);
        return NO;
	}
	TeXDistLog(10,@"****  TeXDist INFO: defaultTeXPath is %@",defaultTeXPath);
	//PATH_MAX
	NSString * defaultTeXContents = [TeXDistUtils activeDistributionContentsPath];// namely /Library/TeX/Distributions/.DefaultTeX/Contents/
	NSString * defaultTeXContentsDestination = [DFM destinationOfSymbolicLinkAtPath:defaultTeXContents error:RORef];
    TeXDistribution * distribution = nil;
	NSString * targetTeXDistName = [defaultTeXPath stringByAppendingPathComponent:defaultTeXContentsDestination.stringByDeletingLastPathComponent].stringByStandardizingPath;
    targetTeXDistName = targetTeXDistName.stringByResolvingSymlinksInPath.lowercaseString;
    //  what about the active distribution ?
    //  find the destination of /Library/TeX/Distributions/.DefaultTeX/Contents
	for (distribution in self.distributions) {
        NSString * fileName = distribution.fileURL.path.stringByResolvingSymlinksInPath.lowercaseString;
        if ([fileName isEqual:targetTeXDistName]) {
            self._activeDistribution = distribution;
            break;
        }
    }
    if (NOT self._activeDistribution) {
        //  distribution is not setup either
        distribution = [self.distributions objectAtIndex:0];
        defaultTeXContentsDestination = distribution.softLinkDestination;
        TeXDistRunAlertPanel(@"WRONG ACTIVE DISTRIBUTION",@"ACTIVATING THE MOST RECENT TEX DISTRIBUTION AVAILABLE",nil);
        if (NOT [[TeXDistAuthentication sharedInstance]
                createSymbolicLinkAtPath:defaultTeXContents withDestinationPath:defaultTeXContentsDestination error:RORef]) {
            TeXDist_RETURN_ERROR(kTeXDistNoDefaultTeXContents,kTeXDistNoDefaultTeXContentsMessage);
            TeXDistRunAlertPanel(@"FAILURE", @"THE LAST OPERATION FAILED",nil);
            return NO;
        }
        targetTeXDistName = [defaultTeXPath stringByAppendingPathComponent:texbinDestination.stringByDeletingLastPathComponent].stringByStandardizingPath;
        targetTeXDistName = targetTeXDistName.stringByResolvingSymlinksInPath.lowercaseString;
        for (distribution in self.distributions) {
            NSString * fileName = distribution.fileURL.path.lowercaseString;
            if ([fileName isEqual:targetTeXDistName]) {
                self._activeDistribution = distribution;
                break;
            }
        }
        if (NOT self._activeDistribution) {
            TeXDist_RETURN_ERROR(kTeXDistNoActiveDistribution,kTeXDistNoActiveDistributionMessage);
            return NO;
        }
    }
    //  self._activeDistribution distribution is properly set up
    //  it is time now to set up the /usr/texbin link
    //  If /usr/texbin points to one of the architectures of the active distribution, nothing is done
    NSString * expectedDestination = [TeXDistUtils activeDistributionProgramsPath_texbin];
    if (NOT [texbinDestination isEqual:expectedDestination]) {
        TeXDistArchitecture * architecture = nil;
        if ((self._activeDistribution.architectures.count == 1)
                AND (architecture=[self._activeDistribution architectureWithDestinationPath:texbinDestination.stringByResolvingSymlinksInPath])) {
            self._activeDistribution.selectedArchitecture = self._activeDistribution.activeArchitecture = architecture;
            return YES;
        }
        TeXDistRunAlertPanel(@"WRONG CONFIGURATION",@"SETTING UP TEXBIN",nil);
        if (NOT[[TeXDistAuthentication sharedInstance]
                    createSymbolicLinkAtPath:[TeXDistUtils path_texbin] withDestinationPath:expectedDestination error:RORef]
                OR NOT [(texbinDestination = [DFM destinationOfSymbolicLinkAtPath:[TeXDistUtils path_texbin] error:RORef]) isEqual:expectedDestination]) {
            TeXDist_RETURN_ERROR(kTeXDistCannotUpdate_usr_texbin,kTeXDistCannotUpdate_usr_texbin_Message);
            TeXDistRunAlertPanel(@"FAILURE", @"THE LAST OPERATION FAILED",nil);
            self._activeDistribution = nil;
            return NO;
        }
    }
    //  texbinDestination == expectedDestination
    distribution = self._activeDistribution;
    TeXDistArchitecture * firstArchitecture = [distribution.architectures objectAtIndex:0];
    NSString * texbinPath = distribution.texbinPath;
    NSString * texbinPathDestination = [[NSFileManager defaultManager]
                              destinationOfSymbolicLinkAtPath:texbinPath error:RORef];
    if ((distribution.selectedArchitecture = distribution.activeArchitecture = [distribution architectureWithName:texbinPathDestination])
        OR (distribution.selectedArchitecture = distribution.activeArchitecture = [distribution architectureWithDestinationPath:texbinPath.stringByResolvingSymlinksInPath])) {
        return YES;
    }
    TeXDistLog(1,@"Active distribution %@",distribution.longDescription);
    TeXDistRunAlertPanel(@"WRONG ARCHITECTURE", @"UPDATING %@ TO USE A CORRECT ARCHITECTURE",distribution.displayName);
    texbinPathDestination = firstArchitecture.name;
    if ([[TeXDistAuthentication sharedInstance] createSymbolicLinkAtPath:texbinPath withDestinationPath:texbinPathDestination error:RORef]) {
        texbinPathDestination = [[NSFileManager defaultManager]
                       destinationOfSymbolicLinkAtPath:texbinPath error:RORef];
        if ((distribution.selectedArchitecture = distribution.activeArchitecture = [distribution architectureWithName:texbinPathDestination])
            OR (distribution.selectedArchitecture = distribution.activeArchitecture = [distribution architectureWithDestinationPath:texbinPath.stringByResolvingSymlinksInPath])) {
            return YES;
        }
    }
    TeXDistRunAlertPanel(@"FAILURE", @"THE LAST OPERATION FAILED",nil);
    self._activeDistribution = nil;
    return NO;
}
- (BOOL)resetAndReturnError:(NSError **)RORef;
{
    return [self updateDistributionsAndReturnError:RORef] AND [self updateActiveDistributionAndReturnError:RORef];
}
- (TeXDistribution *)mostRecentDistribution;
{
    return self.distributions.objectEnumerator.nextObject;
}
- (TeXDistribution *)activeDistribution;
{
    return self._activeDistribution;
}
- (TeXDistribution *)distributionWithName:(NSString *)aName;
{
    aName = aName.lowercaseString;
    for (TeXDistribution * TD in self.distributions) {
        if ([TD.displayName.lowercaseString isEqual:aName]) {
            return TD;
        }
    }
    return nil;
}
- (BOOL)activateDistribution:(TeXDistribution *)newDistribution userInterface:(BOOL)yorn error:(NSError **)RORef;
{
	NSParameterAssert([newDistribution isKindOfClass:[TeXDistribution class]]);
    if ([self._activeDistribution isEqual:newDistribution]) {
        return YES;
    }
    BOOL answer = NO;
    if (newDistribution.selectedArchitecture) {
        [newDistribution willChangeValueForKey:@"isActive"];
        TeXDistribution * oldDistribution = self._activeDistribution;
        [oldDistribution willChangeValueForKey:@"isActive"];
        answer = [self activateDistribution:newDistribution architecture:newDistribution.selectedArchitecture userInterface:yorn error:RORef]
                AND [self updateActiveDistributionAndReturnError:RORef];
        [oldDistribution didChangeValueForKey:@"isActive"];
        [newDistribution didChangeValueForKey:@"isActive"];
    }
	return answer;
}
- (void)setActiveDistribution:(TeXDistribution *)newDistribution;
{
    if (NOT [self._activeDistribution isEqual:newDistribution]) {
        NSError * ROR = nil;
        if (NOT [self activateDistribution:newDistribution userInterface:NO error:&ROR]) {
            if ([ROR.domain isEqual:TeXDistDomain] AND (ROR.code == kTeXDistTooManyCredentials)) {
                TeXDistRunAlertPanel(@"CREDENTIALS",@"NO MORE CREDENTIALS REQUEST",NULL);
            } else {
                [self reportError:ROR];
            }
        }
    }
	return;
}
- (BOOL)activateDistribution:(TeXDistribution *)aDistribution architecture:(TeXDistArchitecture *)anArchitecture userInterface:(BOOL) yorn error:(NSError **)RORef;
{
	// sanity
	NSParameterAssert([aDistribution isKindOfClass:[TeXDistribution class]]);
    NSParameterAssert([anArchitecture isKindOfClass:[TeXDistArchitecture class]]
                      AND [aDistribution.architectures containsObject:anArchitecture]);
    NSString * path = nil;
    NSString * destPath = nil;
    if (NOT [aDistribution isEqual:self._activeDistribution]) {
        path = [TeXDistUtils activeDistributionContentsPath];
        destPath = aDistribution.softLinkDestination;
        if (NOT [[TeXDistAuthentication sharedInstance] createSymbolicLinkAtPath:path withDestinationPath:destPath error:RORef]) {
            TeXDist_RETURN_ERROR(1,@"createSymbolicLinkAtPath... FAILED");
            return NO;
        }
        if (NOT [self updateActiveDistributionAndReturnError:RORef] OR NOT [self._activeDistribution isEqual:aDistribution]) {
            return NO;
        }
    }
    //  self._activeDistribution distribution is properly set up
    //  it is time now to set up the /usr/texbin link
    //  If /usr/texbin points to one of the architectures of the active distribution, nothing is done
    NSFileManager * DFM = [NSFileManager defaultManager];
    NSString * texbinDestination = [DFM destinationOfSymbolicLinkAtPath:[TeXDistUtils path_texbin] error:RORef];
    NSString * expectedDestination = [TeXDistUtils activeDistributionProgramsPath_texbin];
    if (NOT [texbinDestination isEqual:expectedDestination]) {
        if ((self._activeDistribution.architectures.count == 1)
                AND [self._activeDistribution architectureWithDestinationPath:texbinDestination.stringByResolvingSymlinksInPath]) {
            return YES;
        }
//        TeXDistRunAlertPanel(@"WRONG DISTRIBUTION", @"ACTIVATING THE MOST RECENT TEX DISTRIBUTION AVAILABLE",nil);
        if (NOT[[TeXDistAuthentication sharedInstance]
                    createSymbolicLinkAtPath:[TeXDistUtils path_texbin] withDestinationPath:expectedDestination error:RORef]
                OR NOT [(texbinDestination = [DFM destinationOfSymbolicLinkAtPath:[TeXDistUtils path_texbin] error:RORef]) isEqual:expectedDestination]) {
            TeXDist_RETURN_ERROR(kTeXDistCannotUpdate_usr_texbin,kTeXDistCannotUpdate_usr_texbin_Message);
            return NO;
        }
    }
    // aDistribution is active
    if ([aDistribution.activeArchitecture isEqual:anArchitecture]) {
        return YES;
    }
    path = texbinDestination;
    destPath = anArchitecture.name;
    if (yorn) {
        TeXDistRunAlertPanel(@"WRONG ARCHITECTURE",@"UPDATING %@ TO USE A CORRECT ARCHITECTURE",aDistribution.displayName);
    }
    if ([[TeXDistAuthentication sharedInstance] createSymbolicLinkAtPath:path withDestinationPath:destPath error:RORef]
        AND [aDistribution updateActiveArchitectureAndReturnError:RORef]
            AND [aDistribution.activeArchitecture isEqual:anArchitecture]) {
        return YES;
    }
    TeXDist_RETURN_ERROR(kTeXDistNoDefaultTeXContents,kTeXDistNoDefaultTeXContentsMessage);
    if (yorn) {
        TeXDistRunAlertPanel(@"FAILURE", @"THE LAST OPERATION FAILED",nil);
    }
    return NO;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
{
    if (self == object AND [keyPath isEqual:@"_activeDistribution"]) {
        TeXDistribution * old = [change objectForKey:NSKeyValueChangeOldKey];
        TeXDistribution * new = [change objectForKey:NSKeyValueChangeNewKey];
        if ([old isKindOfClass:[TeXDistribution class]]) {
            [old removeObserver:self forKeyPath:@"selectedArchitecture"];
        }
        if ([new isKindOfClass:[TeXDistribution class]]) {
            [new addObserver:self forKeyPath:@"selectedArchitecture" options:(NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew) context:NULL];
        }
        return;
    }
    if ([self.activeDistribution isEqual:object] AND [keyPath isEqual:@"selectedArchitecture"]) {
        TeXDistArchitecture * newArchitecture = [change objectForKey:NSKeyValueChangeNewKey];
        if ([newArchitecture isKindOfClass:[TeXDistArchitecture class]]
            AND [self.activeDistribution.architectures containsObject:newArchitecture]) {
            if (NOT [self.activeDistribution.activeArchitecture isEqual:newArchitecture]) {
                NSError * ROR = nil;
                if ([self activateDistribution:self.activeDistribution architecture:newArchitecture userInterface:NO error:&ROR]) {
                    [(TeXDistribution *)self.activeDistribution updateActiveArchitectureAndReturnError:NULL];
                } else if ([ROR.domain isEqual:TeXDistDomain] AND (ROR.code == kTeXDistTooManyCredentials)) {
                    TeXDistRunAlertPanel(@"CREDENTIALS",@"NO MORE CREDENTIALS REQUEST",NULL);
                }
                if (NOT [self.activeDistribution.selectedArchitecture isEqual:self.activeDistribution.activeArchitecture]) {
                    self.activeDistribution.selectedArchitecture = self.activeDistribution.activeArchitecture;
                }
            }
        }
        return;
    }
    [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
- (NSArray *)sortDescriptors;
{
    return [NSArray arrayWithObjects:
				[[[NSSortDescriptor alloc] initWithKey:@"modificationDate" ascending:NO] autorelease],
				[[[NSSortDescriptor alloc] initWithKey:@"displayName.lowercaseString" ascending:NO] autorelease],
					nil];
}
- (void)listActiveDistribution;
{
    NSError * ROR = nil;
    if ((self.distributions.count OR [self updateDistributionsAndReturnError:&ROR])
            AND [self updateActiveDistributionAndReturnError:&ROR]) {
        NSMutableArray * MRA = [NSMutableArray array];
        [MRA addObject:@"TeXDist SUMMARY: active distribution."];
        TeXDistribution * TDD = self.activeDistribution;
        [MRA addObject:@"================================================================"];
        [MRA addObject:TDD.longDescription];
        [MRA addObject:[NSString stringWithFormat:@"TeXDist list of all the architectures available in %@.", TDD]];
        for (TeXDistArchitecture * TDA in TDD.architectures) {
            [MRA addObject:@"----------------------------------------------------------------"];
            [MRA addObject:TDA.longDescription];
        }
        NSLog(@"%@",[MRA componentsJoinedByString:@"\n"]);
        return;
    }
    NSLog(@"ERROR: %@",ROR);
    return;
}
- (void)listDistributions;
{
    NSError * ROR = nil;
    if (self.distributions.count OR [self updateDistributionsAndReturnError:&ROR]) {
        NSMutableArray * MRA = [NSMutableArray array];
        [MRA addObject:@"TeXDist SUMMARY: list of all the distributions available."];
        for (TeXDistribution * TDD in self.distributions) {
            [MRA addObject:@"================================================================"];
            [MRA addObject:TDD.longDescription];
            [MRA addObject:[NSString stringWithFormat:@"TeXDist list of all the architectures available in %@.", TDD]];
            for (TeXDistArchitecture * TDA in TDD.architectures) {
                [MRA addObject:@"----------------------------------------------------------------"];
                [MRA addObject:TDA.longDescription];
            }
        }
        NSLog(@"%@",[MRA componentsJoinedByString:@"\n"]);
        return;
    }
    NSLog(@"ERROR: %@",ROR);
}
- (void)reset;
{
    NSError * ROR = nil;
    if ([[TeXDistAuthentication sharedInstance] removeItemAtPath:[TeXDistUtils path_texbin] error:&ROR]
            AND [[TeXDistAuthentication sharedInstance] removeItemAtPath:[TeXDistUtils activeDistributionPath] error:&ROR]) {
        return;
    }
    NSLog(@"ERROR: %@",ROR);
}
@end
