package railo.runtime.services;

import java.io.IOException;
import java.sql.SQLException;

import railo.commons.io.res.Resource;
import railo.commons.io.res.filter.ExtensionResourceFilter;
import railo.commons.io.res.util.ResourceUtil;
import railo.runtime.PageContext;
import railo.runtime.config.ConfigWebAdmin;
import railo.runtime.db.DataSourceManager;
import railo.runtime.exp.ExpressionException;
import railo.runtime.exp.PageException;
import railo.runtime.exp.PageRuntimeException;
import railo.runtime.exp.SecurityException;
import railo.runtime.functions.list.ListFirst;
import railo.runtime.type.Array;
import railo.runtime.type.ArrayImpl;
import railo.runtime.type.Struct;
import railo.runtime.type.StructImpl;
import railo.runtime.type.util.KeyConstants;
import coldfusion.server.DataSourceService;
import coldfusion.server.ServiceException;
import coldfusion.sql.DataSource;

public class DataSourceServiceImpl extends ServiceSupport implements DataSourceService {

	private Number maxQueryCount=new Double(500);
	


	public Struct getDefaults() {
		Struct sct=new StructImpl();
		sct.setEL("alter",Boolean.TRUE);
		sct.setEL("blob_buffer",new Double(64000));
		sct.setEL("buffer",new Double(64000));
		sct.setEL("create",Boolean.TRUE);
		sct.setEL("delete",Boolean.TRUE);
		sct.setEL("disable",Boolean.FALSE);
		sct.setEL("disable_blob",Boolean.TRUE);
		sct.setEL("disable_clob",Boolean.TRUE);
		sct.setEL("drop",Boolean.TRUE);
		sct.setEL("grant",Boolean.TRUE);
		sct.setEL("insert",Boolean.TRUE);
		sct.setEL("pooling",Boolean.TRUE);
		sct.setEL("revoke",Boolean.TRUE);
		sct.setEL("select",Boolean.TRUE);
		sct.setEL("storedproc",Boolean.TRUE);
		sct.setEL("update",Boolean.TRUE);
		sct.setEL("",Boolean.TRUE);
		sct.setEL("",Boolean.TRUE);
		sct.setEL("",Boolean.TRUE);
		sct.setEL("interval",new Double(420));
		sct.setEL("login_timeout",new Double(30));
		sct.setEL("timeout",new Double(1200));

		return sct;
	}

	public Number getMaxQueryCount() {
		return maxQueryCount;
	}

	public void setMaxQueryCount(Number maxQueryCount) {
		this.maxQueryCount=maxQueryCount;
	}

	public String encryptPassword(String pass) {
		throw new PageRuntimeException(new ServiceException("method [encryptPassword] is not supported for datasource service"));
		//return pass;
	}

	@Override
	public String getDbdir() {
		Resource db = config().getConfigDir().getRealResource("db");
		if(!db.exists())db.createNewFile();
		return db.getPath();
	}

	@Override
	public Object getCachedQuery(String key) {
		throw new PageRuntimeException(new ServiceException("method [getQueryCache] is not supported for datasource service"));
		//pageContext.getQueryCache().getQuery(sql,datasource,username,password,cachedafter)
		// return null;
	}

	@Override
	public void setCachedQuery(String arg0, Object arg1) {
		throw new PageRuntimeException(new ServiceException("method [setQueryCache] is not supported for datasource service"));
	}

	@Override
	public void purgeQueryCache() throws IOException {
		PageContext pc = pc();
		if(pc!=null)pc.getQueryCache().clearUnused(pc);

	}

	@Override
	public boolean disableConnection(String name) {return false;}

	@Override
	public boolean isJadoZoomLoaded() {return false;}



	public Struct getDrivers() throws ServiceException, SecurityException {
		checkReadAccess();
		Struct rtn=new StructImpl();
		Struct driver;
		
		try {
			Resource railoContext = ResourceUtil.toResourceExisting(pc() ,"/railo-context/admin/dbdriver/");
			Resource[] children = railoContext.listResources(new ExtensionResourceFilter("cfc"));
	    	 
	    	String name;
	    	for(int i=0;i<children.length;i++) {
	    		driver=new StructImpl();
	    		name=ListFirst.call(pc(),children[i].getName(),".");
	    		driver.setEL(KeyConstants._name,name);
	    		driver.setEL("handler",children[i].getName());
	    		rtn.setEL(name,driver);
	    	}
		
		
		} catch (ExpressionException e) {
			throw new ServiceException(e.getMessage());
		}
    	
    	
		
		return rtn;
	}
	
	public Struct getDatasources() throws SecurityException {// MUST muss struct of struct zurŸckgeben!!!
		checkReadAccess();
		railo.runtime.db.DataSource[] sources = config().getDataSources();
		Struct rtn=new StructImpl();
		for(int i=0;i<sources.length;i++) {
			rtn.setEL(sources[i].getName(),new DataSourceImpl(sources[i]));
		}
		return rtn;
	}
	
	public Array getNames() throws SecurityException {
		checkReadAccess();
		railo.runtime.db.DataSource[] sources = config().getDataSources();
		Array names=new ArrayImpl();
		for(int i=0;i<sources.length;i++) {
			names.appendEL(sources[i].getName());
		}
		return names;
	}

	public void removeDatasource(String name) throws SQLException, SecurityException {
		checkWriteAccess();
		try {
			ConfigWebAdmin admin = ConfigWebAdmin.newInstance(config(),"");
			admin.removeDataSource(name);
		} catch (Exception e) {
			// ignoriert wenn die db nicht existiert
		}
	}



	public boolean verifyDatasource(String name) throws SQLException, SecurityException {
		checkReadAccess();
		railo.runtime.db.DataSource d = _getDatasource(name);
		PageContext pc = pc();
		DataSourceManager manager = pc.getDataSourceManager();
    	try {
			manager.releaseConnection(pc,manager.getConnection(pc,name, d.getUsername(), d.getPassword()));
			return true;
		} catch (PageException e) {
			return false;
		}
	}
	
	public DataSource getDatasource(String name) throws SQLException, SecurityException {
		return new DataSourceImpl(_getDatasource(name));
	}

	private railo.runtime.db.DataSource _getDatasource(String name) throws SQLException, SecurityException {
		checkReadAccess();
		name=name.trim();
		railo.runtime.db.DataSource[] sources = config().getDataSources();
		for(int i=0;i<sources.length;i++) {
			if(sources[i].getName().equalsIgnoreCase(name))return sources[i];
		}
		throw new SQLException("no datasource with name ["+name+"] found");
	}
}
