/*
 * Copyright (C) 2012 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.
 */

/*
 *  Adjust the controller's power states.
 */
#include "OverrideLog.h"
#include "PowerSwitch.h"
#include "NfcJniUtil.h"
#include "config.h"


namespace android
{
    void doStartupConfig ();
}

extern bool         gActivated;
extern SyncEvent    gDeactivatedEvent;

PowerSwitch PowerSwitch::sPowerSwitch;
const PowerSwitch::PowerActivity PowerSwitch::DISCOVERY=0x01;
const PowerSwitch::PowerActivity PowerSwitch::SE_ROUTING=0x02;
const PowerSwitch::PowerActivity PowerSwitch::SE_CONNECTED=0x04;
const PowerSwitch::PowerActivity PowerSwitch::HOST_ROUTING=0x08;

/*******************************************************************************
**
** Function:        PowerSwitch
**
** Description:     Initialize member variables.
**
** Returns:         None
**
*******************************************************************************/
PowerSwitch::PowerSwitch ()
:   mCurrLevel (UNKNOWN_LEVEL),
    mCurrDeviceMgtPowerState (NFA_DM_PWR_STATE_UNKNOWN),
    mExpectedDeviceMgtPowerState (NFA_DM_PWR_STATE_UNKNOWN),
    mDesiredScreenOffPowerState (0),
    mCurrActivity(0)
{
}


/*******************************************************************************
**
** Function:        ~PowerSwitch
**
** Description:     Release all resources.
**
** Returns:         None
**
*******************************************************************************/
PowerSwitch::~PowerSwitch ()
{
}


/*******************************************************************************
**
** Function:        getInstance
**
** Description:     Get the singleton of this object.
**
** Returns:         Reference to this object.
**
*******************************************************************************/
PowerSwitch& PowerSwitch::getInstance ()
{
    return sPowerSwitch;
}


/*******************************************************************************
**
** Function:        initialize
**
** Description:     Initialize member variables.
**
** Returns:         None
**
*******************************************************************************/
void PowerSwitch::initialize (PowerLevel level)
{
    static const char fn [] = "PowerSwitch::initialize";
    unsigned long num = 0;

    mMutex.lock ();

    ALOGD ("%s: level=%s (%u)", fn, powerLevelToString(level), level);
    if (GetNumValue (NAME_SCREEN_OFF_POWER_STATE, &num, sizeof(num)))
        mDesiredScreenOffPowerState = (int) num;
    ALOGD ("%s: desired screen-off state=%d", fn, mDesiredScreenOffPowerState);

    switch (level)
    {
    case FULL_POWER:
        mCurrDeviceMgtPowerState = NFA_DM_PWR_MODE_FULL;
        mCurrLevel = level;
        break;

    case UNKNOWN_LEVEL:
        mCurrDeviceMgtPowerState = NFA_DM_PWR_STATE_UNKNOWN;
        mCurrLevel = level;
        break;

    default:
        ALOGE ("%s: not handled", fn);
        break;
    }
    mMutex.unlock ();
}


/*******************************************************************************
**
** Function:        getLevel
**
** Description:     Get the current power level of the controller.
**
** Returns:         Power level.
**
*******************************************************************************/
PowerSwitch::PowerLevel PowerSwitch::getLevel ()
{
    PowerLevel level = UNKNOWN_LEVEL;
    mMutex.lock ();
    level = mCurrLevel;
    mMutex.unlock ();
    return level;
}


/*******************************************************************************
**
** Function:        setLevel
**
** Description:     Set the controller's power level.
**                  level: power level.
**
** Returns:         True if ok.
**
*******************************************************************************/
bool PowerSwitch::setLevel (PowerLevel newLevel)
{
    static const char fn [] = "PowerSwitch::setLevel";
    bool retval = false;

    mMutex.lock ();

    ALOGD ("%s: level=%s (%u)", fn, powerLevelToString(newLevel), newLevel);
    if (mCurrLevel == newLevel)
    {
        retval = true;
        goto TheEnd;
    }

    if (mCurrLevel == UNKNOWN_LEVEL)
    {
        ALOGE ("%s: unknown power level", fn);
        goto TheEnd;
    }

    if ( (mCurrLevel == LOW_POWER && newLevel == FULL_POWER) ||
         (mCurrLevel == FULL_POWER && newLevel == LOW_POWER) )
    {
        mMutex.unlock ();
        SyncEventGuard g (gDeactivatedEvent);
        if (gActivated)
        {
            ALOGD("%s: wait for deactivation", fn);
            gDeactivatedEvent.wait ();
        }
        mMutex.lock ();
    }

    switch (newLevel)
    {
    case FULL_POWER:
        if (mCurrDeviceMgtPowerState == NFA_DM_PWR_MODE_OFF_SLEEP)
            retval = setPowerOffSleepState (false);
        break;

    case LOW_POWER:
    case POWER_OFF:
        if (isPowerOffSleepFeatureEnabled())
            retval = setPowerOffSleepState (true);
        else if (mDesiredScreenOffPowerState == 1) //.conf file desires full-power
        {
            mCurrLevel = FULL_POWER;
            retval = true;
        }
        break;

    default:
        ALOGE ("%s: not handled", fn);
        break;
    }

    ALOGD("%s: actual power level=%s", fn, powerLevelToString(mCurrLevel));

TheEnd:
    mMutex.unlock ();
    return retval;
}


bool PowerSwitch::setScreenOffPowerState (ScreenOffPowerState newState)
{
    ALOGD ("PowerSwitch::setScreenOffPowerState: level=%s (%u)",
        screenOffPowerStateToString(newState), newState);

    mMutex.lock ();
    mDesiredScreenOffPowerState = (int) newState;
    mMutex.unlock ();

    return true;
}


/*******************************************************************************
**
** Function:        setModeOff
**
** Description:     Set a mode to be deactive.
**
** Returns:         True if any mode is still active.
**
*******************************************************************************/
bool PowerSwitch::setModeOff (PowerActivity deactivated)
{
    bool retVal = false;

    mMutex.lock ();
    mCurrActivity &= ~deactivated;
    retVal = mCurrActivity != 0;
    ALOGD ("PowerSwitch::setModeOff(deactivated=0x%x) : mCurrActivity=0x%x", deactivated, mCurrActivity);
    mMutex.unlock ();
    return retVal;
}


/*******************************************************************************
**
** Function:        setModeOn
**
** Description:     Set a mode to be active.
**
** Returns:         True if any mode is active.
**
*******************************************************************************/
bool PowerSwitch::setModeOn (PowerActivity activated)
{
    bool retVal = false;

    mMutex.lock ();
    mCurrActivity |= activated;
    retVal = mCurrActivity != 0;
    ALOGD ("PowerSwitch::setModeOn(activated=0x%x) : mCurrActivity=0x%x", activated, mCurrActivity);
    mMutex.unlock ();
    return retVal;
}


/*******************************************************************************
**
** Function:        setPowerOffSleepState
**
** Description:     Adjust controller's power-off-sleep state.
**                  sleep: whether to enter sleep state.
**
** Returns:         True if ok.
**
*******************************************************************************/
bool PowerSwitch::setPowerOffSleepState (bool sleep)
{
    static const char fn [] = "PowerSwitch::setPowerOffSleepState";
    ALOGD ("%s: enter; sleep=%u", fn, sleep);
    tNFA_STATUS stat = NFA_STATUS_FAILED;
    bool retval = false;

    if (sleep) //enter power-off-sleep state
    {
        //make sure the current power state is ON
        if (mCurrDeviceMgtPowerState != NFA_DM_PWR_MODE_OFF_SLEEP)
        {
            SyncEventGuard guard (mPowerStateEvent);
            mExpectedDeviceMgtPowerState = NFA_DM_PWR_MODE_OFF_SLEEP; //if power adjustment is ok, then this is the expected state
            ALOGD ("%s: try power off", fn);
            stat = NFA_PowerOffSleepMode (TRUE);
            if (stat == NFA_STATUS_OK)
            {
                mPowerStateEvent.wait ();
                mCurrLevel = LOW_POWER;
            }
            else
            {
                ALOGE ("%s: API fail; stat=0x%X", fn, stat);
                goto TheEnd;
            }
        }
        else
        {
            ALOGE ("%s: power is not ON; curr device mgt power state=%s (%u)", fn,
                    deviceMgtPowerStateToString (mCurrDeviceMgtPowerState), mCurrDeviceMgtPowerState);
            goto TheEnd;
        }
    }
    else //exit power-off-sleep state
    {
        //make sure the current power state is OFF
        if (mCurrDeviceMgtPowerState != NFA_DM_PWR_MODE_FULL)
        {
            SyncEventGuard guard (mPowerStateEvent);
            mCurrDeviceMgtPowerState = NFA_DM_PWR_STATE_UNKNOWN;
            mExpectedDeviceMgtPowerState = NFA_DM_PWR_MODE_FULL;  //if power adjustment is ok, then this is the expected state
            ALOGD ("%s: try full power", fn);
            stat = NFA_PowerOffSleepMode (FALSE);
            if (stat == NFA_STATUS_OK)
            {
                mPowerStateEvent.wait ();
                if (mCurrDeviceMgtPowerState != NFA_DM_PWR_MODE_FULL)
                {
                    ALOGE ("%s: unable to full power; curr device mgt power stat=%s (%u)", fn,
                            deviceMgtPowerStateToString (mCurrDeviceMgtPowerState), mCurrDeviceMgtPowerState);
                    goto TheEnd;
                }
                android::doStartupConfig ();
                mCurrLevel = FULL_POWER;
            }
            else
            {
                ALOGE ("%s: API fail; stat=0x%X", fn, stat);
                goto TheEnd;
            }
        }
        else
        {
            ALOGE ("%s: not in power-off state; curr device mgt power state=%s (%u)", fn,
                    deviceMgtPowerStateToString (mCurrDeviceMgtPowerState), mCurrDeviceMgtPowerState);
            goto TheEnd;
        }
    }

    retval = true;
TheEnd:
    ALOGD ("%s: exit; return %u", fn, retval);
    return retval;
}


/*******************************************************************************
**
** Function:        deviceMgtPowerStateToString
**
** Description:     Decode power level to a string.
**                  deviceMgtPowerState: power level.
**
** Returns:         Text representation of power level.
**
*******************************************************************************/
const char* PowerSwitch::deviceMgtPowerStateToString (UINT8 deviceMgtPowerState)
{
    switch (deviceMgtPowerState)
    {
    case NFA_DM_PWR_MODE_FULL:
        return "DM-FULL";
    case NFA_DM_PWR_MODE_OFF_SLEEP:
        return "DM-OFF";
    default:
        return "DM-unknown????";
    }
}


/*******************************************************************************
**
** Function:        powerLevelToString
**
** Description:     Decode power level to a string.
**                  level: power level.
**
** Returns:         Text representation of power level.
**
*******************************************************************************/
const char* PowerSwitch::powerLevelToString (PowerLevel level)
{
    switch (level)
    {
    case UNKNOWN_LEVEL:
        return "PS-UNKNOWN";
    case FULL_POWER:
        return "PS-FULL";
    case LOW_POWER:
        return "PS-LOW-POWER";
    case POWER_OFF:
        return "PS-POWER-OFF";
    default:
        return "PS-unknown????";
    }
}

/*******************************************************************************
**
** Function:        screenOffPowerStateToString
**
** Description:     Decode power level to a string.
**                  level: power level.
**
** Returns:         Text representation of power level.
**
*******************************************************************************/
const char* PowerSwitch::screenOffPowerStateToString (ScreenOffPowerState state)
{
    switch (state)
    {
    case POWER_STATE_OFF:
        return "SOPS-POWER_OFF";
    case POWER_STATE_FULL:
        return "SOPS-FULL";
    case POWER_STATE_CARD_EMULATION:
        return "SOPS-CARD_EMULATION";
    default:
        return "SOPS-unknown????";
    }
}

/*******************************************************************************
**
** Function:        abort
**
** Description:     Abort and unblock currrent operation.
**
** Returns:         None
**
*******************************************************************************/
void PowerSwitch::abort ()
{
    static const char fn [] = "PowerSwitch::abort";
    ALOGD ("%s", fn);
    SyncEventGuard guard (mPowerStateEvent);
    mPowerStateEvent.notifyOne ();
}


/*******************************************************************************
**
** Function:        deviceManagementCallback
**
** Description:     Callback function for the stack.
**                  event: event ID.
**                  eventData: event's data.
**
** Returns:         None
**
*******************************************************************************/
void PowerSwitch::deviceManagementCallback (UINT8 event, tNFA_DM_CBACK_DATA* eventData)
{
    static const char fn [] = "PowerSwitch::deviceManagementCallback";

    switch (event)
    {
    case NFA_DM_PWR_MODE_CHANGE_EVT:
        {
            tNFA_DM_PWR_MODE_CHANGE& power_mode = eventData->power_mode;
            ALOGD ("%s: NFA_DM_PWR_MODE_CHANGE_EVT; status=0x%X; device mgt power state=%s (0x%X)", fn,
                    power_mode.status, sPowerSwitch.deviceMgtPowerStateToString (power_mode.power_mode),
                    power_mode.power_mode);
            SyncEventGuard guard (sPowerSwitch.mPowerStateEvent);
            if (power_mode.status == NFA_STATUS_OK)
            {
                //the event data does not contain the newly configured power mode,
                //so this code assigns the expected value
                sPowerSwitch.mCurrDeviceMgtPowerState = sPowerSwitch.mExpectedDeviceMgtPowerState;
            }
            sPowerSwitch.mPowerStateEvent.notifyOne ();
        }
        break;
    }
}


/*******************************************************************************
**
** Function:        isPowerOffSleepFeatureEnabled
**
** Description:     Whether power-off-sleep feature is enabled in .conf file.
**
** Returns:         True if feature is enabled.
**
*******************************************************************************/
bool PowerSwitch::isPowerOffSleepFeatureEnabled ()
{
    return mDesiredScreenOffPowerState == 0;
}