/*
 * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.media.sound;

import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.StringTokenizer;



/**
 * Audio configuration class for exposing attributes specific to the platform or system.
 *
 * @author Kara Kytle
 * @author Florian Bomers
 */
final class Platform {


    // STATIC FINAL CHARACTERISTICS

    // native library we need to load
    private static final String libNameMain     = "jsound";
    private static final String libNameALSA     = "jsoundalsa";
    private static final String libNameDSound   = "jsoundds";

    // extra libs handling: bit flags for each different library
    public static final int LIB_MAIN     = 1;
    public static final int LIB_ALSA     = 2;
    public static final int LIB_DSOUND   = 4;

    // bit field of the constants above. Willbe set in loadLibraries
    private static int loadedLibs = 0;

    // features: the main native library jsound reports which feature is
    // contained in which lib
    public static final int FEATURE_MIDIIO       = 1;
    public static final int FEATURE_PORTS        = 2;
    public static final int FEATURE_DIRECT_AUDIO = 3;

    // SYSTEM CHARACTERISTICS
    // vary according to hardware architecture

    // signed8 (use signed 8-bit values) is true for everything we support except for
    // the solaris sbpro card.
    // we'll leave it here as a variable; in the future we may need this in java.
    // wait, is that true?  i'm not sure.  i think solaris takes unsigned data?
    // $$kk: 03.11.99: i think solaris takes unsigned 8-bit or signed 16-bit data....
    private static boolean signed8;

    // intel is little-endian.  sparc is big-endian.
    private static boolean bigEndian;

    // this is the value of the "java.home" system property.  i am looking it up here
    // for use when trying to load the soundbank, just so
    // that all the privileged code is localized in this file....
    private static String javahome;

    // this is the value of the "java.class.path" system property
    private static String classpath;




    static {
        if(Printer.trace)Printer.trace(">> Platform.java: static");

        loadLibraries();
        readProperties();
    }


    /**
     * Private constructor.
     */
    private Platform() {
    }


    // METHODS FOR INTERNAL IMPLEMENTATION USE


    /**
     * Dummy method for forcing initialization.
     */
    static void initialize() {

        if(Printer.trace)Printer.trace("Platform: initialize()");
    }


    /**
     * Determine whether the system is big-endian.
     */
    static boolean isBigEndian() {

        return bigEndian;
    }


    /**
     * Determine whether the system takes signed 8-bit data.
     */
    static boolean isSigned8() {

        return signed8;
    }


    /**
     * Obtain javahome.
     * $$kk: 04.16.99: this is *bad*!!
     */
    static String getJavahome() {

        return javahome;
    }

    /**
     * Obtain classpath.
     * $$jb: 04.21.99: this is *bad* too!!
     */
    static String getClasspath() {

        return classpath;
    }


    // PRIVATE METHODS

    /**
     * Load the native library or libraries.
     */
    private static void loadLibraries() {
        if(Printer.trace)Printer.trace(">>Platform.loadLibraries");

        try {
            // load the main library
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                @Override
                public Void run() {
                    System.loadLibrary(libNameMain);
                    return null;
                }
            });
            // just for the heck of it...
            loadedLibs |= LIB_MAIN;
        } catch (SecurityException e) {
            if(Printer.err)Printer.err("Security exception loading main native library.  JavaSound requires access to these resources.");
            throw(e);
        }

        // now try to load extra libs. They are defined at compile time in the Makefile
        // with the define EXTRA_SOUND_JNI_LIBS
        String extraLibs = nGetExtraLibraries();
        // the string is the libraries, separated by white space
        StringTokenizer st = new StringTokenizer(extraLibs);
        while (st.hasMoreTokens()) {
            final String lib = st.nextToken();
            try {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    @Override
                    public Void run() {
                        System.loadLibrary(lib);
                        return null;
                    }
                });

                if (lib.equals(libNameALSA)) {
                    loadedLibs |= LIB_ALSA;
                    if (Printer.debug) Printer.debug("Loaded ALSA lib successfully.");
                } else if (lib.equals(libNameDSound)) {
                    loadedLibs |= LIB_DSOUND;
                    if (Printer.debug) Printer.debug("Loaded DirectSound lib successfully.");
                } else {
                    if (Printer.err) Printer.err("Loaded unknown lib '"+lib+"' successfully.");
                }
            } catch (Throwable t) {
                if (Printer.err) Printer.err("Couldn't load library "+lib+": "+t.toString());
            }
        }
    }


    static boolean isMidiIOEnabled() {
        return isFeatureLibLoaded(FEATURE_MIDIIO);
    }

    static boolean isPortsEnabled() {
        return isFeatureLibLoaded(FEATURE_PORTS);
    }

    static boolean isDirectAudioEnabled() {
        return isFeatureLibLoaded(FEATURE_DIRECT_AUDIO);
    }

    private static boolean isFeatureLibLoaded(int feature) {
        if (Printer.debug) Printer.debug("Platform: Checking for feature "+feature+"...");
        int requiredLib = nGetLibraryForFeature(feature);
        boolean isLoaded = (requiredLib != 0) && ((loadedLibs & requiredLib) == requiredLib);
        if (Printer.debug) Printer.debug("          ...needs library "+requiredLib+". Result is loaded="+isLoaded);
        return isLoaded;
    }

    // the following native methods are implemented in Platform.c
    private native static boolean nIsBigEndian();
    private native static boolean nIsSigned8();
    private native static String nGetExtraLibraries();
    private native static int nGetLibraryForFeature(int feature);


    /**
     * Read the required system properties.
     */
    private static void readProperties() {
        // $$fb 2002-03-06: implement check for endianness in native. Facilitates porting !
        bigEndian = nIsBigEndian();
        signed8 = nIsSigned8(); // Solaris on Sparc: signed, all others unsigned
        javahome = JSSecurityManager.getProperty("java.home");
        classpath = JSSecurityManager.getProperty("java.class.path");
    }
}
