package com.jscape.util.file;

/**
 * @author Alex Usun
 */
public enum PosixFilePermissionsFormat {

    S_IRUSR(0x00000100) {
        @Override
        public int format(PosixFilePermissions permissions, int data) {
            return set(permissions.ownerCanRead, data);
        }
    },
    S_IWUSR(0x00000080) {
        @Override
        public int format(PosixFilePermissions permissions, int data) {
            return set(permissions.ownerCanWrite, data);
        }
    },
    S_IXUSR(0x00000040) {
        @Override
        public int format(PosixFilePermissions permissions, int data) {
            return set(permissions.ownerCanExecute, data);
        }
    },
    S_IRGRP(0x00000020) {
        @Override
        public int format(PosixFilePermissions permissions, int data) {
            return set(permissions.groupCanRead, data);
        }
    },
    S_IWGRP(0x00000010) {
        @Override
        public int format(PosixFilePermissions permissions, int data) {
            return set(permissions.groupCanWrite, data);
        }
    },
    S_IXGRP(0x00000008) {
        @Override
        public int format(PosixFilePermissions permissions, int data) {
            return set(permissions.groupCanExecute, data);
        }
    },
    S_IROTH(0x00000004) {
        @Override
        public int format(PosixFilePermissions permissions, int data) {
            return set(permissions.otherCanRead, data);
        }
    },
    S_IWOTH(0x00000002) {
        @Override
        public int format(PosixFilePermissions permissions, int data) {
            return set(permissions.otherCanWrite, data);
        }
    },
    S_IXOTH(0x00000001) {
        @Override
        public int format(PosixFilePermissions permissions, int data) {
            return set(permissions.otherCanExecute, data);
        }
    },
    S_ISUID(0x00000800) {
        @Override
        public int format(PosixFilePermissions permissions, int data) {
            return set(permissions.setUserIdOnExecution, data);
        }
    },
    S_ISGID(0x00000400) {
        @Override
        public int format(PosixFilePermissions permissions, int data) {
            return set(permissions.setGroupIdOnExecution, data);
        }
    },
    S_ISVTX(0x00000200) {
        @Override
        public int format(PosixFilePermissions permissions, int data) {
            return set(permissions.directoryDeletionRestricted, data);
        }
    };

    public final int mask;

    public static int format(PosixFilePermissions permissions) {
        int data = 0;
        for (PosixFilePermissionsFormat permissionFormat : values()) {
            data = permissionFormat.format(permissions, data);
        }

        return data;
    }

    public static String formatString(PosixFilePermissions permissions) {
        return String.format("%c%c%c%c%c%c%c%c%c",
                             readPermissionChar(permissions.ownerCanRead),
                             writePermissionChar(permissions.ownerCanWrite),
                             executePermissionChar(permissions.ownerCanExecute),
                             readPermissionChar(permissions.groupCanRead),
                             writePermissionChar(permissions.groupCanWrite),
                             executePermissionChar(permissions.groupCanExecute),
                             readPermissionChar(permissions.otherCanRead),
                             writePermissionChar(permissions.otherCanWrite),
                             executePermissionChar(permissions.otherCanExecute));
    }

    public static PosixFilePermissions parse(String data, int index) {
        return new PosixFilePermissions(readPermission(data.charAt(index)),
                                        writePermission(data.charAt(index + 1)),
                                        executePermission(data.charAt(index + 2)),
                                        readPermission(data.charAt(index + 3)),
                                        writePermission(data.charAt(index + 4)),
                                        executePermission(data.charAt(index + 5)),
                                        readPermission(data.charAt(index + 6)),
                                        writePermission(data.charAt(index + 7)),
                                        executePermission(data.charAt(index + 8)),
                                        false,
                                        false,
                                        false);
    }

    public static PosixFilePermissions parse(int data) {
        return new PosixFilePermissions(S_IRUSR.allowed(data),
                                        S_IWUSR.allowed(data),
                                        S_IXUSR.allowed(data),
                                        S_IRGRP.allowed(data),
                                        S_IWGRP.allowed(data),
                                        S_IXGRP.allowed(data),
                                        S_IROTH.allowed(data),
                                        S_IWOTH.allowed(data),
                                        S_IXOTH.allowed(data),
                                        S_ISUID.allowed(data),
                                        S_ISGID.allowed(data),
                                        S_ISVTX.allowed(data));
    }

    private static char readPermissionChar(boolean value) {
        return value ? 'r' : '-';
    }

    private static boolean readPermission(char ch) {
        return ch == 'r';
    }

    private static char writePermissionChar(boolean value) {
        return value ? 'w' : '-';
    }

    private static boolean writePermission(char ch) {
        return ch == 'w';
    }

    private static char executePermissionChar(boolean value) {
        return value ? 'x' : '-';
    }

    private static boolean executePermission(char ch) {
        return ch == 'x';
    }

    PosixFilePermissionsFormat(int mask) {
        this.mask = mask;
    }

    public boolean allowed(int data) {
        return (data & this.mask) == this.mask;
    }

    public int set(boolean value, int data) {
        return value ? data | this.mask : data;
    }

    public abstract int format(PosixFilePermissions permissions, int data);
}
