/*
 * Created on 31 janv. 2005
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package org.injac.indexing.util;

import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.rmi.ServerException;
import java.util.Enumeration;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.Vector;



import javax.servlet.http.HttpSession;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpRecoverableException;
import org.apache.commons.httpclient.HttpURL;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.util.URIUtil;
import org.apache.log4j.Logger;
import org.apache.webdav.lib.WebdavResource;
import org.apache.webdav.lib.methods.PropFindMethod;
import org.injac.indexing.exceptions.ForbiddenAccessException;

/**
 * @author shoam
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class WebdavConnection {
	/**
	 * Static logger
	 */
	static Logger logger = Logger.getLogger(WebdavConnection.class);
	private static final String SESSION_ATTR_NAME="webdavresource";
	/**
	 * The dav ressource uri
	 */
	private WebdavResource webdavResource;
	/**
	 * The dav ressource uri ex : http://saroumane.fr/slide/files/root
	 */
	private String webdavpath;
	
	/**
	 * Credentials
	 */
	private String login;
	private String password;
	private boolean connected=false;
	private HttpSession session = null;
	public WebdavConnection(HttpSession session)
	{
		super();
         this.session = session;
		
		
	}
	/**
	 * Connect to the WebDAV server	 
	 * @throws ServerException
	 */
	public void connect(String connexionString) throws Exception
	{
		connect(connexionString, null, null);
	}
	/**
	 * Connect to the WebDAV server	 
	 * @throws ServerException
	 */
	public void connect(String connexionString, String alogin, String apassword) throws Exception
	{
		logger.debug("ConnexionString : "+ connexionString+"\nlogin : "+login);
		
		
		webdavResource=(WebdavResource) session.getAttribute(SESSION_ATTR_NAME);
		if(webdavResource==null)
		{
			logger.debug("WebdavConnection:connect : no webdavresource in session.");
			createNewInstance(connexionString, alogin, apassword);
		}
		else
		{	
			// check if session instance has the same properties as needed ie host and userinfos
			HttpURL httpUrl = webdavResource.getHttpURL();
			//;
			boolean change= false;
			String user = httpUrl.getUser();
			String host = httpUrl.getHost();
			String reqhost= new HttpURL(connexionString).getHost();
			
			if((alogin !=null) && (!alogin.equals(user)))
					change = true;
			if(!reqhost.equals(host))
					change = true;
			if(change)
			{
				
				 webdavResource.close();
				 webdavResource= null;
				 createNewInstance(connexionString, alogin, apassword);
				 httpUrl = webdavResource.getHttpURL();
				user = httpUrl.getUser();
				String sessionpath= httpUrl.getPath();
				logger.debug("modifying session instance of webdavresource \npath : "+sessionpath+"\nlogin"+user);
			}
			logger.debug("WebdavConnection:connect : webdavresource found in session.");
			
			
		}
		connected=(webdavResource!=null);
	}
	
	private void createNewInstance(String connexionString, String alogin, String apassword) throws Exception
	{
		webdavpath=connexionString;
		login=alogin;
		password=apassword;
		
		logger.debug("WebdavConnection:createNewInstance.");
		HttpURL httpUrl = null;
		try 
		{
				if((login!=null) && (password!=null))
				{	
					httpUrl = new HttpURL(webdavpath);
					httpUrl.setUserinfo(login, password);
				}
				else
				{
					httpUrl = new HttpURL(webdavpath);
					logger.debug("Connexion String :" + webdavpath);
				}
				webdavResource = new WebdavResource(httpUrl);
				session.setAttribute(SESSION_ATTR_NAME, webdavResource);
				logger.debug("WebdavConnection:connect : new webdavresource created in session.");
			}
		catch (HttpException ex) 
		{	
			if(httpUrl != null)
				logger.error("WebdavConnection:connect httpUrl path : "+ httpUrl.getPath()+"\n"+ex.getMessage());	
	        throw new Exception();
	    }
		catch (IOException ex) 
		{  	
			if(httpUrl != null)
				logger.error("WebdavConnection:connect httpUrl path : "+ httpUrl.getPath()+"\n"+ex.getMessage());		
	        throw new Exception("WebdavConnection:connect", ex);
	    }
	}
	/**
	 * Give the property of resource
	 * @return the array of properties
	 */
	public Enumeration getProperties(String path, int depth, Vector properties) throws URIException, IOException, ForbiddenAccessException 
	{
		if(!connected)
			return null;
		Enumeration result=null;
		int response_code;
		
		try
		{
			path = checkPath(path);
			PropFindMethod method = new PropFindMethod(path, depth, properties.elements());
			response_code= webdavResource.retrieveSessionInstance().executeMethod(method);
			logger.debug("WebdavConnection::getProperties HTTP response code :"+response_code);
			result= method.getResponseProperties(path);
			
		}catch(Exception e)
		{
			logger.error("WebdavConnection::getProperties : propfindMethod error: \npath: "+webdavpath+"\tpath : "+path);
			throw new IOException("WebdavConnection::getProperties : propfindMethod error :\nwebdavpath: "+webdavpath+"\tpath : "+path);
		}
		return (Enumeration)checkResult(path, result, response_code);
	}
	
	
	public Enumeration getProperties(String path, int depth) throws URIException, ForbiddenAccessException 
	{
		if(!connected)
			return null;
		Enumeration result=null;
		int response_code;
		try
		{
			path = checkPath(path);
			
			PropFindMethod method = new PropFindMethod( path, depth);
			response_code= webdavResource.retrieveSessionInstance().executeMethod(method);
			result= method.getResponseProperties(path);
		}catch(Exception e)
		{
			logger.error("WebdavConnection::getProperties : propfindMethod error: \nwebdavpath: "+webdavpath+"\tpath : "+path+e.getLocalizedMessage());
			throw new URIException("WebdavConnection::getProperties : propfindMethod error :\npath: "+webdavpath+"\tpath : "+path+e.getLocalizedMessage());
		}
		logger.debug("WebdavConnection::getProperties : result:"+result.toString());
		return (Enumeration)checkResult(path, result, response_code);
		
	}
	
	public Enumeration getResponses(String path, int depth) throws ForbiddenAccessException
	{
		if(!connected)
			return null;
		Enumeration result=null;
		int response_code=0;
		try
		{
			path = checkPath(path);
			logger.info("WebdavGenerator::buildResponse path: "+path);
			PropFindMethod method = new PropFindMethod( path, depth);
			response_code= webdavResource.retrieveSessionInstance().executeMethod(method);
		    result = method.getResponses();
			logger.info("WebdavGenerator::getResponses ok");
		}catch(Exception e)
		{
			logger.error("WebdavGenerator::getResponses error:\npath: "+path+"\n"+e.getMessage());
		}	
		return (Enumeration)checkResult(path, result, response_code);
	}
	
	private Object checkResult(String path,Object result, int response_code ) throws ForbiddenAccessException
	{
		switch(response_code){
		case 403:
			throw new ForbiddenAccessException(path+" : access forbidden.");
		default : return result;
		}
	}
	
	private String checkPath(String path)
	{
		
		String realPath = path;
		try{
		
		if (path.endsWith("/")) {
			realPath = realPath.substring(0, realPath.length()-1);
		}	
		
		realPath = URIUtil.encodePathQuery(realPath);		
		realPath = realPath.replaceAll("!","%21");			
		realPath = realPath.replaceAll("#","%23");
		realPath = realPath.replaceAll("\\$","%24");					
		realPath = realPath.replaceAll("&","%26");
		realPath = realPath.replaceAll("'","%27");
		realPath = realPath.replaceAll("\\(","%28");
		realPath = realPath.replaceAll("\\)","%29");		
		realPath = realPath.replaceAll("@","%40");		
		realPath = realPath.replaceAll("`","%60");
		}catch(Exception e)
		{
			logger.error("WebdavConnection:checkPath : "+e);
		}
		/*
		int index ;
		while((index =path.indexOf(' ')) !=-1)
		{
			path = path.substring(0, index)+"%20"+path.substring(index+1);
			
		}*/
		
		
		
		return realPath;
		
	
	}
	
	public Enumeration getResponses(int depth) throws ForbiddenAccessException
	{
		int index = webdavpath.indexOf("/slide/");
		// no slide context case
		if(index==-1)
			index = webdavpath.indexOf("/files");
		
		String path = webdavpath.substring(index);
		return getResponses(path, depth);
		
	}
	public synchronized InputStream getMethodData(String path) 
	{
		InputStream result=null;
	
		try
		{
			//path = checkPath(path);
			result=webdavResource.getMethodData(path);
			
		}catch(HttpRecoverableException hre)
		{
			logger.debug("parsing recoverableException form getMethodData.");
			return result;
		
		}
		catch(Exception e)
		{
			logger.error("WebdavConnection::getMethodData : path="+path+"\n"+ e.getMessage());
			return null;
		}
		return result;
		
	}

}
