package eu.dnetlib.enabling.aas.ismock;

import java.util.Arrays;
import java.util.List;

import javax.xml.transform.OutputKeys;
import javax.xml.ws.wsaddressing.W3CEndpointReference;

import org.apache.cxf.endpoint.Endpoint;
import org.apache.log4j.Logger;
import org.xmldb.api.DatabaseManager;
import org.xmldb.api.base.Collection;
import org.xmldb.api.base.Database;
import org.xmldb.api.base.Resource;
import org.xmldb.api.base.ResourceIterator;
import org.xmldb.api.base.ResourceSet;
import org.xmldb.api.base.XMLDBException;
import org.xmldb.api.modules.CollectionManagementService;
import org.xmldb.api.modules.XMLResource;
import org.xmldb.api.modules.XPathQueryService;

import eu.dnetlib.common.rmi.UnimplementedException;
import eu.dnetlib.enabling.aas.service.A2Constants;
import eu.dnetlib.enabling.is.store.rmi.ISStoreException;
import eu.dnetlib.enabling.is.store.rmi.ISStoreService;
import eu.dnetlib.enabling.resultset.rmi.ResultSetException;
import eu.dnetlib.enabling.resultset.rmi.ResultSetService;
import eu.dnetlib.soap.cxf.CxfEndpointReferenceBuilder;

/**
 * Mock class implementing ISStore service functionalities.
 * @author mhorst
 *
 */
public class MockISStore implements ISStoreService {

	protected static final Logger log = Logger.getLogger(MockISStore.class);
	
	public static final String IDENTIFY = "ISStore-mock-0.9.1";
	
	/**
	 * XML database URI
	 */
	private String dbURI;
	
	/**
	 * Default xmlDb driver. Can be overriden in Spring configuration.
	 */
	private String xmlDbDriver = "org.exist.xmldb.DatabaseImpl";

	private ResultSetService resultSetService;
	
	/**
	 * Local endpoint reference.
	 */
	private Endpoint localEndpoint;
	
	/**
	 * Init method to setup and initialize ISLookUp serivce. 
	 */
	@SuppressWarnings("unchecked")
	public void init() {
//			 initialize database driver
		try {
			Class cl;
			cl = Class.forName(xmlDbDriver);
			Database database = (Database) cl.newInstance();
		    DatabaseManager.registerDatabase(database);
		} catch (ClassNotFoundException e) {
			log.error("Error occured during xml database initialization!",e);
		} catch (InstantiationException e) {
			log.error("Error occured during xml database initialization!",e);
		} catch (IllegalAccessException e) {
			log.error("Error occured during xml database initialization!",e);
		} catch (XMLDBException e) {
			log.error("Error occured during xml database initialization!",e);
		}
		
	}
	
	/* (non-Javadoc)
	 * @see eu.dnetlib.enabling.is.store.rmi.ISStoreService#createFileColl(java.lang.String)
	 */
	public boolean createFileColl(String fileColl) {
		if (fileColl==null)
			return false;
		try {
			Collection col = DatabaseManager.getCollection(dbURI + fileColl);
			if (col==null) {
				Collection root = DatabaseManager.getCollection(dbURI + "/db");
	            CollectionManagementService mgtService = (CollectionManagementService)
	                root.getService("CollectionManagementService", "1.0");
	            mgtService.createCollection(fileColl.substring("/db".length()));
			}
			return true;

		} catch (XMLDBException e) {
			log.error("Exception occured when trying to create collection "+fileColl,e);
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.enabling.is.store.rmi.ISStoreService#deleteFileColl(java.lang.String)
	 */
	public boolean deleteFileColl(String fileColl) {
		if (fileColl==null)
			return false;
		try {
			Collection col = DatabaseManager.getCollection(dbURI + fileColl);
			if (col!=null) {
				Collection root = DatabaseManager.getCollection(dbURI + "/db");
	            CollectionManagementService mgtService = (CollectionManagementService)
	                root.getService("CollectionManagementService", "1.0");
	            mgtService.removeCollection(fileColl.substring("/db".length()));
			}
			return true;
		} catch (XMLDBException e) {
			log.error("Exception occured when trying to delete collection "+fileColl,e);
		}
		return false;
	}


	/* (non-Javadoc)
	 * @see eu.dnetlib.enabling.is.store.rmi.ISStoreService#deleteXML(java.lang.String, java.lang.String)
	 */
	public boolean deleteXML(String fileName, String fileColl) {
		if (fileName==null || fileColl==null)
			return false;
		try {
			Collection col = DatabaseManager.getCollection(dbURI + fileColl);
			if (col==null) {
				log.error("Collection "+fileColl+" doesn't exist!");
				return false;
			}
			XMLResource res = (XMLResource)col.getResource(fileName);
			if (res!=null) {
				col.removeResource(res);
			}
			return true;
		} catch (XMLDBException e) {
			log.error("Exception occured when trying to remove '"+fileName+"' from collection "+fileColl,e);
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.enabling.is.store.rmi.ISStoreService#getFileColls()
	 */
	public List<String> getFileColls() {
		try {
			Collection root = DatabaseManager.getCollection(dbURI + "/db");
			return Arrays.asList(root.listChildCollections());
		} catch (XMLDBException e) {
			log.error("Exception occured when trying to list collections names!",e);
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.enabling.is.store.rmi.ISStoreService#getXML(java.lang.String, java.lang.String)
	 */
	public String getXML(String fileName, String fileColl) {
		if (fileName==null || fileColl==null)
			return null;
		try {
			Collection col = DatabaseManager.getCollection(dbURI + fileColl);
			if (col==null) {
				log.error("Collection "+fileColl+" doesn't exist!");
				return null;
			}
			col.setProperty(OutputKeys.INDENT, "no");
			XMLResource res = (XMLResource)col.getResource(fileName);
			if (res==null) {
				log.info("Resource "+fileName+" doesn't exist in collection "+fileColl+"!");
				return null;
			}
			return (String) res.getContent();
		} catch (XMLDBException e) {
			log.error("Exception occured when trying to get "+fileName+" from collection "+fileColl,e);
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.enabling.is.store.rmi.ISStoreService#insertXML(java.lang.String, java.lang.String, java.lang.String)
	 */
	public boolean insertXML(String fileName, String fileColl, String fileContent) {
		if (fileName==null || fileColl==null || fileContent==null)
			return false;
		try {
			Collection col = DatabaseManager.getCollection(dbURI + fileColl);
			if (col==null) {
				log.error("Collection "+fileColl+" doesn't exist!");
				return false;
			}
			col.setProperty(OutputKeys.INDENT, "no");
			XMLResource checkRes = (XMLResource)col.getResource(fileName);
			if (checkRes!=null) {
				log.error("Resource "+fileName+" already exists in collection "+fileColl+"!");
				return false;
			}
			Resource resource = col.createResource(fileName, XMLResource.RESOURCE_TYPE);
			resource.setContent(fileContent);
			col.storeResource(resource);
			return true;
		} catch (XMLDBException e) {
			log.error("Exception occured when trying to get "+fileName+" from collection "+fileColl,e);
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.enabling.is.store.rmi.ISStoreService#searchXML(java.lang.String)
	 */
	public W3CEndpointReference searchXML(String xQuery) {
		if (xQuery==null)
			return null;
		String rsId = String.valueOf(System.currentTimeMillis());
		try {
			Collection col = DatabaseManager.getCollection(dbURI + A2Constants.IS_COLLECTION_ROOT_PATH);
			if (col==null) {
				log.error("Couldn't find root db collection!");
				return null;
			}
			XPathQueryService service =
                (XPathQueryService) col.getService("XPathQueryService", "1.0");
            service.setProperty("indent", "no");
            ResourceSet result = service.query(xQuery);
            ResourceIterator i = result.getIterator();
            String[] results = new String[(int) result.getSize()];
            int count = 0;
            while(i.hasMoreResources()) {
                Resource r = i.nextResource();
                results[count] = ((String)r.getContent());
                count++;
            }
         	resultSetService.populateRS(rsId, Arrays.asList(results));
            
		} catch (XMLDBException e) {
			log.error("Exception occured when trying to execute query for root collection: "+xQuery,e);
		} catch (ResultSetException e) {
			log.error("Exception occured when populating ResultSet!",e);
		}
		return wrapResultSetId(rsId);
	}
	
	/**
	 * @param xQuery
	 * @return
	 */
	public String searchDirectXML(String xQuery) {
		if (xQuery==null)
			return null;
		
		try {
			Collection col = DatabaseManager.getCollection(dbURI + A2Constants.IS_COLLECTION_ROOT_PATH);
			if (col==null) {
				log.error("Couldn't find root db collection!");
				return null;
			}
			XPathQueryService service =
                (XPathQueryService) col.getService("XPathQueryService", "1.0");
            service.setProperty("indent", "no");
            ResourceSet result = service.query(xQuery);
            ResourceIterator i = result.getIterator();
            
            StringBuffer strBuff = new StringBuffer();
            while(i.hasMoreResources()) {
                Resource r = i.nextResource();
                strBuff.append(((String)r.getContent()));
            }
            return strBuff.toString();
            
		} catch (XMLDBException e) {
			log.error("Exception occured when trying to execute query for root collection: "+xQuery,e);
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.enabling.aas.ismock.IISStore#deleteArrayXML(java.lang.String[], java.lang.String)
	 */
	public boolean deleteArrayXML(List<String> fileNames, String fileColl) {
		if (fileNames==null || fileColl==null)
			return false;
		try {
			Collection col = DatabaseManager.getCollection(dbURI + fileColl);
			if (col==null) {
				log.error("Collection "+fileColl+" doesn't exist!");
				return false;
			}
			for (String fileName : fileNames) {
				if (fileName!=null) {
					Resource res=col.getResource(fileName);
					if (res!=null)
						col.removeResource(res);
				}
			}
			return true;
		} catch (XMLDBException e) {
			log.error("Exception occured when trying to remove "+
					fileNames.size()+ " resources from collection "+fileColl,e);
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.enabling.aas.ismock.IISStore#getFileNames(java.lang.String)
	 */
	public List<String> getFileNames(String fileColl) {
		if (fileColl==null)
			return null;
		try {
			Collection col = DatabaseManager.getCollection(dbURI + fileColl);
			if (col==null) {
				log.error("Collection "+fileColl+" doesn't exist!");
				return null;
			}
			return Arrays.asList(col.listResources());
		} catch (XMLDBException e) {
			log.error("Exception occured when trying to get list of resources from collection "+fileColl,e);
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.enabling.is.store.rmi.ISStoreService#getXMLbyQuery(java.lang.String)
	 */
	public String getXMLbyQuery(String query) {
		if (query==null)
			return null;
		try {
			Collection col = DatabaseManager.getCollection(dbURI + A2Constants.IS_COLLECTION_ROOT_PATH);
			if (col==null) {
				log.error("Couldn't find root db collection!");
				return null;
			}
			XPathQueryService service =
                (XPathQueryService) col.getService("XPathQueryService", "1.0");
            service.setProperty("indent", "no");
            ResourceSet result = service.query(query);
            ResourceIterator i = result.getIterator();
            StringBuffer strBuff = new StringBuffer();
            while(i.hasMoreResources()) {
                Resource r = i.nextResource();
                strBuff.append((String)r.getContent());
            }
            return strBuff.toString();
			
		} catch (XMLDBException e) {
			log.error("Exception occured when trying to execute query for root collection: "+query,e);
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.enabling.is.store.rmi.ISStoreService#updateXML(java.lang.String, java.lang.String, java.lang.String)
	 */
	public boolean updateXML(String fileName, String fileColl, String fileContent) {
		if (fileName==null || fileColl==null || fileContent==null)
			return false;
		try {
			Collection col = DatabaseManager.getCollection(dbURI + fileColl);
			if (col==null) {
				log.error("Collection "+fileColl+" doesn't exist!");
				return false;
			}
			col.setProperty(OutputKeys.INDENT, "no");
			XMLResource res = (XMLResource)col.getResource(fileName);
			if (res==null) {
				log.error("Resource "+fileName+" doesn't exist in collection "+fileColl+"!");
				return false;
			}
			res.setContent(fileContent);
			col.storeResource(res);
			return true;
			
		} catch (XMLDBException e) {
			log.error("Exception occured when trying to get "+fileName+" from collection "+fileColl,e);
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.enabling.is.store.rmi.ISStoreService#executeXUpdate(java.lang.String)
	 */
	public boolean executeXUpdate(String query) throws ISStoreException {
		throw new UnimplementedException();
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.enabling.is.store.rmi.ISStoreService#quickSearchXML(java.lang.String)
	 */
	public List<String> quickSearchXML(String query) throws ISStoreException {
		throw new UnimplementedException();
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.enabling.is.store.rmi.ISStoreService#reindex()
	 */
	public boolean reindex() {
		throw new UnimplementedException();
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.enabling.is.store.rmi.ISStoreService#sync()
	 */
	public boolean sync() {
		throw new UnimplementedException();
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.common.rmi.BaseService#identify()
	 */
	public String identify() {
		return IDENTIFY;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.common.rmi.BaseService#notify(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
	 */
	public void notify(String subscriptionId, String topic, String isId,
			String message) {
		throw new UnimplementedException();
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.common.rmi.BaseService#start()
	 */
	public void start() {
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.enabling.is.store.rmi.ISStoreService#backup()
	 */
	@Override
	public String backup() throws ISStoreException {
		throw new UnimplementedException();
	}
	
	/**
	 * Encapsulates rsId in dummy {@link W3CEndpointReference} object.
	 * @param rsId
	 * @return
	 */
	W3CEndpointReference wrapResultSetId(String rsId) {
		return new CxfEndpointReferenceBuilder().getEndpointReference(localEndpoint, rsId);
	}
	
	/**
	 * Emulates ISLookUp by wrapping RSId.
	 * @param rsId
	 * @return wrapper RSId
	 */
	String wrapResultSetId_old(String rsId) {
		return "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
		"<wsa:EndpointReference xmlns:wsa=\"http://www.driver.org/schema\" xmlns:driver=\"http://www.driver.org\" xmlns:wsaw=\"http://www.w3.org/2006/02/addressing/wsdl\" xmlns:wsdl=\"http://www.w3.org/2005/08/wsdl-instance\">" +
		"<wsa:Address>http://146.48.87.147:8000/SOAP/ResultSet</wsa:Address>" +
		"<wsa:ReferenceParameters>" +
		"<driver:ResourceIdentifier>" +
		rsId +
		"</driver:ResourceIdentifier>" +
		"</wsa:ReferenceParameters>" +
		"<wsa:Metadata wsdl:wsdlLocation=\"http://146.48.87.147:8000/SOAP/ResultSet?WSDL\">" +
		"<wsaw:ServiceName>theResultSet</wsaw:ServiceName>" +
		"</wsa:Metadata>" +
		"</wsa:EndpointReference>";
		
	}
	
	/**
	 * Returns xml database URI.
	 * @return xml database URI
	 */
	public String getDbURI() {
		return dbURI;
	}

	/**
	 * Sets  xml database URI.
	 * @param dbURI
	 */
	public void setDbURI(String dbURI) {
		this.dbURI = dbURI;
	}
	
	/** 
	 * Sets xmlDbDriver
	 * @param xmlDbDriver
	 */
	public void setXmlDbDriver(String xmlDbDriver) {
		this.xmlDbDriver = xmlDbDriver;
	}

	/**
	 * Returns resultSet service.
	 * @return resultSet service
	 */
	public ResultSetService getResultSetService() {
		return resultSetService;
	}

	/**
	 * Sets resultSet service.
	 * @param resultSetService
	 */
	public void setResultSetService(ResultSetService resultSetService) {
		this.resultSetService = resultSetService;
	}

}
