/**
 * Copyright 2008-2009 DRIVER PROJECT (Bielefeld University)
 * Original author: Marek Imialek <marek.imialek at uni-bielefeld.de>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package eu.dnetlib.data.sts.lls;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.cxf.helpers.IOUtils;
import org.apache.log4j.Logger;

import pl.edu.icm.yadda.service2.ArchiveContentPartFacade;
import pl.edu.icm.yadda.service2.YaddaObjectID;
import pl.edu.icm.yadda.service2.catalog.CountingIterator;
import pl.edu.icm.yadda.service2.exception.ServiceException;
import pl.edu.icm.yadda.service3.ArchiveObject2Meta;
import pl.edu.icm.yadda.service3.ArchiveObjectFacade;
import pl.edu.icm.yadda.service3.archive.IArchiveFacade2;
import eu.dnetlib.data.sts.das.DataAccessServiceException;
import eu.dnetlib.data.sts.das.lls.ILLSAccessFacade;
import eu.dnetlib.data.sts.das.lls.LLSLookUpResponse;

/**
 * @author <a href="mailto:marek.imialek at uni-bielefeld.de">Marek Imialek</a>
 *
 */
public class LLSAccessFacade implements ILLSAccessFacade {

	/** The Constant log. */
	protected static final Logger log = Logger.getLogger(LLSAccessFacade.class);

	/** The Constant oss protocol. */
	public static final String protocol = "yar://";
	
	/** The lls access facade. */
	private IArchiveFacade2 llsArchiveFacade;

	/** The initial page size. */
	private int initialPageSize;

	
	public LLSLookUpResponse searchObjects (String storeDataStructureId,
			String structureSubtype, int startPosition, 
			int initialPageSize, boolean returnUris) 
			throws DataAccessServiceException {
		
		String searchedPath = protocol+ storeDataStructureId + "/"
				+ structureSubtype;
		log.info("Srearching Storage for: " + searchedPath);
		
		LLSLookUpResponse llsLookUpResponse = new LLSLookUpResponse();
		CountingIterator<ArchiveObject2Meta> response = null;
	
		try {
			response = 
				llsArchiveFacade.queryObjects(searchedPath, true);
		} catch (ServiceException e) {
			String msg = "Failed to query object" + searchedPath;
			log.error(msg);
			throw (DataAccessServiceException) new DataAccessServiceException(
				msg).initCause(e);
		}
		
		if (response == null || response.count() == 0){
			log.warn("No objects found in the storage!");
			return null;
		}
		
		List<String> results = new ArrayList<String>();

		if (response.hasNext()) {	
			
			ArchiveObject2Meta object = response.next();
			ArchiveObjectFacade foundObject = null;
			
			try {
				
				foundObject = 
					llsArchiveFacade.getObject(
							object.getId(), 
							new String[]{"BasicContent"}, 
							true);	
			} catch (ServiceException e) {
				String msg = " Exception occured when try get object " 
					+object.getId() +" from the storage : "+e;
				log.error(msg);
					
			}
			
			Map<String, List<YaddaObjectID>> children = foundObject.getChildren();
			
			Iterator<String> chIter = children.keySet().iterator(); 
			llsLookUpResponse.setResponseSize(children.size());
			int counter = 1;
			
			while (chIter.hasNext() && counter<=initialPageSize) {
				
				if (counter < startPosition)
					continue;
				
				ArchiveObjectFacade foundSingleObject = null;
				counter++;
				
				try {
					String key = chIter.next();
					List<YaddaObjectID> values = children.get(key);
					Iterator<YaddaObjectID> iter = values.iterator();
					while (iter.hasNext()){
						YaddaObjectID value = iter.next();
					
						foundSingleObject = 
							llsArchiveFacade.getObject(
									value, 
									new String[]{"BasicContent"}, 
									false);
					}
			
				} catch (ServiceException e) {
					String msg = " Exception occured when try get object " 
						+object.getId() +" from the storage : "+e;
					log.error(msg);
					
				}
			
				ArchiveContentPartFacade contentPartFacade = 
					foundSingleObject.getPart("BasicContent");
		
				if (returnUris){
					log.debug("Object Path: " + contentPartFacade.getPath());
					if (contentPartFacade.getPath() != null)
						results.add(contentPartFacade.getPath());
				} else {
					log.debug("Converting InputStream to String.");
					String convertedObject = convertObject(contentPartFacade);
					if (convertedObject != null)
						results.add(convertedObject);
					else 
						log.error("Object " +
								"not converted from InputStream to String");
				}
				
			}
			
			log.debug("Finished");
			
		}
		
		if (results == null){
			log.warn("No objects found in the storage!");
			return null;
		}
		else {
			llsLookUpResponse.setResponseResults(results);
			return llsLookUpResponse;
		}
	}

	public LLSLookUpResponse searchObjectsByTags(
			String[] searchedTags, int startPosition, int initialpageSize,
			boolean returnUris) throws DataAccessServiceException {
		
		LLSLookUpResponse llsLookUpResponse = new LLSLookUpResponse();
		CountingIterator<ArchiveObject2Meta> response = null;
		
		try {
			response = 
				llsArchiveFacade.listObjects(null, null, searchedTags, false);
		} catch (ServiceException e) {
			throw (DataAccessServiceException) new DataAccessServiceException(
				"Failed to search storage").initCause(e);
		}

		if (response == null){
			log.warn("No objects found in the storage!");
			return null;
		}
		
		List<String> results = new ArrayList<String>();
		
		int counter = 1;
		llsLookUpResponse.setResponseSize(response.count());
		
		while (response.hasNext() && counter<=initialPageSize) {
			
			if (counter < startPosition)
				continue;
			
			ArchiveObject2Meta object = response.next();
			counter++;
			
			if (!object.getStatus().isDeleted()) {
				
				ArchiveObjectFacade foundObject = null;
				
				try {
					
					foundObject = 
						llsArchiveFacade.getObject(
								object.getId(), 
								new String[]{"BasicContent"}, 
								true);	
				} catch (ServiceException e) {
					String msg = " Exception occured when try get object " 
						+object.getId() +" from the storage : "+e;
					log.error(msg);
						
				}
				
				ArchiveContentPartFacade contentPartFacade = 
					foundObject.getPart("BasicContent");
				
				if (returnUris) {
					System.out.println("Object Path: " + contentPartFacade.getPath());
					if (contentPartFacade.getPath() != null)
						results.add(contentPartFacade.getPath());
				
				} else {
					
					InputStream is = contentPartFacade.getData();
					
					try {
						String objectToText = IOUtils.readStringFromStream(is);
						if (objectToText != null)
							results.add(objectToText);
						is.close();
					} catch (IOException e) {
						String msg = "Failed to convert object from " +
								"Inputstream to Text.";
						log.error(msg);
					} finally {
						try {
							is.close();
						} catch (IOException e) {
							log.error("Failed to close InputStream");
						}
					
					}
				}	
			}
				
		}
		
		if (results == null){
			log.warn("No objects found in the storage!");
			return null;
		}
		else {
			
			llsLookUpResponse.setResponseResults(results);
			return llsLookUpResponse;
		}
	}

	/**
	 * Get objects and covert them from InputStream to string.
	 * 
	 * @param contentPartFacade the content part facade
	 * 
	 * @return object string
	 */
	private String convertObject (ArchiveContentPartFacade contentPartFacade) {
		
		InputStream is = contentPartFacade.getData();
		String result = null;
		
		try {
			String objectToText = IOUtils.readStringFromStream(is);
			
			if (objectToText != null)
				result = objectToText;
			is.close();
		} catch (IOException e) {
			String msg = "Failed to convert object from " +
					"Inputstream to Text.";
			log.error(msg);
		} finally {
			try {
				is.close();
			} catch (IOException e) {
				log.error("Failed to close InputStream");
			}
		}
		
		return result;
	}
	
	public IArchiveFacade2 getLlsArchiveFacade() {
		return llsArchiveFacade;
	}

	public void setLlsArchiveFacade(IArchiveFacade2 llsArchiveFacade) {
		this.llsArchiveFacade = llsArchiveFacade;
	}

	public void setInitialPageSize(int initialPageSize) {
		this.initialPageSize = initialPageSize;
	}

	public int getInitialPageSize() {
		return initialPageSize;
	}
	
	
}
