package com.nutiteq.services;

import com.mgmaps.utils.Tools;
import com.nutiteq.cache.Cache;
import com.nutiteq.components.KmlPlace;
import com.nutiteq.components.WgsBoundingBox;
import com.nutiteq.components.WgsPoint;
import com.nutiteq.core.MappingCore;
import com.nutiteq.io.ResourceDataWaiter;
import com.nutiteq.io.ResourceRequestor;
import com.nutiteq.kml.JsonKmlReader;
import com.nutiteq.kml.KmlElementsWaiter;
import com.nutiteq.kml.KmlService;
import com.nutiteq.log.Log;
import com.nutiteq.task.TasksRunner;
import com.nutiteq.utils.Utils;

/**
 * <p>
 * Default implementation for geocoding services. Uses default Nutiteq backend
 * for searching.
 * </p>
 * 
 * <p>
 * Additional search parameters can be added to search URL by appending these to
 * baseurl used in service creation.
 * </p>
 */
public class CloudMadeGeocodingService implements Service, KmlElementsWaiter,
        KmlService, ResourceRequestor, ResourceDataWaiter {
    private static final String ERROR_MESSAGE_START = "Error:";
    public static final int NETWORK_ERROR = 0;
    public static final int NOT_FOUND = 404;
    public static final int SERVER_ERROR = 500;

    private static final int ERROR_RESPONSE_LENGTH = 9;
    /**
     * Search type for geocoding (place search)
     */
    public static final String SEARCH_TYPE_GEOCODING = "q";
    /**
     * Search type for POI search
     */
    public static final String SEARCH_TYPE_POI = "l";

    private final String searchUrl;
    private final int numberOfElements;

    public static final String SERVICE_URL_BASE = "http://geocoding.cloudmade.com/BC9A493B41014CAABB98F0471D759707/geocoding/v2/find.js";
    private final GeocodingResultWaiter resultWaiter;
    private boolean stopped;

    /**
     * Uses CloudMade HTTP geocoding API
     * 
     * @param resultWaiter
     *            object waiting for search results
     * @param baseurl
     *            baseurl for service
     * @param lang
     *            search language
     * @param searchNear
     *            location for searching (in WGS84)
     * @param query
     *            query
     * @param searchType
     *            search type (place search or POI search)
     * @param categories
     *            possible categories for search
     * @param numberOfElements
     *            number of elements to ask from server
     * @param gzipResponse
     *            should response be compressed in server. Adds gzip=(yes|no) to
     *            end of url. Not implemented in server yet, so it is ignored
     */
    public CloudMadeGeocodingService(
            final GeocodingResultWaiter resultWaiter,
            final String baseurl, 
            final String lang, 
            final WgsPoint searchNear,
            final String query, 
            final String searchType,
            final int[] categories, 
            final int numberOfElements,
            final boolean gzipResponse) {
        this.resultWaiter = resultWaiter;
        this.numberOfElements = numberOfElements;
        final StringBuffer url = new StringBuffer(Utils
                .prepareForParameters(baseurl));

        url.append("&query=").append(Tools.urlEncode(query));
        url.append("&return_location=true");
        searchUrl = url.toString();
        Log.debug("CloudMade url="+searchUrl);
    }

    public void execute() {
        enqueueDownload(MappingCore.getInstance().getTasksRunner());
    }

    protected void enqueueDownload(final TasksRunner d) {
        d.enqueueDownload(this, Cache.CACHE_LEVEL_NONE);
    }

    public String resourcePath() {
        return searchUrl;
    }

    public void notifyError() {
        resultWaiter.errors(NETWORK_ERROR);
    }

    public void dataRetrieved(final byte[] data) {
        if (stopped) {
            return;
        }

        if (data.length < 4) {
                resultWaiter.errors(CloudMadeGeocodingService.NOT_FOUND);
                return;
            }
        
        final JsonKmlReader reader = new JsonKmlReader(this, this, searchUrl);
        
        reader.dataRetrieved(data);
    }

    public void addKmlPlaces(final KmlService service, final KmlPlace[] read) {

        resultWaiter.searchResults(read);
    }

    public String getServiceUrl(final WgsBoundingBox boundingBox, final int zoom) {
        return searchUrl;
    }

    public int maxResults() {
        return numberOfElements;
    }

    public boolean needsUpdate(final WgsBoundingBox boundingBox, final int zoom) {
        return false;
    }

    public void cancel() {
        stopped = true;
    }

    public int getCachingLevel() {
        return Cache.CACHE_LEVEL_NONE;
    }

    public String getDefaultIcon() {
        // TODO Auto-generated method stub
        return null;
    }
}

