package com.shopizer.modules.shipping.distance;

import java.math.BigDecimal;
import java.util.List;
import java.util.Locale;

import org.apache.commons.lang.Validate;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.maps.DistanceMatrixApi;
import com.google.maps.GeoApiContext;
import com.google.maps.GeocodingApi;
import com.google.maps.model.Distance;
import com.google.maps.model.DistanceMatrix;
import com.google.maps.model.DistanceMatrixRow;
import com.google.maps.model.GeocodingResult;
import com.google.maps.model.LatLng;
import com.salesmanager.core.model.common.Delivery;
import com.salesmanager.core.model.merchant.MerchantStore;
import com.salesmanager.core.model.shipping.PackageDetails;
import com.salesmanager.core.model.shipping.ShippingConfiguration;
import com.salesmanager.core.model.shipping.ShippingOrigin;
import com.salesmanager.core.model.shipping.ShippingQuote;
import com.salesmanager.core.model.system.IntegrationConfiguration;
import com.salesmanager.core.model.system.IntegrationModule;
import com.salesmanager.core.modules.constants.Constants;
import com.salesmanager.core.modules.integration.IntegrationException;
import com.salesmanager.core.modules.integration.shipping.model.ShippingQuotePrePostProcessModule;

/**
 * Uses google api to get lng, lat and distance in km for a given delivery address
 * The route will be displayed on a map to the end user and available
 * from the admin section
 * 
 * The module can be configured to use miles by changing distance.inMeters
 * 
 * To use this pre-processor you will need a google api-key
 * 
 * Access google developers console
 * https://console.developers.google.com/project
 * 
 * Geocoding
 * Distance Matrix
 * Directions
 * 
 * Create new key for server application
 * Copy API key
 * https://console.developers.google.com
 * 
 * https://developers.google.com/maps/documentation/webservices/client-library
 * https://github.com/googlemaps/google-maps-services-java/tree/master/src/test/java/com/google/maps
 * 
 * @author carlsamson
 *
 */
public class ShippingDistancePreProcessorImpl implements ShippingQuotePrePostProcessModule {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(ShippingDistancePreProcessorImpl.class);
	
	private final static String BLANK = " ";
	
	private final static String MODULE_CODE = "shippingDistanceModule";

	
	private String apiKey;

	private List<String> allowedZonesCodes = null;



	public List<String> getAllowedZonesCodes() {
		return allowedZonesCodes;
	}





	public void setAllowedZonesCodes(List<String> allowedZonesCodes) {
		this.allowedZonesCodes = allowedZonesCodes;
	}





	public void prePostProcessShippingQuotes(ShippingQuote quote,
			List<PackageDetails> packages, BigDecimal orderTotal,
			Delivery delivery, ShippingOrigin origin, MerchantStore store,
			IntegrationConfiguration globalShippingConfiguration,
			IntegrationModule currentModule,
			ShippingConfiguration shippingConfiguration,
			List<IntegrationModule> allModules, Locale locale)
			throws IntegrationException {
		
		
		/** which destinations are supported by this module **/
		
		if(delivery.getZone()==null) {
			return;
		}
		
		boolean zoneAllowed = false;
		if(allowedZonesCodes!=null) {
			for(String zoneCode : allowedZonesCodes) {
				if(zoneCode.equals(delivery.getZone().getCode())) {
					zoneAllowed = true;
					break;
				}
			}
		}
		
		if(!zoneAllowed) {
			return;
		}
		
		if(StringUtils.isBlank(delivery.getPostalCode())) {
			return;
		}
		
		Validate.notNull(apiKey, "Requires the configuration of google apiKey");
		
		GeoApiContext context = new GeoApiContext().setApiKey(apiKey);
		
		//build origin address
		StringBuilder originAddress = new StringBuilder();
		
		originAddress.append(origin.getAddress()).append(BLANK)
		.append(origin.getCity()).append(BLANK)
		.append(origin.getPostalCode()).append(BLANK);
		
		if(!StringUtils.isBlank(origin.getState())) {
			originAddress.append(origin.getState()).append(" ");
		}
		if(origin.getZone()!=null) {
			originAddress.append(origin.getZone().getCode()).append(" ");
		}
		originAddress.append(origin.getCountry().getIsoCode());

		
		//build destination address
		StringBuilder destinationAddress = new StringBuilder();
		
		destinationAddress.append(delivery.getAddress()).append(BLANK);
		if(!StringUtils.isBlank(delivery.getCity())) {
			destinationAddress.append(delivery.getCity()).append(BLANK);
		}
		destinationAddress.append(delivery.getPostalCode()).append(BLANK);
		
		if(!StringUtils.isBlank(delivery.getState())) {
			destinationAddress.append(delivery.getState()).append(" ");
		}
		if(delivery.getZone()!=null) {
			destinationAddress.append(delivery.getZone().getCode()).append(" ");
		}
		destinationAddress.append(delivery.getCountry().getIsoCode());
		
		
		try {
			GeocodingResult[] originAdressResult =  GeocodingApi.geocode(context,
					originAddress.toString()).await();

			GeocodingResult[] destinationAdressResult =  GeocodingApi.geocode(context,
					destinationAddress.toString()).await();

			if(originAdressResult.length>0 && destinationAdressResult.length>0) {
				LatLng originLatLng = originAdressResult[0].geometry.location;
				LatLng destinationLatLng = destinationAdressResult[0].geometry.location;
				
				delivery.setLatitude(String.valueOf(destinationLatLng.lat));
				delivery.setLongitude(String.valueOf(destinationLatLng.lng));
				
				//keep latlng for further usage in order to display the map
	
				
				DistanceMatrix  distanceRequest = DistanceMatrixApi.newRequest(context)
		 		.origins(new LatLng(originLatLng.lat, originLatLng.lng))
		 		
		 		.destinations(new LatLng(destinationLatLng.lat, destinationLatLng.lng))
		 				.awaitIgnoreError();
				
				
				if(distanceRequest!=null) {
					DistanceMatrixRow distanceMax = distanceRequest.rows[0];
					Distance distance = distanceMax.elements[0].distance;
					quote.getQuoteInformations().put(Constants.DISTANCE_KEY, 0.001 * distance.inMeters);
				}

			}
		
		} catch (Exception e) {
			LOGGER.error("Exception while calculating the shipping distance",e);
		}

	}





	public String getApiKey() {
		return apiKey;
	}





	public void setApiKey(String apiKey) {
		this.apiKey = apiKey;
	}





	
	public String getModuleCode() {
		// TODO Auto-generated method stub
		return MODULE_CODE;
	}














}
