package com.limegroup.gnutella.util;

import java.util.Enumeration;

import com.apple.cocoa.foundation.NSMutableArray;
import com.apple.cocoa.foundation.NSMutableDictionary;
import com.apple.cocoa.foundation.NSObject;
import com.apple.cocoa.foundation.NSUserDefaults;
import com.apple.cocoa.foundation.NSSystem;

/**
 * A collection of utility methods for OSX.
 * These methods should only be called if run from OSX,
 * otherwise ClassNotFoundErrors may occur.
 *
 * To determine if the Cocoa Foundation classes are present,
 * use the method CommonUtils.isCocoaFoundationAvailable().
 */
public class MacOSXUtils {
    private MacOSXUtils() {}
    
    /**
     * The login domain.
     */
    private static final String LOGIN_DOMAIN = "loginwindow";
    
    /**
     * The name of the dictionary which contains the apps that are launched.
     */
    private static final String DICTIONARY =
        "AutoLaunchedApplicationDictionary";
    
    /**
     * The key for "hide"
     */
    private static final String HIDE = "Hide";
    
    /**
     * The key for "path";
     */
    private static final String PATH = "Path";
    
    /**
     * The name of the app that launches.
     */
    private static final String APP_NAME = "LimeWire At Login.app";

    /**
     * Creates a mutable clone of the specified object.
     */
    private static Object createMutableClone(Object a)
      throws CloneNotSupportedException {
        if(a == null)
            throw new CloneNotSupportedException();
        else if (!(a instanceof NSObject))
            throw new CloneNotSupportedException();
        else
            return ((NSObject)a).mutableClone();
    }

    /**
     * Modifies the loginwindow.plist file to either include or exclude
     * starting up LimeWire.
     */
    public static void setLoginStatus(boolean allow) {
        Object temp; // Used for the assignment when retrieiving as an object.
        NSUserDefaults defaults = NSUserDefaults.standardUserDefaults();

        temp = defaults.persistentDomainForName(LOGIN_DOMAIN);
        // If no domain existed, create one and make its initial dictionary.
        if(temp == null) {
            temp = new NSMutableDictionary();
            ((NSMutableDictionary)temp).setObjectForKey(new NSMutableArray(),
                                        DICTIONARY);
        } else if(!(temp instanceof NSMutableDictionary))
            return; // nothing we can do.

        NSMutableDictionary logins = null;
        try {
            logins = (NSMutableDictionary)createMutableClone(temp);
        } catch(CloneNotSupportedException cnso) {
            //nothing we can do, exit.
            return;
        }
        
        // Retrieve the array that contains the various dictionaries
        temp = logins.objectForKey(DICTIONARY);
        // If no object existed, create one.
        if(temp == null)
            temp = new NSMutableArray();
        // if it is not an NSMutableArray, exit (we dunno how to recover)
        else if(!(temp instanceof NSMutableArray))
            return;

        NSMutableArray allApps = null;
        try {
            allApps = (NSMutableArray)createMutableClone(temp);
        } catch(CloneNotSupportedException cnso) {
            // nothing we can do, exit.
            return;
        }
        
        Enumeration items = allApps.objectEnumerator();
        boolean found = false;
        // Iterate through each item in the array,
        // looking for one whose 'Path' key has a value
        // containing the stub startup app LimeWire uses.
        while(items.hasMoreElements()) {
            temp = items.nextElement();
            // If it isn't an NSMutableDictionary, ignore it
            // because we won't know how to handle it.
            if(!(temp instanceof NSMutableDictionary))
                continue;

            NSMutableDictionary itemInfo = null;
            try {
                itemInfo = (NSMutableDictionary)createMutableClone(temp);
            } catch(CloneNotSupportedException cnse) {
                continue; // can't do anything, continue.
            }
            
            Object path = itemInfo.objectForKey(PATH);
            // If it isn't a String, ignore it because we
            // won't know how to handle it.
            if(!(path instanceof String))
                continue;
            String itemPath = (String)path;
            if(itemPath.indexOf(APP_NAME) != -1) {
                found = true;
                // If not allowed, remove.
                // We must remove with temp, not itemInfo, because itemInfo
                // is a clone.
                if(!allow)
                    allApps.removeIdenticalObject(temp);
                else
                    itemInfo.setObjectForKey(getAppDir(), PATH);
                break;
            }
        }
        
        if(!found) {
            if(!allow)
                return;
            else {
                NSMutableDictionary newItem = new NSMutableDictionary();
                newItem.setObjectForKey(new Integer(0), HIDE);
                newItem.setObjectForKey(getAppDir(), PATH);
                allApps.addObject(newItem);
            }
        }
        
        //Make sure we set add our new allApps to the dictionary.
        logins.setObjectForKey(allApps, DICTIONARY);
        
        // Set the new domain.
        defaults.setPersistentDomainForName(logins, LOGIN_DOMAIN);
        // Synchronize it to disk immediately
        defaults.synchronize();
    }
    
    /**
     * Gets the full user's name.
     */
    public static String getUserName() {
        return NSSystem.currentFullUserName();
    }
    
    /**
     * Retrieves the app directory & name.
     * If the user is not running from the bundled app as we named it,
     * defaults to /Applications/LimeWire/ as the directory of the app.
     */
    private static String getAppDir() {
        String appDir = "/Applications/LimeWire/";
        String path = CommonUtils.getCurrentDirectory().getPath();
        int app = path.indexOf("LimeWire.app");
        if(app != -1)
            appDir = path.substring(0, app);
        return appDir + APP_NAME;
    }
}