package org.gcube.portlets.user.gisviewer.server;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.gcube.portlets.user.gisviewer.client.Constants;
import org.gcube.portlets.user.gisviewer.client.GisViewerService;
import org.gcube.portlets.user.gisviewer.client.commons.beans.DataResult;
import org.gcube.portlets.user.gisviewer.client.commons.beans.GeoInformationForWMSRequest;
import org.gcube.portlets.user.gisviewer.client.commons.beans.GisViewerBaseLayerInterface;
import org.gcube.portlets.user.gisviewer.client.commons.beans.LayerItem;
import org.gcube.portlets.user.gisviewer.client.commons.beans.Property;
import org.gcube.portlets.user.gisviewer.client.commons.beans.Styles;
import org.gcube.portlets.user.gisviewer.client.commons.beans.TransectParameters;
import org.gcube.portlets.user.gisviewer.client.commons.beans.WebFeatureTable;
import org.gcube.portlets.user.gisviewer.client.commons.beans.WmsRequest;
import org.gcube.portlets.user.gisviewer.client.commons.beans.ZAxis;
import org.gcube.portlets.user.gisviewer.server.datafeature.ClickDataParser;
import org.gcube.portlets.user.gisviewer.server.datafeature.FeatureParser;
import org.gcube.portlets.user.gisviewer.server.datafeature.FeatureTypeParser;
import org.gcube.portlets.user.gisviewer.server.datafeature.wfs.FeatureExporterFileConfig;
import org.gcube.portlets.user.gisviewer.server.datafeature.wfs.WFSExporter;
import org.gcube.portlets.user.gisviewer.server.datafeature.wfs.WFSExporter.WFS_TO;
import org.gcube.portlets.user.gisviewer.shared.FeatureExportViewConfig;
import org.gcube.spatial.data.geoutility.GeoNcWMSMetadataUtility;
import org.gcube.spatial.data.geoutility.bean.LayerStyles;
import org.gcube.spatial.data.geoutility.bean.LayerZAxis;
import org.gcube.spatial.data.geoutility.bean.WmsParameters;

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

/**
 * The Class GisViewerServiceImpl.
 * @author Ceras
 * @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it
 * Jan 21, 2016
 */
public abstract class GisViewerServiceImpl extends RemoteServiceServlet implements GisViewerService {

	private static final long serialVersionUID = -2481133840394575925L;
	private static Logger logger = Logger.getLogger(GisViewerServiceImpl.class);
	protected static final boolean BASE_LAYER = true;
//	protected static final GeoserverMethodResearch researchMethod = GeoserverMethodResearch.MOSTUNLOAD;
//	private Map<String, GeoCaller> geoCallersMap = new HashMap<String, GeoCaller>();

	/**
	 * Gets the parameters.
	 *
	 * @return the parameters
	 * @throws Exception the exception
	 */
	protected abstract GisViewerServiceParameters getParameters() throws Exception;

	/**
	 * Gets the base layers to add gis viewer.
	 *
	 * @return the base layers to add gis viewer
	 * @throws Exception the exception
	 */
	protected abstract List<? extends GisViewerBaseLayerInterface> getBaseLayersToAddGisViewer()throws Exception;


	/* (non-Javadoc)
	 * @see org.gcube.portlets.user.gisviewer.client.GisViewerService#getGcubeSecurityToken()
	 */
	public abstract String getGcubeSecurityToken();

	/* (non-Javadoc)
	 * @see org.gcube.portlets.user.gisviewer.client.GisViewerService#getDataResult(java.util.List)
	 */
	@Override
	@Deprecated
	public List<DataResult> getDataResult(List<String> urls) {
		//THIS METHOD IS ORPHAN. IT SHOULD NOT BE CALLED FROM THIS CODE
		List<DataResult> result = new ArrayList<DataResult>();

		for (String url : urls) {
			logger.info("Data point selection: "+url);
			List<DataResult> oneGeoserverResult = ClickDataParser.getDataResult(url); // TODO adjust url adding wms
			result.addAll(oneGeoserverResult);
		}

		return result;
	}

	/* (non-Javadoc)
	 * @see org.gcube.portlets.user.gisviewer.client.GisViewerService#parseWmsRequest(java.lang.String, java.lang.String)
	 */
	@Override
	public GeoInformationForWMSRequest parseWmsRequest(String wmsRequest, String layerName) throws Exception{
		return loadGeoInfoForWmsRequest(wmsRequest, layerName);
	}

	/**
	 * Load geo info for wms request.
	 *
	 * @param wmsRequest the wms request
	 * @param layerName the layer name
	 * @return the gis viewer wms valid parameters
	 * @throws Exception the exception
	 */
	public static GeoInformationForWMSRequest loadGeoInfoForWmsRequest(String wmsRequest, String layerName) throws Exception{
		try {
			GisViewerWMSUrlValidator validator = new GisViewerWMSUrlValidator(wmsRequest, layerName);
			String wmsServiceHost = validator.getWmsServiceHost();
			String validWMSRequest = validator.parseWMSRequest(true, true);
			layerName = validator.getLayerName();
			String versionWms = validator.getValueOfParsedWMSParameter(WmsParameters.VERSION);
			String crs = validator.getValueOfParsedWMSParameter(WmsParameters.CRS);
	//
			HashMap<String, String> mapWmsNotStandard = new HashMap<String, String>();

			if(validator.getMapWmsNoStandardParams()!=null){
				mapWmsNotStandard.putAll(validator.getMapWmsNoStandardParams());
			}
	//
			GeoNcWMSMetadataUtility geoGS = new GeoNcWMSMetadataUtility(validWMSRequest, 4000);
			//STYLES
			LayerStyles layerStyle  = geoGS.loadStyles();
			Map<String,String> mapNcWmsStyles = layerStyle.getMapNcWmsStyles()==null?new HashMap<String, String>(1):layerStyle.getMapNcWmsStyles();
			mapWmsNotStandard.putAll(mapNcWmsStyles);
			//MAP STYLES INTO GWT-SERIALIZABLE OBJECT
			Styles styles = new Styles(layerStyle.getGeoStyles(), layerStyle.getMapNcWmsStyles(), layerStyle.isNcWms());
			//ZAxis
			LayerZAxis layerZAxis = geoGS.loadZAxis();
			//MAP ZAXIS INTO GWT-SERIALIZABLE OBJECT
			ZAxis zAxis = layerZAxis!=null?new ZAxis(layerZAxis.getUnits(), layerZAxis.isPositive(), layerZAxis.getValues()):null;

			return new GeoInformationForWMSRequest(wmsServiceHost, validWMSRequest, layerName, versionWms, crs, mapWmsNotStandard, styles, styles.isNcWms(), zAxis);
		}
		catch (Exception e) {
			String msg = "An error occurred during wms request validation for layer: "+layerName;
			logger.error(msg,e);
			throw new Exception(msg);
		}
	}

	/* (non-Javadoc)
	 * @see org.gcube.portlets.user.gisviewer.client.GisViewerService#getBaseLayersToGisViewer()
	 */
	@Override
	public List<? extends GisViewerBaseLayerInterface> getBaseLayersToGisViewer() {

		try {

			return getBaseLayersToAddGisViewer();
		} catch (Exception e) {
			e.printStackTrace();
			return new DefaultGisViewerServiceImpl().getBaseLayersToGisViewer();
		}
	}

	/**
	 * Sets the default opacity.
	 *
	 * @param layerItem the new default opacity
	 */
	private void setDefaultOpacity(LayerItem layerItem) {
		if (layerItem.getOpacity()<0) {
			layerItem.setOpacity(Constants.defaultOpacityLayers);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public TransectParameters getTransectParameters() {
		try {
			GisViewerServiceParameters prm = getParameters();
			TransectParameters tp = new TransectParameters(prm.getTransectUrl(), prm.getScope());
			logger.info("getTransectParameters returning: "+tp);
			return tp;
		} catch (Exception e) {
			logger.error("Error on getting transect parameters",e);
			return null;
		}
	}

	/* (non-Javadoc)
	 * @see org.gcube.portlets.user.gisviewer.client.GisViewerService#getDataResult(java.util.List, java.lang.String, int)
	 */
	@Override
	public List<WebFeatureTable> getDataResult(List<LayerItem> layerItems, String bbox, int maxWFSFeature, int zoomLevel) {
		String dataMinerURL = "";
		try {
			GisViewerServiceParameters parameters = getParameters();
			dataMinerURL = parameters.getDataMinerUrl();
		}
		catch (Exception e) {
			logger.error("Error on retrieving DataMiner URL from parameters, returning empty list of features");
			return new ArrayList<WebFeatureTable>();
		}
		List<WebFeatureTable> result = FeatureParser.getDataResults(layerItems, bbox, maxWFSFeature, getGcubeSecurityToken(), dataMinerURL, zoomLevel);
		return result;
	}


	/* (non-Javadoc)
	 * @see org.gcube.portlets.user.gisviewer.client.GisViewerService#getListProperty(java.lang.String, org.gcube.portlets.user.gisviewer.client.commons.beans.LayerItem)
	 */
	@Override
	public List<Property> getListProperty(String geoserverUrl, LayerItem layer){
		return FeatureTypeParser.getProperties(geoserverUrl, layer.getLayer());
	}

	/**
	 * Parses the wms request.
	 *
	 * @param request the request
	 * @return a WMS request
	 * @throws Exception the exception
	 */
	@Override
	public String parseWmsRequest(WmsRequest request) throws Exception{
		try {
			return MapGeneratorUtils.createWmsRequest(request);
		} catch (Exception e) {
			logger.error("Error on creating wms request",e);
			throw new Exception("Sorry, an error occurred when creating wms request, try again later");
		}
	}
	
	@Override
	public Map<String, FeatureExportViewConfig> getWFSToViewExporterMap(){
		logger.debug("getWFSToViewExporterMap called");
		WFSExporter exporter = new WFSExporter();
		FeatureExporterFileConfig wfsExport = exporter.getWFSExporterConfigTo(WFS_TO.VIEW);
		return wfsExport.getMap();
		
	}

}
