/**
 * Copyright 2010 JogAmp Community. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice, this list of
 *       conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list
 *       of conditions and the following disclaimer in the documentation and/or other materials
 *       provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are those of the
 * authors and should not be interpreted as representing official policies, either expressed
 * or implied, of JogAmp Community.
 */

package jogamp.opengl;

import java.util.StringTokenizer;

import javax.media.opengl.GLContext;

import com.jogamp.common.util.VersionNumberString;

/**
 * A class for storing and comparing OpenGL version numbers.
 * This only works for desktop OpenGL at the moment.
 */
class GLVersionNumber extends VersionNumberString {

    private final boolean valid;

    private GLVersionNumber(int[] val, String versionString, boolean valid) {
        super(val[0], val[1], val[2], versionString);
        this.valid = valid;
    }
    
    public static GLVersionNumber create(String versionString) {
        int[] val = new int[] { 0, 0, 0 };
        try {
            if (versionString.startsWith("GL_VERSION_")) {
                StringTokenizer tok = new StringTokenizer(versionString, "_");
                tok.nextToken(); // GL_
                tok.nextToken(); // VERSION_
                if (!tok.hasMoreTokens()) {
                    val[0] = 0;
                } else {
                    val[0] = Integer.valueOf(tok.nextToken()).intValue();
                    if (!tok.hasMoreTokens()) {
                        val[1] = 0;
                    } else {
                        val[1] = Integer.valueOf(tok.nextToken()).intValue();
                        if (!tok.hasMoreTokens()) {
                            val[2] = 0;
                        } else {
                            val[2] = Integer.valueOf(tok.nextToken()).intValue();
                        }
                    }
                }
            } else {
                int radix = 10;
                if (versionString.length() > 2) {
                    if (Character.isDigit(versionString.charAt(0)) && versionString.charAt(1) == '.' && Character.isDigit(versionString.charAt(2))) {
                        val[0] = Character.digit(versionString.charAt(0), radix);
                        val[1] = Character.digit(versionString.charAt(2), radix);
                        // See if there's version-specific information which might
                        // imply a more recent OpenGL version
                        StringTokenizer tok = new StringTokenizer(versionString, " ");
                        if (tok.hasMoreTokens()) {
                            tok.nextToken();
                            if (tok.hasMoreTokens()) {
                                String token = tok.nextToken();
                                int i = 0;
                                while (i < token.length() && !Character.isDigit(token.charAt(i))) {
                                    i++;
                                }
                                if (i < token.length() - 2 && Character.isDigit(token.charAt(i)) && token.charAt(i + 1) == '.' && Character.isDigit(token.charAt(i + 2))) {
                                    int altMajor = Character.digit(token.charAt(i), radix);
                                    int altMinor = Character.digit(token.charAt(i + 2), radix);
                                    // Avoid possibly confusing situations by putting some
                                    // constraints on the upgrades we do to the major and
                                    // minor versions
                                    if ( (altMajor == val[0] && altMinor > val[1]) || altMajor == val[0] + 1 ) {
                                        if( GLContext.isValidGLVersion(altMajor, altMinor) ) {
                                            val[0] = altMajor;
                                            val[1] = altMinor;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return new GLVersionNumber(val, versionString, true);
        } catch (Exception e) {
            e.printStackTrace();
            // FIXME: refactor desktop OpenGL dependencies and make this
            // class work properly for OpenGL ES
            System.err.println("Info: ExtensionAvailabilityCache: FunctionAvailabilityCache.Version.<init>: " + e);
            val[0] = 1;
            val[1] = 0;
            /*
            throw (IllegalArgumentException)
            new IllegalArgumentException(
            "Illegally formatted version identifier: \"" + versionString + "\"")
            .initCause(e);
             */            
        }
        return new GLVersionNumber(val, versionString, false);
    }

    public final boolean isValid() {
        return valid;
    }
    
    /** 
     * Returns the optional vendor version at the end of the 
     * <code>GL_VERSION</code> string if exists, otherwise <code>null</code>.
     * <pre>
     *   2.1 Mesa 7.0.3-rc2 -> 7.0.3 (7.0.3-rc2)
     *   4.2.12171 Compatibility Profile Context 9.01.8 -> 9.1.8 (9.01.8)
     *   4.3.0 NVIDIA 310.32 -> 310.32 (310.32)
     * </pre> 
     */
    public static final VersionNumberString createVendorVersion(String versionString) {
        if (versionString == null || versionString.length() <= 0) {
            return null;
        }    
        final String[] strings = versionString.trim().split("\\s+");
        if ( strings.length <= 0 ) {
            return null;
        }
        // Test all segments backwards from [len-1..1], skipping the 1st entry (GL version)
        // If a segment represents a valid VersionNumber - use it.
        for(int i=strings.length-1; i>=1; i--) {
            final String s = strings[i];
            final VersionNumberString version = new VersionNumberString(s, ".");
            if( !version.isZero() ) {
                return version;
            }
        }
        return null;
    }
}