/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * 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 com.android.internal.location;

import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationProvider;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;

import java.util.ArrayList;
import java.util.HashMap;

/**
 * An abstract superclass for location provider implementations.
 * Location provider implementations are typically instantiated by the
 * location manager service in the system process, and location
 * information is made available to implementations via the manager.
 *
 * {@hide}
 */
public abstract class LocationProviderImpl extends LocationProvider {
    private static final String TAG = "LocationProviderImpl";

    private static ArrayList<LocationProviderImpl> sProviders =
        new ArrayList<LocationProviderImpl>();
    private static HashMap<String, LocationProviderImpl> sProvidersByName
        = new HashMap<String, LocationProviderImpl>();

    private final ILocationManager mLocationManager;
    private boolean mLocationTracking = false;
    private long mMinTime = 0;

    protected LocationProviderImpl(String name, ILocationManager locationManager) {
        super(name);
        mLocationManager = locationManager;
    }

    public static void addProvider(LocationProviderImpl provider) {
        sProviders.add(provider);
        sProvidersByName.put(provider.getName(), provider);
    }

    public static void removeProvider(LocationProviderImpl provider) {
        sProviders.remove(provider);
        sProvidersByName.remove(provider.getName());
    }

    public static ArrayList<LocationProviderImpl> getProviders() {
        return sProviders;
    }

    public static LocationProviderImpl getProvider(String name) {
        return sProvidersByName.get(name);
    }

    public void reportLocationChanged(Location location) {
        try {
            mLocationManager.setLocation(location);
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException calling ILocationManager.setLocation");
        }
    }
    
    /**
     * Enables this provider.  When enabled, calls to {@link #getStatus()}
     * must be handled.  Hardware may be started up
     * when the provider is enabled.
     */
    public abstract void enable();

    /**
     * Disables this provider.  When disabled, calls to {@link #getStatus()}
     * need not be handled.  Hardware may be shut
     * down while the provider is disabled.
     */
    public abstract void disable();

    /**
     * Returns true if this provider is enabled, false otherwise;
     */
    public abstract boolean isEnabled();

    /**
     * Returns a information on the status of this provider.
     * {@link #OUT_OF_SERVICE} is returned if the provider is
     * out of service, and this is not expected to change in the near
     * future; {@link #TEMPORARILY_UNAVAILABLE} is returned if
     * the provider is temporarily unavailable but is expected to be
     * available shortly; and {@link #AVAILABLE} is returned
     * if the provider is currently available.
     */
    public int getStatus() {
        return getStatus(null);
    }

    /**
     * Returns a information on the status of this provider.
     * {@link #OUT_OF_SERVICE} is returned if the provider is
     * out of service, and this is not expected to change in the near
     * future; {@link #TEMPORARILY_UNAVAILABLE} is returned if
     * the provider is temporarily unavailable but is expected to be
     * available shortly; and {@link #AVAILABLE} is returned
     * if the provider is currently available.
     *
     * <p> If extras is non-null, additional status information may be
     * added to it in the form of provider-specific key/value pairs.
     */
    public abstract int getStatus(Bundle extras);

    /**
     * Returns the time at which the status was last updated. It is the
     * responsibility of the provider to appropriately set this value
     * using {@link android.os.SystemClock.elapsedRealtime()} each time
     * there is a status update that it wishes to broadcast to all its
     * listeners. The provider should be careful not to broadcast
     * the same status again.
     *
     * @return time of last status update in millis since last reboot
     */
    public long getStatusUpdateTime() {
        return 0;
    }

    /**
     * Notifies the location provider that clients are listening for locations.
     * Called with enable set to true when the first client is added and
     * called with enable set to false when the last client is removed.
     * This allows the provider to prepare for receiving locations,
     * and to shut down when no clients are remaining.
     *
     * @param enable true if location tracking should be enabled.
     */
    public void enableLocationTracking(boolean enable) {
        mLocationTracking = enable;
    }

    /**
     * Returns true if the provider has any listeners
     *
     * @return true if provider is being tracked
     */
    public boolean isLocationTracking() {
        return mLocationTracking;
    }

    /**
     * Notifies the location provider of the smallest minimum time between updates amongst
     * all clients that are listening for locations.  This allows the provider to reduce
     * the frequency of updates to match the requested frequency.
     *
     * @param minTime the smallest minTime value over all listeners for this provider.
     */
    public void setMinTime(long minTime) {
        mMinTime = minTime;
    }

    /**
     * Gets the smallest minimum time between updates amongst all the clients listening
     * for locations. By default this value is 0 (as frqeuently as possible)
     *
     * @return the smallest minTime value over all listeners for this provider
     */
    public long getMinTime() {
        return mMinTime;
    }

    /**
     * Updates the network state for the given provider. This function must
     * be overwritten if {@link #requiresNetwork} returns true. The state is
     * {@link #TEMPORARILY_UNAVAILABLE} (disconnected), OR {@link #AVAILABLE}
     * (connected or connecting).
     *
     * @param state data state
     */
    public void updateNetworkState(int state) {
    }

    /**
     * Implements addditional location provider specific additional commands.
     *
     * @param command name of the command to send to the provider.
     * @param extras optional arguments for the command (or null).
     * The provider may optionally fill the extras Bundle with results from the command.
     *
     * @return true if the command succeeds. 
     */
    public boolean sendExtraCommand(String command, Bundle extras) {
        return false;
    }

    /**
     * Informs the location provider when a new client is listening for location information
     *
     * @param uid the uid of the client proces
     */
    public void addListener(int uid) {
    }

    /**
     * Informs the location provider when a client is no longer listening for location information
     *
     * @param uid the uid of the client proces
     */
    public void removeListener(int uid) {
    }

    /**
     * Informs the location provider when the location manager service has acquired its wake lock
     */
    public void wakeLockAcquired() {
    }

    /**
     * Informs the location provider when the location manager service has released its wake lock
     */
    public void wakeLockReleased() {
    }
}
