package org.jcon.df.util.security;
                    
import org.jcon.df.request.Filter;
import org.jcon.df.work.DatabaseUtil;
import org.jcon.df.work.Row;
import org.jcon.util.DataLib;
import org.jcon.util.GenLib;

import java.util.Vector;
                  
/**
* This class contains a "user profile" which describes what
* a user of that profile type can and can't do. This is
* accomplished by using SecureItem names to filter what
* the user is allowed to do.  
* <p> <pre>
* The filter algorithm is:
* - If IsAdmin can do anything
* - If no SecureItem associated can do it
* - If SecureItem not allowed, can't do it
* - If SecureItem is allowed, can do it
* - If SecureItem is not in profile, can't do it </pre> 
* <p>
* SecureItem names are hierarchial, ie "Awards.Menu.Edit".
* When checking isAllowed(criteriaName) a match occurs if
* the item name lists "contains" the criteria name. For
* example a list name of "Awards" contains "Awards.Menu.Edit"
* and "System.Edit" contains "System.Edit.User". This scheme
* allows complex hierarchial grouping of secure system areas.
* The lists are IsAllowedItems and IsNotAllowedItems. 
*
* @author Jack Harich
*/
public class UserProfile {        

//---------- Private Fields ------------------------------ 
private String name;
private String description;
private String[] isAllowedItems;    // List
private String[] isNotAllowedItems; // List

private static final String TABLE = "UserProfile";     
            
//---------- Initialization ------------------------------
// Table structure is:
//     Name
//     Description
//     IsAllowedItems (new line delimited)
//     IsNotAllowedItems (new line delimited)
//
public static UserProfile createInstance(User user,
        DatabaseUtil dbUtil) { 
            
    String userProfileMID = String.valueOf(user.getUserProfileMID());
    if (userProfileMID == null) {
        GenLib.error("UserProfile.createInstance()",
            "UserID '" + user.getUserID() + "' has no UserProfileMID.");
        return null;
    }
    // Get row
    Filter filter = new Filter();
    filter.addExpression("MID", "=", userProfileMID);

    Row row = dbUtil.readRow(TABLE, filter);
    if (row != null) {
        
        UserProfile profile = new UserProfile();
        // name, description
        profile.name = row.getString(TABLE + ".Name");
        profile.description = row.getString(TABLE + ".Description");
        // isAllowedITems
        String items = row.getString(TABLE + ".IsAllowedItems");
        profile.isAllowedItems = DataLib.convertDelimStringToArray(items, "\n");
        // isNotAllowedItems
        items = row.getString(TABLE + ".IsNotAllowedItems");
        profile.isNotAllowedItems = DataLib.convertDelimStringToArray(items, "\n");
        
        return profile;
             
    } else {
        GenLib.error("UserProfile.createInstance()",
            "UserID '" + user.getUserID() + "', UserProfileMID '" + userProfileMID + "' not found.");
        return null;
    }            
}                
//---------- Public Methods ------------------------------ 
public String getName() {
    return name;
}    
/**
* Returns true if the user is allowed access to the
* secureItem, false if not. The local algorithm is: <p> <pre>
* - If no SecureItem associated can do it
* - If SecureItem not allowed, can't do it 
* - If SecureItem is allowed, can do it 
* - If SecureItem is not in profile, can't do it </pre>
*/     
public boolean isAllowed(String secureItem) {
    
    // If no SecureItem associated can do it
    // This is by far the most common case
    if (secureItem == null) return true;
        
    // If SecureItem not allowed, can't do it
    //print(".isAllowed() - Checking not allowed");
    if (arrayContains(isNotAllowedItems, secureItem))
        return false;
    
    // If SecureItem is allowed, can do it
    //print(".isAllowed() - Checking allowed");
    if (arrayContains(isAllowedItems, secureItem)) 
        return true;

    // If SecureItem is not in profile, can't do it
    //print(".isAllowed() - Not in profile");
    return false;
       
}
//---------- Private Methods ----------------------------
/**
* Returns true if the array "hierarchially contains" the
* value, false if not. Case sensitive. 
*/  
private boolean arrayContains(String[] array, String value) {
if (value == null) return false;

for (int i = array.length; --i >= 0;) { 
// An exact match
    // *** skip if (value.equals(array[i])) return true; 
    
    // Hierarchial containment - Examples:
    // "Awards.Menu.Edit.".startsWith("Awards.Menu.Edit.") is true
    // "Awards.Menu.Edit.".startsWith("Awards.Menu.") is true 
    // "Awards.Menu.Edit.".startsWith("Awards.") is true
    value = value + "."; // This allows skipping equals()
    //print(" - Comparing value " + value + " against list item " + array[i] + ".");
    if (value.startsWith(array[i] + ".")) return true;
}
return false;
} // End method
     
//--- Std
private static void print(String text) {
    System.out.println("UserProfile" + text);
}  

} // End class














