

import java.io.File;
import java.util.Enumeration;
import com.denova.io.FileSystem;
import com.denova.io.Log;
import com.denova.JExpress.Installer.CustomUninstaller;
import com.denova.JExpress.Installer.InstallPropertyNames;
import com.denova.JExpress.Installer.StatusPanel;
import com.denova.runtime.Exec;
import com.denova.runtime.MacOS;
import com.denova.runtime.UnixCommands;
import com.denova.runtime.UnixMenu;
import com.denova.runtime.WindowsRegistry;
import com.denova.util.PropertyList;



/**
  * Autostart menu item.
  *
  * <p>
  * Important:
  * <br>
  *  1. This class must be included in the "After install" field 
  *     or one of the later fields on the  Custom panel in JExpress Developer.
  *  2. Change the value of the StartMenu constant.
  * <p>
  * There is no user interaction in this class.
 **/
public class AutoStartMenu extends StatusPanel
                         implements InstallPropertyNames
{
    // change to fit your configuration
    private static final String StartMenu = "Start program";
    
    private static final String WindowsNtRegKey = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Explorer\\Shell Folders";
    private static final String WindowsRegKey = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders";

    private static final String MacAutoStartDirname = "Library/Preferences";
    private static final String MacAutoStartFilename = "loginwindow.plist";
    
    
    /**
     *Constructor for the AutoStartMenu object
     *
     * @param  properties
     */
    public AutoStartMenu(PropertyList properties)
    {
        // initialize WizardPanel superclass
        super(properties);

        String configuringMessage = "Configuring";
        String statusMessage = " auto startup...";

        setLabels(configuringMessage, statusMessage, "");
    }


    /** 
     * Enter the panel. 
     *
     * Do not call this method; only the wizard should call it.
     */
    public void enter()
    {
        try {

            configureAutoStart();
        }
        
        catch (Exception e) {

            log(e);
        }

        // we don't want any user interaction
        // so move them to the next panel automatically
        showNextPanel();
    }


    // don't let the user bypass this command before we're done
    /**
     *  Gets the next button enabled.
     *
     * @return    next button enabled
     */
    public boolean isNextButtonEnabled()
    {
        return false;
    }

    // don't let the user back up before we're done
    /**
     *  Gets the previous button enabled.
     *
     * @return    previous button enabled
     */
    public boolean isPreviousButtonEnabled()
    {
        return false;
    }

    /**
      * Supplies the name of the panel.
      *
      * @return the classname.
      */

    public String getName () {
        return "AutoStartMenu";
    }

    
    private void configureAutoStart()
        throws Exception
    {
        log("configuring auto startup");

        boolean done = false;
        String submenu = getPropertyList().getProperty(Submenu, "");
        PropertyList menus = getPropertyList().getPropertyListProperty(MenusAttributeName);
        Enumeration menusEnum = menus.propertyNames();
        
        // go through the list of menu items until we find the Start menu
        while (!done &&
               menusEnum.hasMoreElements())
        {
            String name = (String) menusEnum.nextElement();
            if (name.equals(StartMenu))
            {
                String propsString = menus.getProperty(name);
                PropertyList props = new PropertyList();
                props.fromString(propsString);

                setupAutoStartup(submenu, props);
                
                done = true;
            }
        }
    }


    private void setupAutoStartup(String submenu,
                                  PropertyList menuProperties)
    {
        String applicationDirectory = getPropertyList().getProperty(ApplicationDirectory, ".");
        
        if (isWindows())
        {
            
            autoStartWindows(submenu, menuProperties, applicationDirectory);
        }
        
        else if (isMacOsX())
        {
            autoStartMac(menuProperties, applicationDirectory);
        }
        
        else if (isUnix())
        {
            autoStartUnix(submenu, menuProperties, applicationDirectory);
        }
        
        else {
            
            log("unable to configure auto start on this OS");
        }
    }


    private void autoStartUnix(String submenu,
                               PropertyList menuProperties,
                               String applicationDirectory)
    {
        log("configuring unix");
                
        UnixMenu unixMenu = new UnixMenu();
        String kdeMenuDirName = UnixMenu.getKdeMenuDir();
        String kdeAutoStartDirName = unixMenu.getKdeAutostartDir();
        
        if (kdeMenuDirName != null &&
            kdeMenuDirName.length() > 0 &&
            kdeAutoStartDirName != null &&
            kdeAutoStartDirName.length() > 0)
        {
            String longName = menuProperties.getProperty(MenuLongName);
    
            setupKdeAutoStart(kdeMenuDirName, 
                              kdeAutoStartDirName,
                              submenu,
                              longName,
                              applicationDirectory);
        }
        
        else {
            
            log("unable to configure the auto startup");
        }
    }
    
    
    private void setupKdeAutoStart(String kdeMenuDirName, 
                                   String kdeAutoStartDirName,
                                   String submenu,
                                   String longName,
                                   String applicationDirectory)
    {
        File fromDir = new File(kdeMenuDirName, submenu);
        File fromFile = new File(fromDir, longName + UnixMenu.MenuExtension);
        String fromFilename = fromFile.getPath();

        // the file must have the desktop extension to work as a link       
        File toFile = new File(kdeAutoStartDirName, "Autostart " + longName + UnixMenu.MenuExtension);
        String toFilename = toFile.getPath();
        
        // make sure the destination file doesn't already exist
        toFile. delete();
            
        log("setting up kde autostart");
        
        // just create a link to the file
        UnixCommands.ln(fromFilename, toFilename);
        UnixCommands.chmod("0744", toFilename);
        log("linked " + fromFilename + " to " + toFilename);
        
        configUninstaller(toFilename, applicationDirectory);
    }
    
    
    private void autoStartWindows(String submenu,
                                  PropertyList menuProperties,
                                  String applicationDirectory)
    {
        log("configuring windows");
        
        String fromDir = getWindowsMenuDir();
        String toDir = getWindowsStartupDir();

        if (stringHasContent(fromDir) &&
            stringHasContent(toDir))
        {
            String longName = menuProperties.getProperty(MenuLongName);
            String fromFilename = fromDir + submenu + File.separator + longName + ".LNK";
            String toFilename = toDir + "Autostart " + longName + ".LNK";
            
            copyFile(fromFilename, toFilename, applicationDirectory);

            configUninstaller(toFilename, applicationDirectory);
        }
    }


    private String getWindowsMenuDir()
    {
        String menusDir = WindowsRegistry.getData(WindowsRegKey, "Programs");
        
        if (menusDir == null ||
            menusDir.length() <= 0) {
                
            menusDir = WindowsRegistry.getData(WindowsNtRegKey, "Programs");
        }

        // add the trailing back slash
        if (menusDir != null &&
            !menusDir.endsWith(File.separator)) {
                
            menusDir += File.separator;
        }

        if (menusDir != null) {
            
            log("menus dir " + menusDir);
        }

        return menusDir;
    }


    /**
     *  Gets the startup dir.
     *
     * @return    startup dir
     */
    private String getWindowsStartupDir()
    {
        String startupDir = WindowsRegistry.getData(WindowsRegKey, "Startup");

        if (startupDir == null ||
            startupDir.length() <= 0)
        {
            
            startupDir = WindowsRegistry.getData(WindowsNtRegKey, "Startup");
        }
        
        if (startupDir == null ||
            startupDir.length() <= 0)
        {
                
            startupDir = WindowsRegistry.getData(WindowsRegKey, "Common Startup");
        }

        // add the trailing back slash
        if (startupDir != null &&
            !startupDir.endsWith(File.separator))
        {
            startupDir += File.separator;
        }

        if (startupDir != null) {
            log("startup dir " + startupDir);
        }

        return startupDir;
    }


    private void autoStartMac(PropertyList menuProperties,
                               String applicationDirectory)
    {
        log("configuring mac");
                
        String shortName = menuProperties.getProperty(MenuShortName);
        String scriptFilename = shortName + MacOS.LaunchScriptExtension;
        String autoStartFilename = "autostart." + shortName + MacOS.LaunchScriptExtension;
    
        setupMacAutoStart(scriptFilename, autoStartFilename, applicationDirectory);
    }
    
    
    private void setupMacAutoStart(String scriptFilename,
                                   String autoStartFilename,
                                   String applicationDirectory)
    {
        String startCommand = applicationDirectory + File.separator + scriptFilename;
        String autoStartCommand = applicationDirectory + File.separator + autoStartFilename;        
        String autoStartVariable = "$autostartCommand";
        String dictCommand = "'<dict><key>Hide</key><true/><key>Path</key><string>" + autoStartVariable + "</string></dict>'";
        
        try {
            
            int i = dictCommand. indexOf(autoStartVariable);
            if (i != -1)
            {
                backupPlistFile();        
                
                dictCommand = 
                 dictCommand.substring(0, i) + autoStartCommand + dictCommand.substring(i + autoStartVariable.length());                 
                String homeDir = System.getProperty("user.home", ".");                
                String command = 
                 "defaults write loginwindow AutoLaunchedApplicationDictionary -array-add " + dictCommand;
                
                Exec.runCommand(command, homeDir);
                log("issued command: " + command);
                
                reconfigMacAutoStart(startCommand,
                                     autoStartCommand,
                                     applicationDirectory);
            }
        }
        
        catch (Throwable t) {
            log(t);
        }
    }
    
    
    private void reconfigMacAutoStart(String startCommand,
                                      String autoStartCommand,
                                      String applicationDirectory)
    {
        File startFile = new File(startCommand);
        File autoStartFile = new File(autoStartCommand);

        if (startFile.exists() &&
            !autoStartFile.exists())
        {
            // Mac's too smart and won't accept
            // a link file to autostart; instead,
            // it changes the link to the actual file
            // so we'll adjust things so the command
            // has a more unique name
            startFile.renameTo(autoStartFile);
            
            UnixCommands.ln(autoStartCommand, startCommand);
            UnixCommands.chmod("0744", startCommand);
            log("linked " + autoStartCommand + " to " + startCommand);
            
            configUninstaller(autoStartCommand, applicationDirectory);
        }
    }
    

    private void backupPlistFile()
    {
        try {
            File plistFile = new File(getPlistFilename());
            File backupFile = new File(getPlistFilename() + ".backup");
            backupFile. delete();
                
            FileSystem.copyFile(plistFile, backupFile);
            log("backuped plist to: " + backupFile.getPath());
        }
        
        catch (Throwable e) {
            log(e);
        }
    }
    
    
    private String getPlistFilename()
    {
        String configFilename = null;
        
        String homeDir = System.getProperty("user.home", ".");
        File configDir = new File(homeDir, MacAutoStartDirname);
        File configFile = new File(configDir, MacAutoStartFilename);
        
        if (configFile.exists()) {
            configFilename = configFile.getPath();
        }

        else {

            log("unable to find: " + configFile.getPath());
        }

        return configFilename;
    }


    private static boolean stringHasContent(String s)
    {
        return s != null &&
            s.length() > 0;
    }
    
    
    private boolean copyFile(String fromFilename,
                             String toFilename,
                             String applicationDirectory)
    {
        boolean ok = false;
          
        // make sure the file doesn't already exist
        File toFile = new File(toFilename);
        toFile. delete();
            
        try {
            // copy the link file from the menu directory to the desktop directory
            com.denova.io.FileSystem.copyFile(fromFilename, toFilename);
            log("copied " + fromFilename + " to " + toFilename);
            
            ok = true;
        }
        catch (Exception e) {
            log(e);
        }
        
        return ok;
    }


    private void configUninstaller(String fullFilename,
                                   String applicationDirectory)
    {
        // delete the file during the uninstall
        CustomUninstaller customUninstaller = new CustomUninstaller();
        customUninstaller.deleteFile(fullFilename);
        customUninstaller.append(applicationDirectory);
        log("setup to delete file during uninstall: " + fullFilename);
    }

    private void log ( String s ) {
        File f = new File ( LogFilename );
        if ( f. exists () ) {
            startLog ();
            autostartLog. write ( s );
      }
    }

    private void log ( Throwable t ) {
        File f = new File ( LogFilename );
        if ( f. exists () ) {
            startLog ();
            autostartLog. write ( t );
      }
    }

    private void startLog () {
        if ( autostartLog == null ) {
            autostartLog = new Log ( LogFilename );
            autostartLog. setLogging ( true );
        }
    }

    private void stopLogging () {
        if ( autostartLog != null ) {
            autostartLog. stopLogging ();
        }
    }

    Log autostartLog = null;
    static final String LogFilename = "autostart";
    
} // AutoStartMenu

