//
// Copyright (c) 2017 The Khronos Group Inc.
//
// 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.
//

#include "sleep.h"
#include "utility.h"

#if defined(__APPLE__)
#include <IOKit/pwr_mgt/IOPMLib.h>
#include <IOKit/IOMessage.h>

struct
{
    io_connect_t connection;
    IONotificationPortRef port;
    io_object_t iterator;
} sleepInfo;

void sleepCallback(void* refcon, io_service_t service, natural_t messageType,
                   void* messageArgument);

void sleepCallback(void* refcon UNUSED, io_service_t service UNUSED,
                   natural_t messageType, void* messageArgument)
{

    IOReturn result;
    /*
    service -- The IOService whose state has changed.
    messageType -- A messageType enum, defined by IOKit/IOMessage.h or by the
    IOService's family. messageArgument -- An argument for the message,
    dependent on the messageType.
    */
    switch (messageType)
    {
        case kIOMessageSystemWillSleep:
            // Handle demand sleep (such as sleep caused by running out of
            // batteries, closing the lid of a laptop, or selecting
            // sleep from the Apple menu.
            IOAllowPowerChange(sleepInfo.connection, (long)messageArgument);
            vlog("Hard sleep occurred.\n");
            break;
        case kIOMessageCanSystemSleep:
            // In this case, the computer has been idle for several minutes
            // and will sleep soon so you must either allow or cancel
            // this notification. Important: if you don’t respond, there will
            // be a 30-second timeout before the computer sleeps.
            // IOCancelPowerChange(root_port,(long)messageArgument);
            result = IOCancelPowerChange(sleepInfo.connection,
                                         (long)messageArgument);
            if (kIOReturnSuccess != result)
                vlog("sleep prevention failed. (%d)\n", result);
            break;
        case kIOMessageSystemHasPoweredOn:
            // Handle wakeup.
            break;
    }
}
#endif


void PreventSleep(void)
{
#if defined(__APPLE__)
    vlog("Disabling sleep... ");
    sleepInfo.iterator = (io_object_t)0;
    sleepInfo.port = NULL;
    sleepInfo.connection = IORegisterForSystemPower(
        &sleepInfo, // void * refcon,
        &sleepInfo.port, // IONotificationPortRef * thePortRef,
        sleepCallback, // IOServiceInterestCallback callback,
        &sleepInfo.iterator // io_object_t * notifier
    );

    if ((io_connect_t)0 == sleepInfo.connection)
        vlog("failed.\n");
    else
        vlog("done.\n");

    CFRunLoopAddSource(CFRunLoopGetCurrent(),
                       IONotificationPortGetRunLoopSource(sleepInfo.port),
                       kCFRunLoopDefaultMode);
#else
    vlog("*** PreventSleep() is not implemented on this platform.\n");
#endif
}

void ResumeSleep(void)
{
#if defined(__APPLE__)
    IOReturn result = IODeregisterForSystemPower(&sleepInfo.iterator);
    if (0 != result)
        vlog("Got error %d restoring sleep \n", result);
    else
        vlog("Sleep restored.\n");
#else
    vlog("*** ResumeSleep() is not implemented on this platform.\n");
#endif
}
