package jnr.posix.util;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * Honor semantics of chmod as best we can in pure Java.  Note, this uses reflection to be 
 * more tolerant of different Java versions. 
 */
public class Chmod {
    private static final boolean CHMOD_API_AVAILABLE;
    private static final Method setWritable;
    private static final Method setReadable;
    private static final Method setExecutable;
    
    static {
        boolean apiAvailable = false;
        Method setWritableVar = null;
        Method setReadableVar = null;
        Method setExecutableVar = null;
        try {
            setWritableVar = File.class.getMethod("setWritable", new Class[] {Boolean.TYPE, Boolean.TYPE});
            setReadableVar = File.class.getMethod("setReadable", new Class[] {Boolean.TYPE, Boolean.TYPE});
            setExecutableVar = File.class.getMethod("setExecutable", new Class[] {Boolean.TYPE, Boolean.TYPE});
            apiAvailable = true;
        } catch (Exception e) {
            // failed to load methods, no chmod API available
        }
        setWritable = setWritableVar;
        setReadable = setReadableVar;
        setExecutable = setExecutableVar;
        CHMOD_API_AVAILABLE = apiAvailable;
    }
    
    public static int chmod(File file, String mode) {
        if (CHMOD_API_AVAILABLE) {
            // fast version
            char other = '0';
            if (mode.length() >= 1) {
                other = mode.charAt(mode.length() - 1);
            }
            //char group = mode.charAt(mode.length() - 2);
            char user = '0';
            if (mode.length() >= 3) {
                user = mode.charAt(mode.length() - 3);
            }
            //char setuidgid = mode.charAt(mode.length() - 3);
            
            // group and setuid/gid are ignored, no way to do them fast. Should we fall back on slow?
            if (!setPermissions(file, other, false)) return -1;
            if (!setPermissions(file, user, true)) return -1;
            return 0;
        } else {
            // slow version
            try {
                Process chmod = Runtime.getRuntime().exec("/bin/chmod " + mode + " " + file.getAbsolutePath());
                chmod.waitFor();
                return chmod.exitValue();
            } catch (IOException ioe) {
                // FIXME: ignore?
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        }
        return -1;
    }
    
    private static boolean setPermissions(File file, char permChar, boolean userOnly) {
        int permValue = Character.digit(permChar, 8);
        
        try {
            if ((permValue & 1) != 0) {
                setExecutable.invoke(file, new Object[] {Boolean.TRUE, Boolean.valueOf(userOnly)});
            } else {
                setExecutable.invoke(file, new Object[] {Boolean.FALSE, Boolean.valueOf(userOnly)});
            }
            
            if ((permValue & 2) != 0) {
                setWritable.invoke(file, new Object[] {Boolean.TRUE, Boolean.valueOf(userOnly)});
            } else {
                setWritable.invoke(file, new Object[] {Boolean.FALSE, Boolean.valueOf(userOnly)});
            }
            
            if ((permValue & 4) != 0) {
                setReadable.invoke(file, new Object[] {Boolean.TRUE, Boolean.valueOf(userOnly)});
            } else {
                setReadable.invoke(file, new Object[] {Boolean.FALSE, Boolean.valueOf(userOnly)});
            }
            
            return true;
        } catch (IllegalAccessException iae) {
            // ignore, return false below
        } catch (InvocationTargetException ite) {
            // ignore, return false below
        }
        
        return false;
    }
}
