/*
 * Copyright (C) 2006 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.telephony;

import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
import android.os.Registrant;
import android.os.RegistrantList;
import android.telephony.ServiceState;

/**
 * {@hide}
 */
public abstract class ServiceStateTracker extends Handler {
    /**
     *  The access technology currently in use:
     *  0 = unknown
     *  1 = GPRS only
     *  2 = EDGE
     *  3 = UMTS
     */
    protected static final int DATA_ACCESS_UNKNOWN = 0;
    protected static final int DATA_ACCESS_GPRS = 1;
    protected static final int DATA_ACCESS_EDGE = 2;
    protected static final int DATA_ACCESS_UMTS = 3;
    protected static final int DATA_ACCESS_CDMA_IS95A = 4;
    protected static final int DATA_ACCESS_CDMA_IS95B = 5;
    protected static final int DATA_ACCESS_CDMA_1xRTT = 6;
    protected static final int DATA_ACCESS_CDMA_EvDo_0 = 7;
    protected static final int DATA_ACCESS_CDMA_EvDo_A = 8;
    //***** Instance Variables

    protected CommandsInterface cm;

    public ServiceState ss;
    protected ServiceState newSS;

    // Used as a unique identifier to track requests associated with a poll
    // and ignore stale responses.The value is a count-down of expected responses
    // in this pollingContext
    protected int[] pollingContext;
    protected boolean mDesiredPowerState;

    protected boolean dontPollSignalStrength = false; // Default is to poll strength
    // If we're getting unsolicited signal strength updates from the radio,
    // set value to true and don't bother polling any more

    protected RegistrantList networkAttachedRegistrants = new RegistrantList();
    protected RegistrantList roamingOnRegistrants = new RegistrantList();
    protected RegistrantList roamingOffRegistrants = new RegistrantList();

    //***** Constants

    protected  static final boolean DBG = true;

    // signal strength poll rate
    protected static final int POLL_PERIOD_MILLIS = 20 * 1000;

    // waiting period before recheck gprs and voice registration
    public static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000;

    public static final int MAX_NUM_DATA_STATE_READS = 15;
    public static final int DATA_STATE_POLL_SLEEP_MS = 100;

    //*****GSM events
    protected static final int EVENT_RADIO_STATE_CHANGED               = 1;
    protected static final int EVENT_NETWORK_STATE_CHANGED             = 2;
    protected static final int EVENT_GET_SIGNAL_STRENGTH               = 3;
    protected static final int EVENT_POLL_STATE_REGISTRATION           = 4;
    protected static final int EVENT_POLL_STATE_GPRS                   = 5;
    protected static final int EVENT_POLL_STATE_OPERATOR               = 6;
    protected static final int EVENT_POLL_SIGNAL_STRENGTH              = 10;
    protected static final int EVENT_NITZ_TIME                         = 11;
    protected static final int EVENT_SIGNAL_STRENGTH_UPDATE            = 12;
    protected static final int EVENT_RADIO_AVAILABLE                   = 13;
    protected static final int EVENT_POLL_STATE_NETWORK_SELECTION_MODE = 14;
    protected static final int EVENT_GET_LOC_DONE                      = 15;
    protected static final int EVENT_SIM_RECORDS_LOADED                = 16;
    protected static final int EVENT_SIM_READY                         = 17;
    protected static final int EVENT_LOCATION_UPDATES_ENABLED          = 18;
    protected static final int EVENT_GET_PREFERRED_NETWORK_TYPE        = 19;
    protected static final int EVENT_SET_PREFERRED_NETWORK_TYPE        = 20;
    protected static final int EVENT_RESET_PREFERRED_NETWORK_TYPE      = 21;
    protected static final int EVENT_CHECK_REPORT_GPRS                 = 22;
    protected static final int EVENT_RESTRICTED_STATE_CHANGED          = 23;

    //*****CDMA events:
    protected static final int EVENT_POLL_STATE_REGISTRATION_CDMA      = 24;
    protected static final int EVENT_POLL_STATE_OPERATOR_CDMA          = 25;
    protected static final int EVENT_RUIM_READY                        = 26;
    protected static final int EVENT_RUIM_RECORDS_LOADED               = 27;
    protected static final int EVENT_POLL_STATE_NETWORK_SELECTION_MODE_CDMA = 28;
    protected static final int EVENT_POLL_SIGNAL_STRENGTH_CDMA         = 29;
    protected static final int EVENT_GET_SIGNAL_STRENGTH_CDMA          = 30;
    protected static final int EVENT_NETWORK_STATE_CHANGED_CDMA        = 31;
    protected static final int EVENT_GET_LOC_DONE_CDMA                 = 32;
    protected static final int EVENT_SIGNAL_STRENGTH_UPDATE_CDMA       = 33;
    protected static final int EVENT_NV_LOADED                         = 34;

    //***** Time Zones
    protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone";

    // List of ISO codes for countries that can have an offset of GMT+0
    // when not in daylight savings time.  This ignores some small places
    // such as the Canary Islands (Spain) and Danmarkshavn (Denmark).
    // The list must be sorted by code.
    protected static final String[] GMT_COUNTRY_CODES = {
        "bf", // Burkina Faso
        "ci", // Cote d'Ivoire
        "eh", // Western Sahara
        "fo", // Faroe Islands, Denmark
        "gh", // Ghana
        "gm", // Gambia
        "gn", // Guinea
        "gw", // Guinea Bissau
        "ie", // Ireland
        "lr", // Liberia
        "is", // Iceland
        "ma", // Morocco
        "ml", // Mali
        "mr", // Mauritania
        "pt", // Portugal
        "sl", // Sierra Leone
        "sn", // Senegal
        "st", // Sao Tome and Principe
        "tg", // Togo
        "uk", // U.K
    };


    //***** Constructors
    public ServiceStateTracker() {

    }


    /**
     * Registration point for combined roaming on
     * combined roaming is true when roaming is true and ONS differs SPN
     *
     * @param h handler to notify
     * @param what what code of message when delivered
     * @param obj placed in Message.obj
     */
    public  void registerForRoamingOn(Handler h, int what, Object obj) {
        Registrant r = new Registrant(h, what, obj);
        roamingOnRegistrants.add(r);

        if (ss.getRoaming()) {
            r.notifyRegistrant();
        }
    }

    public  void unregisterForRoamingOn(Handler h) {
        roamingOnRegistrants.remove(h);
    }

    /**
     * Registration point for combined roaming off
     * combined roaming is true when roaming is true and ONS differs SPN
     *
     * @param h handler to notify
     * @param what what code of message when delivered
     * @param obj placed in Message.obj
     */
    public  void registerForRoamingOff(Handler h, int what, Object obj) {
        Registrant r = new Registrant(h, what, obj);
        roamingOffRegistrants.add(r);

        if (!ss.getRoaming()) {
            r.notifyRegistrant();
        }
    }

    public  void unregisterForRoamingOff(Handler h) {
        roamingOffRegistrants.remove(h);
    }

    /**
     * Reregister network through toggle perferred network type
     * This is a work aorund to deregister and register network since there is
     * no ril api to set COPS=2 (deregister) only.
     *
     * @param onComplete is dispatched when this is complete.  it will be
     * an AsyncResult, and onComplete.obj.exception will be non-null
     * on failure.
     */
    public void reRegisterNetwork(Message onComplete) {
        cm.getPreferredNetworkType(
                obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE, onComplete));
    }


    //***** Called from Phone
    public void
    setRadioPower(boolean power) {
        mDesiredPowerState = power;

        setPowerStateToDesired();
    }


    public void enableLocationUpdates() {
        cm.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
    }

    public void disableLocationUpdates() {
        cm.setLocationUpdates(false, null);
    }

    //***** Overridden from Handler
    public abstract void handleMessage(Message msg);

    //***** Protected abstract Methods
    protected abstract void handlePollStateResult(int what, AsyncResult ar);
    protected abstract void updateSpnDisplay();
    protected abstract void setPowerStateToDesired();

    /** Cancel a pending (if any) pollState() operation */
    protected void cancelPollState() {
        // This will effectively cancel the rest of the poll requests
        pollingContext = new int[1];
    }
}
