/* Copyright (C) 2005-2011 Fabio Riccardi */

package com.lightcrafts.platform;

import com.lightcrafts.utils.ColorProfileInfo;
import com.lightcrafts.utils.Version;
import com.lightcrafts.utils.directory.DirectoryMonitor;
import com.lightcrafts.utils.directory.UnixDirectoryMonitor;

import javax.swing.*;
import javax.swing.filechooser.FileSystemView;
import java.awt.color.ICC_Profile;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.util.Collection;
import java.util.Collections;

/**
 * This interface defines all platform-specific or platform-customized
 * features.
 * <p>
 * Instances are only available through the static initializer that picks an
 * instance based on the operating system.
 */
@SuppressWarnings({"InnerClassFieldHidesOuterClassField"})
public class Platform {

    /**
     * A <code>Type</code> encodes the platform type.
     */
    public enum Type {
        Linux  ( "com.lightcrafts.platform.linux.LinuxPlatform" ),
        MacOSX ( "com.lightcrafts.platform.macosx.MacOSXPlatform" ),
        Windows( "com.lightcrafts.platform.windows.WindowsPlatform" ),
        Other  ( "com.lightcrafts.platform.Platform" );

        /**
         * Gets the fully qualified name of the class that extends
         * {@link Platform} for the platform for this type.
         *
         * @return Returns said class name.
         */
        String getPlatformImplementationClassName() {
            return m_implementationClassName;
        }

        /**
         * Gets the <code>Type</code> for the currently running operating
         * system.
         *
         * @return Returns said <code>Type</code>.
         */
        static Type getTypeForOS() {
            final String osName = System.getProperty( "os.name" ).toLowerCase();
            if ( osName.startsWith( "linux" ) )
                return Linux;
            if ( osName.equals( "mac os x" ) )
                return MacOSX;
            if ( osName.startsWith( "windows" ) )
                return Windows;
            return Other;
        }

        ////////// private ////////////////////////////////////////////////////

        /**
         * Constructs a <code>Type</code>.
         *
         * @param implementationClassName The fully qualified name of the class
         * that extends {@link Platform} for the new type.
         */
        private Type( String implementationClassName ) {
            m_implementationClassName = implementationClassName;
        }

        private final String m_implementationClassName;
    }

    //
    // These are declared for backwards compatibility.
    //
    public static final Type Linux   = Type.Linux;
    public static final Type MacOSX  = Type.MacOSX;
    public static final Type Windows = Type.Windows;
    public static final Type Other   = Type.Other;

    /**
     * Bring the given application to the front.
     *
     * @param appName The name of the application.
     */
    public void bringAppToFront( String appName ) {
        // do nothing by default
    }

    /**
     * Creates a new {@link AlertDialog}.
     *
     * @return Returns a new {@link AlertDialog}.
     * @noinspection MethodMayBeStatic
     */
    public AlertDialog getAlertDialog() {
        return DefaultAlertDialog.INSTANCE;
    }

    /**
     * Gets a directory where images are likely to be found.
     *
     * @return Returns said directory.
     */
    public File getDefaultImageDirectory() {
        final String home = System.getProperty( "user.home" );
        return new File( home, Version.getApplicationName() );
    }

    /**
     * Gets the display name for the given {@link File}.
     *
     * @param file The {@link File} to get the display name for.
     * @return Returns said name.
     */
    public String getDisplayNameOf( File file ) {
        return getFileSystemView().getSystemDisplayName( file );
    }

    /**
     * Gets the {@link FileChooser} for the current <code>Platform</code>.
     *
     * @return Returns said {@link FileChooser}.
     */
    public FileChooser getFileChooser() {
        return new DefaultFileChooser();
    }

    /**
     * Gets the directory where easily-accessible documents go.
     *
     * @return Returns said directory.
     */
    public File getLightZoneDocumentsDirectory() {
        final String home = System.getProperty( "user.home" );
        return new File( home, Version.getApplicationName() );
    }

    /**
     * Returns the Swing pluggable look-and-feel class name suitable for
     * setting on the UIManager.
     */
    public LookAndFeel getLookAndFeel() {
        return UIManager.getLookAndFeel();
    }

    /**
     * Gets a new {@link DirectoryMonitor}.
     *
     * @return Returns said {@link DirectoryMonitor}.
     */
    @SuppressWarnings({"MethodMayBeStatic"})
    public DirectoryMonitor getDirectoryMonitor() {
        return new UnixDirectoryMonitor();
    }

    /**
     * Get the ICC_Profile to use for rendering to the screen on this platform.
     */
    public ICC_Profile getDisplayProfile() {
        return null;
    }

    /**
     * Gets a Collection&gt;ColorProfileInfo&lt; objects that are suitable for
     * exporting images to files.
     */
    public Collection<ColorProfileInfo> getExportProfiles() {
        return Collections.emptySet();
    }

    /**
     * Get the {@link FileSystemView} for this platform.
     *
     * @return Returns said {@link FileSystemView}.
     * @noinspection MethodMayBeStatic
     */
    public FileSystemView getFileSystemView() {
        return FileSystemView.getFileSystemView();
    }

    /**
     * Gets the amount of physical memory installed in the computer.
     *
     * @return Returns the amount of memory in megabytes.
     */
    public int getPhysicalMemoryInMB() {
        return 0;
    }

    /**
     * Gets the path components to the platform's &quot;Pictures&quot; folder.
     *
     * @return Returns an array of the names of the path components starting
     * from the root folder shown in the folder tree (not the root of the
     * filesystem).
     */
    public String[] getPathComponentsToPicturesFolder() {
        return new String[]{ Version.getApplicationName() };
    }

    /**
     * Gets the current <code>Platform</code>.
     *
     * @return Returns said <code>Platform</code>.
     */
    public static Platform getPlatform() {
        return m_platform;
    }

    /**
     * Get a Collection of ColorProfileInfo objects that are suitable for
     * sending images to printers.
     */
    public Collection<ColorProfileInfo> getPrinterProfiles() {
        return Collections.emptySet();
    }

    /**
     * Creates a new {@link ProgressDialog}.
     *
     * @return Returns a new {@link ProgressDialog}.
     * @noinspection MethodMayBeStatic
     */
    public ProgressDialog getProgressDialog() {
        return new DefaultProgressDialog();
    }

    /**
     * Gets the type of the current platform.
     *
     * @return Returns said type.
     */
    public static Type getType() {
        return m_type;
    }

    /**
     * Checks whether this computer has an active Internet connection.
     *
     * @param hostName The fully qualified name of the desired host to connect
     * to.
     * @return Returns <code>true</code> only if this computer currently has
     * an active internet connection and thus can reach the specified host.
     */
    @SuppressWarnings({"ResultOfMethodCallIgnored"})
    public boolean hasInternetConnectionTo( String hostName ) {
        try {
            InetAddress.getByName( hostName );
            return true;
        }
        catch (Throwable t) {
            return false;
        }
    }

    /**
     * Make a given file hidden to the user in the OS's file browser.
     *
     * @param file The {@link File} to hide.
     */
    @SuppressWarnings( { "UnusedDeclaration" } )
    public void hideFile( File file ) throws IOException {
        // do nothing by default
    }

    /**
     * Detect whether the specified key is currently pressed.  The purpose of
     * a platform-specific implementation of this method is to distinguish
     * the synthetic key events generated by auto-repeat.  This default
     * implementation just throws UnsupportedOperationException.
     */
    @SuppressWarnings( { "UnusedDeclaration", "MethodMayBeStatic" } )
    public boolean isKeyPressed( int keyCode )
        throws UnsupportedOperationException
    {
        throw new UnsupportedOperationException(
            "The current Platform does not implement isKeyPressed()"
        );
    }

    /**
     * Checks whether the given {@link File} is special in some way on the
     * platform.
     *
     * @param file The {@link File} to check.
     * @return If the file is special, returns a platform-specific instance of
     * some class derived from {@link File} that implements some special
     * behavior; otherwise returns the passed-in file.
     */
    public File isSpecialFile( File file ) {
        return file;
    }

    /**
     * Loads native libraries for the current <code>Platform</code>.
     */
    public void loadLibraries() throws UnsatisfiedLinkError {
        // do nothing by default
    }

    /**
     * If this Platform supports Java Dialog ModalityTypes, then set the
     * given dialog's ModalityType to DOCUMENT_MODAL.  Otherwise just call
     * Dialog.setModal(true).
     * <p>
     * The "document" modality for dialogs us useful because it allows help
     * windows to be used alongside the modal dialogs they describe.
     */
    @SuppressWarnings( { "MethodMayBeStatic" } )
    public void makeModal(Dialog dialog) {
        dialog.setModal(true);
    }

    /**
     * Move a set of files to the Trash.
     *
     * @param pathNames An array of full paths of files to be moved to the
     * Trash.
     * @return Returns <code>true</code> only if all the file were moved.
     */
    public boolean moveFilesToTrash( String[] pathNames ) {
        for ( String pathName : pathNames ) {
            final File file = new File( pathName );
            if ( !file.delete() )
                return false;
        }
        return true;
    }

    /**
     * Assert that we are now ready to handle opening image files.
     */
    public void readyToOpenFiles() {
        // do nothing
    }

    /**
     * Resolve an alias file.
     *
     * @param file The {@link File} to resolve.
     * @return Returns the resolved path (or the original path if it didn't
     * refer to an alias) or <code>null</code> if there was an error.
     */
    public String resolveAliasFile( File file ) {
        return file.getAbsolutePath();
    }

    /**
     * Tells the OS's file browser to show the folder the given file is in.
     *
     * @param path The full path of the file to show.
     * @return Returns <code>true</code> only if the file was shown
     * successfully.
     */
    public boolean showFileInFolder( String path ) {
        return false;
    }

    /**
     * Show the application help for a specific topic.
     *
     * @param topic The topic to show or <code>null</code> to show the cover
     * page.
     */
    public void showHelpTopic( String topic ) {
        // do nothing
    }

    private PrinterLayer printerLayer = new DefaultPrinterLayer();

    public PrinterLayer getPrinterLayer() {
        return printerLayer;
    }

    ////////// private ////////////////////////////////////////////////////////

    /**
     * The current <code>Platform</code>.
     */
    private static Platform m_platform;

    /**
     * The type of the current platform.
     */
    private static final Type m_type;

    static {
        m_type = Type.getTypeForOS();
        final String className = m_type.getPlatformImplementationClassName();
        try {
            m_platform = (Platform)Class.forName( className ).newInstance();
        }
        catch ( Exception e ) {
            m_platform = new Platform();
            System.err.println(
                e.getClass().getName() + ": " + e.getMessage()
            );
        }
    }
}
/* vim:set et sw=4 ts=4: */
