package railo.runtime.tag;

import railo.runtime.exp.ApplicationException;
import railo.runtime.exp.PageException;
import railo.runtime.exp.SecurityException;
import railo.runtime.ext.tag.TagImpl;
import railo.runtime.op.Caster;
import railo.runtime.registry.RegistryEntry;
import railo.runtime.registry.RegistryQuery;
import railo.runtime.security.SecurityManager;
import railo.runtime.type.KeyImpl;
import railo.runtime.type.QueryImpl;
import railo.runtime.type.util.KeyConstants;

/**
* Reads, writes, and deletes keys and values in the system registry. The cfregistry tag is supported 
*   on all platforms, including Linux, Solaris, and HP-UX.
*
*
*
**/
public final class Registry extends TagImpl {
    
    private static final short ACTION_GET_ALL=0;
    private static final short ACTION_GET=1;
    private static final short ACTION_SET=2;
    private static final short ACTION_DELETE=3;
    
    

	/** Value data to set. If you omit this attribute, cfregistry creates default value, as follows:
	** 
	** string: creates an empty string: "" 
	** dWord: creates a value of 0 (zero) */
	private String value;

	/** action to the registry */
	private short action=-1;

	/** Sorts query column data (case-insensitive). Sorts on Entry, Type, and Value columns as text. Specify a combination of columns from query output, in a comma-delimited list. For example: 
	** sort = "value desc, entry asc"
	** 
	** asc: ascending (a to z) sort order 
	** desc: descending (z to a) sort order */
	private String sort;

	/** string: return string values 
	** dWord: return DWord values 
	** key: return keys 
	** any: return keys and values */
	private short type=RegistryEntry.TYPE_ANY;

	/** Name of a registry branch. */
	private String branch;

	/** Registry value to access. */
	private String entry;

	/** Variable into which to put value. */
	private String variable;

	/** Name of record set to contain returned keys and values. */
	private String name;
	

	@Override
	public void release()	{
		super.release();
		value=null;
		action=-1;
		sort=null;
		type=RegistryEntry.TYPE_ANY;
		branch=null;
		entry=null;
		variable=null;
		name=null;
	}

	/** set the value value
	*  Value data to set. If you omit this attribute, cfregistry creates default value, as follows:
	* 
	* string: creates an empty string: "" 
	* dWord: creates a value of 0 (zero)
	* @param value value to set
	**/
	public void setValue(String value)	{
		this.value=value;
	}

	/** set the value action
	*  action to the registry
	* @param action value to set
	 * @throws ApplicationException
	**/
	public void setAction(String action) throws ApplicationException	{
	    action=action.toLowerCase().trim();
	    if(action.equals("getall")) this.action=ACTION_GET_ALL;
	    else if(action.equals("get")) this.action=ACTION_GET;
	    else if(action.equals("set")) this.action=ACTION_SET;
	    else if(action.equals("delete")) this.action=ACTION_DELETE;
	    else throw new ApplicationException("attribute action of the tag registry has an invalid value ["+action+"], valid values are [getAll, get, set, delete]");
	}

	/** set the value sort
	*  Sorts query column data (case-insensitive). Sorts on Entry, Type, and Value columns as text. Specify a combination of columns from query output, in a comma-delimited list. For example: 
	* sort = "value desc, entry asc"
	* 
	* asc: ascending (a to z) sort order 
	* desc: descending (z to a) sort order
	* @param sort value to set
	**/
	public void setSort(String sort)	{
		this.sort=sort;
	}

	/** set the value type
	*  string: return string values 
	* dWord: return DWord values 
	* key: return keys 
	* any: return keys and values
	* @param type value to set
	 * @throws ApplicationException
	**/
	public void setType(String type) throws ApplicationException	{
	    type=type.toLowerCase().trim();
	    if(type.equals("string")) this.type=RegistryEntry.TYPE_STRING;
	    else if(type.equals("dword")) this.type=RegistryEntry.TYPE_DWORD;
	    else if(type.equals("key")) this.type=RegistryEntry.TYPE_KEY;
	    else if(type.equals("any")) this.type=RegistryEntry.TYPE_ANY;
	    else throw new ApplicationException("attribute type of the tag registry has an invalid value ["+type+"], valid values are [string, dword]");
	}

	/** set the value branch
	*  Name of a registry branch.
	* @param branch value to set
	**/
	public void setBranch(String branch)	{
		this.branch=branch;
	}

	/** set the value entry
	*  Registry value to access.
	* @param entry value to set
	**/
	public void setEntry(String entry)	{
		this.entry=entry;
	}

	/** set the value variable
	*  Variable into which to put value.
	* @param variable value to set
	**/
	public void setVariable(String variable)	{
		this.variable=variable;
	}

	/** set the value name
	*  Name of record set to contain returned keys and values.
	* @param name value to set
	**/
	public void setName(String name)	{
		this.name=name;
	}


	@Override
	public int doStartTag() throws PageException	{
	    if(pageContext.getConfig().getSecurityManager().getAccess(SecurityManager.TYPE_TAG_REGISTRY)==SecurityManager.VALUE_NO) 
			throw new SecurityException("can't access tag [registry]","access is prohibited by security manager");
		
	    
	    if(action==ACTION_GET) doGet();
	    else if(action==ACTION_GET_ALL) doGetAll();
	    else if(action==ACTION_SET) doSet();
	    else if(action==ACTION_DELETE) doDelete();
	    
	    
	    
		return SKIP_BODY;
	}

	/**
	 * @throws PageException
     * 
     */
    private void doDelete() throws PageException {
        try {
            RegistryQuery.deleteValue(branch,entry);
        }
        catch (Exception e) {
            throw Caster.toPageException(e);
        } 
    }

    /**
     * @throws PageException
     * 
     */
    private void doSet() throws PageException {
        if(entry==null)throw new ApplicationException("attribute entry is required for tag registry, when action is [set]");
        if(type==RegistryEntry.TYPE_ANY)type=RegistryEntry.TYPE_STRING;
        if(value==null) {
            if(type==RegistryEntry.TYPE_DWORD)value="0";
            else value="";
        }
        
        try {
            RegistryQuery.setValue(branch,entry,type,value);
        } 
        catch (Exception e) {
            throw Caster.toPageException(e);
        } 
        
    }

    /**
     * @throws PageException
     * 
     */
    private void doGetAll() throws PageException {
        if(name==null)throw new ApplicationException("attribute name is required for tag registry, when action is [getAll]");
        
        
        try {
            RegistryEntry[] entries = RegistryQuery.getValues(branch,type);
            if(entries!=null) {
            	railo.runtime.type.Query qry=new QueryImpl(
                        new String[]{"entry","type","value"},
                        new String[]{"VARCHAR","VARCHAR","OTHER"},
                        entries.length,"query");
                for(int i=0;i<entries.length;i++) {
                    RegistryEntry e = entries[i];
                    int row=i+1;
                    qry.setAt(KeyConstants._entry,row,e.getKey());
                    qry.setAt(KeyConstants._type,row,RegistryEntry.toCFStringType(e.getType()));
                    qry.setAt(KeyConstants._value,row,e.getValue());
                }
                

        		// sort
        		if(sort!=null) {
        			String[] arr=sort.toLowerCase().split(",");
        			for(int i=arr.length-1;i>=0;i--) {
        				String[] col=arr[i].trim().split("\\s+");
        				if(col.length==1)qry.sort(KeyImpl.init(col[0].trim()));
        				else if(col.length==2) {
        					String order=col[1].toLowerCase().trim();
        					if(order.equals("asc"))
        					    qry.sort(KeyImpl.init(col[0]),railo.runtime.type.Query.ORDER_ASC);
        					else if(order.equals("desc"))
        					    qry.sort(KeyImpl.init(col[0]),railo.runtime.type.Query.ORDER_DESC);
        					else 
        						throw new ApplicationException("invalid order type ["+col[1]+"]");
        				}
        			}		
        		}
                
                
                
                
                
                
                
                pageContext.setVariable(name,qry);
            }
        } 
        catch (Exception e) {
            throw Caster.toPageException(e);
        } 
        
    }

    /**
     * @throws PageException
     * 
     */
    private void doGet() throws PageException {
        // "HKEY_LOCAL_MACHINE\\SOFTWARE\\Google\\NavClient","installtime"
        if(entry==null)throw new ApplicationException("attribute entry is required for tag registry, when action is [get]");
        if(variable==null)throw new ApplicationException("attribute variable is required for tag registry, when action is [get]");
        //if(type==-1)throw new ApplicationException("attribute type is required for tag registry, when action is [get]");
        
        
        try {
            RegistryEntry re = RegistryQuery.getValue(branch,entry,type);
            if(re!=null)pageContext.setVariable(variable,re.getValue());
        } 
        catch (Exception e) {
            throw Caster.toPageException(e);
        } 
      	
    }

    @Override
	public int doEndTag()	{
		return EVAL_PAGE;
	}

}