package eu.dnetlib.enabling.ui.server;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternUtils;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService;
import eu.dnetlib.enabling.is.registry.rmi.ISRegistryException;
import eu.dnetlib.enabling.is.registry.rmi.ISRegistryService;
import eu.dnetlib.enabling.tools.ServiceLocator;
import eu.dnetlib.enabling.ui.common.beans.DHNInfo;
import eu.dnetlib.enabling.ui.common.beans.RepositoryDetailsInfo;
import eu.dnetlib.enabling.ui.common.services.MyGwtException;
import eu.dnetlib.enabling.ui.common.services.RegistryService;
import eu.dnetlib.enabling.ui.server.util.ResourceSubmitter;

public class RegistryServlet extends RemoteServiceServlet implements RegistryService, ResourceLoaderAware {

	private static final long serialVersionUID = 8541042711174183040L;
	private ServiceLocator<ISRegistryService> isRegistryLocator;
	private ServiceLocator<ISLookUpService> isLookUpLocator;
	private ResourceSubmitter resourceSubmitter;

	private static final Log log = LogFactory.getLog(RegistryServlet.class); // NOPMD by marko on 11/24/08 5:02 PM

	/**
	 * resource loader.
	 */
	private ResourceLoader resourceLoader;

	public RegistryServlet() {
		super();
	}

	public ServiceLocator<ISRegistryService> getIsRegistryLocator() {
		return isRegistryLocator;
	}

	@Required
	public void setIsRegistryLocator(final ServiceLocator<ISRegistryService> isRegistryLocator) {
		this.isRegistryLocator = isRegistryLocator;
	}

	public ServiceLocator<ISLookUpService> getIsLookUpLocator() {
		return isLookUpLocator;
	}

	@Required
	public void setIsLookUpLocator(final ServiceLocator<ISLookUpService> isLookUpLocator) {
		this.isLookUpLocator = isLookUpLocator;
	}

	@Override
	public Boolean deleteProfile(final String id) {
		try {
			return isRegistryLocator.getService().deleteProfile(id);
		} catch (final Exception e) {
			return false;
		}
	}

	@Override
	public Boolean removeAllProfiles(final List<String> list) {
		final ISRegistryService registry = isRegistryLocator.getService();
		for (final String profId : list) {
			try {
				registry.deleteProfile(profId);
			} catch (final Exception e) {
			}
		}
		return true;
	}

	@Override
	public String invalidateProfile(final String id) {
		try {
			return isRegistryLocator.getService().invalidateProfile(id);
		} catch (final Exception e) {
			return null;
		}
	}

	@Override
	public String validateProfile(final String id) {
		try {
			return isRegistryLocator.getService().validateProfile(id);
		} catch (final Exception e) {
			return null;
		}
	}

	@Override
	public String importProfiles(final String path) throws MyGwtException {
		log.info("importing profiles in " + path);

		int cOk = 0;
		int cTotal = 0;

		try {
			final File dirDone = new File(path + "/done_" + UUID.randomUUID());
			boolean doneDirCreated = false;

			if (!(new File(path)).isDirectory())
				return "ERROR: invalid directory";

			for (final Resource res : ResourcePatternUtils.getResourcePatternResolver(resourceLoader).getResources("file://" + path + "/*")) {
				if (res.getFile().isFile()) {
					cTotal++;
					if (resourceSubmitter.registerFile(res)) {
						if (!doneDirCreated)
							doneDirCreated = dirDone.mkdirs();

						// move the file only if we could create the dest dir
						if (doneDirCreated)
							res.getFile().renameTo(new File(dirDone, res.getFile().getName()));
						cOk++;
					}
				}
			}
		} catch (final IOException e) {
			log.info("got some exception: " + e);
			throw new MyGwtException("cannot find profiles to import", e.toString());
		}

		return cOk + "/" + cTotal;
	}

	@Override
	public Boolean uploadDefaultSchemas() {
		return resourceSubmitter.submitDefaultSchemas();
	}

	@Override
	public Boolean updateDHN(final DHNInfo info) throws MyGwtException {
		final String textProps = "<SERVICE_PROPERTIES>" + "<PROPERTY key='name' value='" + info.getName() + "'/>" + "<PROPERTY key='latitude' value='"
		+ info.getLat() + "'/>" + "<PROPERTY key='longitude' value='" + info.getLng() + "'/>" + "<PROPERTY key='timezone' value='"
		+ info.getTimezone() + "'/>" + "</SERVICE_PROPERTIES>";

		try {
			return isRegistryLocator.getService().updateProfileNode(info.getIdentifier(), "//SERVICE_PROPERTIES", textProps);
		} catch (final ISRegistryException e) {
			throw new MyGwtException(e.getMessage(), "");
		}
	}

	@Override
	public String addRepository(RepositoryDetailsInfo repo) throws MyGwtException {

		try {
			SAXReader reader = new SAXReader();
			Document doc = reader.read(getClass().getResourceAsStream("repository-tmpl.xml"));

			applyRepositoryDetails(doc, repo);

			return isRegistryLocator.getService().insertProfileForValidation("RepositoryServiceResourceType", doc.asXML());
		} catch (Exception e) {
			log.error("Error adding repository", e);
			throw new MyGwtException(e.getMessage(), "");
		}
	}

	@Override
	public void updateRepository(String id, RepositoryDetailsInfo repo) throws MyGwtException {
		try {
			String profileSource = isLookUpLocator.getService().getResourceProfile(id);
			SAXReader reader = new SAXReader();
			Document doc = reader.read(new StringReader(profileSource));

			applyRepositoryDetails(doc, repo);

			isRegistryLocator.getService().updateProfile(id, doc.asXML(), "RepositoryServiceResourceType");
		} catch (Exception e) {
			log.error("Error adding repository", e);
			throw new MyGwtException(e.getMessage(), "");
		}
	}

	/**
	 * Applies all values from a repo details info bean into a repository profile DOM, overwriting existing values but
	 * preserving information not represented in the info bean.
	 * 
	 * @param doc
	 *            repository profile DOM
	 * @param repo
	 *            repo details bean
	 */
	protected void applyRepositoryDetails(final Document doc, final RepositoryDetailsInfo repo) {
		float lng = repo.getLng();
		float lat = repo.getLat();

		if (lat == 0 && lng == 0) {
			try {
				Map<String,Float> geoInfo = obtainGeoGraphichInfoForURL(repo.getBaseUrl());			
				if (geoInfo.containsKey("latitude")) 
					lat = geoInfo.get("latitude");
				if (geoInfo.containsKey("longitude"))
					lng = geoInfo.get("longitude");
			} catch (Exception e) {
				log.warn("Failed discovery of Geographic Info");
			}
		}

		// Official Name
		doc.selectSingleNode("//OFFICIAL_NAME").setText(repo.getName());
		// English Name
		doc.selectSingleNode("//ENGLISH_NAME").setText(repo.getEnglishName());
		// Repository Institution
		doc.selectSingleNode("//REPOSITORY_INSTITUTION").setText(repo.getInstitution());
		// Repository Icon
		doc.selectSingleNode("//ICON_URI").setText(repo.getIcon());
		// Repository E-MAIL
		doc.selectSingleNode("//ADMIN_INFO").setText(repo.getEmail());
		// Repository website
		doc.selectSingleNode("//REPOSITORY_WEBPAGE").setText(repo.getUrl());
		// Repository aggregator
		final Node aggregatorNode = doc.selectSingleNode("//EXTRA_FIELDS/FIELD[key='aggregatorName']/value");
		if(aggregatorNode != null)
			aggregatorNode.setText(repo.getAggregator());
		// Platform
		doc.selectSingleNode("//TYPOLOGY").setText(repo.getPlatform());
		// OAI BaseURL
		doc.selectSingleNode("//BASE_URL").setText(repo.getBaseUrl());
		// Country
		doc.selectSingleNode("//COUNTRY").setText(repo.getCountry());
		// Longitude
		doc.selectSingleNode("//LONGITUDE").setText(Float.toString(lng));
		// Latitude
		doc.selectSingleNode("//LATITUDE").setText(Float.toString(lat));
		// Timezone
		doc.selectSingleNode("//TIMEZONE").setText(Float.toString(repo.getTimezone()));
	}

	protected Map<String, Float> obtainGeoGraphichInfoForURL(String address) throws IOException {
		URL geoServerURL = new URL("http://www.datasciencetoolkit.org/ip2coordinates/" + new URL(address).getHost());

		String json = "";
		BufferedReader br = new BufferedReader(new InputStreamReader(geoServerURL.openStream()));
		String str = "";
		while ((str = br.readLine()) != null) {
			json += str;
		}
		br.close();
		
		Map<String, Float> response = new HashMap<String, Float>();
		
		for (String s : json.replaceAll(".+\\{","").replaceAll("\\}.+","").split(",")) {
			if (s.contains("latitude")) {
				response.put("latitude", Float.valueOf(s.substring(s.indexOf(":") + 1)));
			} else if (s.contains("longitude")) {
				response.put("longitude", Float.valueOf(s.substring(s.indexOf(":") + 1)));
			}
		}
		return response;
	}

	@Override
	public void setResourceLoader(final ResourceLoader resourceLoader) {
		this.resourceLoader = resourceLoader;
	}

	public ResourceSubmitter getResourceSubmitter() {
		return resourceSubmitter;
	}

	@Required
	public void setResourceSubmitter(ResourceSubmitter resourceSubmitter) {
		this.resourceSubmitter = resourceSubmitter;
	}

}
