#if defined(__APPLE__)
#include <TargetConditionals.h>
#if TARGET_OS_IOS

#include <CoreMotion/CoreMotion.h>

#define ENABLE_STEP_COUNTER_LOGGING 1
#if ENABLE_STEP_COUNTER_LOGGING
#define STEP_COUNTER_LOG(...) NSLog(@"StepCounter - %@", [NSString stringWithFormat: __VA_ARGS__])
#else
#define STEP_COUNTER_LOG(...) {}
#endif

class iOSStepCounterWrapper
{
public:
    typedef void (*OnDataReceived) (int deviceId, int numberOfSteps);

    struct iOSStepCounterCallbacks
    {
        OnDataReceived dataReceived;
    };


    iOSStepCounterWrapper()
    {
        m_Pedometer = nullptr;
        m_Device = -1;
    }

    void Enable(int deviceId, iOSStepCounterCallbacks* callbacks)
    {
        if (IsEnabled())
        {
            STEP_COUNTER_LOG(@"Was already enabled?");
            if (m_Device != deviceId)
                STEP_COUNTER_LOG(@"Enabling with different device id? Expected %d, was %d. Are you creating more than one iOSStepCounter", m_Device, deviceId);
        }
        m_Pedometer = [[CMPedometer alloc]init];
        m_Callbacks = *callbacks;
        m_Device = deviceId;
        [m_Pedometer startPedometerUpdatesFromDate: [NSDate date] withHandler:^(CMPedometerData * _Nullable pedometerData, NSError * _Nullable error)
        {
            // Note: We need to call our callback on the same thread the Unity scripting is operating
            dispatch_async(dispatch_get_main_queue(), ^{
                if (error != nil)
                {
                    STEP_COUNTER_LOG(@"startPedometerUpdatesFromDate threw an error '%@', was it authorized?", [error localizedDescription]);
                    if (m_Device != -1)
                        Disable(m_Device);
                    return;
                }
                // Guard against situation where device was disabled, any event which are received after that, should be ignored.
                if (m_Device == -1)
                    return;
                m_Callbacks.dataReceived(m_Device, [pedometerData.numberOfSteps intValue]);
            });
        }];
    }

    bool Disable(int deviceId)
    {
        if (m_Pedometer == nullptr)
            return false;
        if (m_Device != deviceId)
            STEP_COUNTER_LOG(@"Disabling with wrong device id, expected %d, was %d", m_Device, deviceId);
        [m_Pedometer stopPedometerUpdates];
        m_Pedometer = nullptr;
        m_Device = -1;
        return true;
    }

    bool IsEnabled() const
    {
        return m_Pedometer != nullptr;
    }

private:
    CMPedometer* m_Pedometer;
    iOSStepCounterCallbacks m_Callbacks;
    int m_Device;
};

static iOSStepCounterWrapper s_Wrapper;
static const int kResultSuccess = 1;
static const int kResultFailure = -1;

extern "C" int _iOSStepCounterIsAvailable()
{
    return [CMPedometer isStepCountingAvailable] ? 1 : 0;
}

extern "C" int _iOSStepCounterGetAuthorizationStatus()
{
    if (@available(iOS 11.0, *))
    {
        return (int)[CMPedometer authorizationStatus];
    }
    return 0;
}

extern "C" int _iOSStepCounterEnable(int deviceId, iOSStepCounterWrapper::iOSStepCounterCallbacks* callbacks, int sizeOfCallbacks)
{
    if (sizeof(iOSStepCounterWrapper::iOSStepCounterCallbacks) != sizeOfCallbacks)
    {
        STEP_COUNTER_LOG(@"iOSStepCounterCallbacks size mismatch, expected %lu was %d", sizeof(iOSStepCounterWrapper::iOSStepCounterCallbacks), sizeOfCallbacks);
        return kResultFailure;
    }

    if (_iOSStepCounterIsAvailable() == 0)
    {
        STEP_COUNTER_LOG(@"Step counting is not available");
        return kResultFailure;
    }

    NSString* motionUsage = @"NSMotionUsageDescription";

    if ([[NSBundle mainBundle] objectForInfoDictionaryKey: motionUsage] == nil)
    {
        STEP_COUNTER_LOG(@"%@ is missing in Info.plist, please enable Motion Usage in Input Settings", motionUsage);
        return kResultFailure;
    }

    if (@available(iOS 11.0, *))
    {
        if ([CMPedometer authorizationStatus] == CMAuthorizationStatusRestricted)
        {
            STEP_COUNTER_LOG(@"Step Counter was restricted.");
            return kResultFailure;
        }

        if ([CMPedometer authorizationStatus] == CMAuthorizationStatusDenied)
        {
            STEP_COUNTER_LOG(@"Step Counter was denied. Enable Motion & Fitness under app settings.");
            return kResultFailure;
        }
        // Do nothing for Authorized and NotDetermined
    }

    // Note: After installation this function will prompt a dialog asking about Motion & Fitness authorization
    //       If user denies the prompt, there will be an error in startPedometerUpdatesFromDate callback
    //       The dialog only appears once, not sure how to trigger it again, besides reinstalling app
    s_Wrapper.Enable(deviceId, callbacks);

    return kResultSuccess;
}

extern "C" int _iOSStepCounterDisable(int deviceId)
{
    return s_Wrapper.Disable(deviceId) ? kResultSuccess : kResultFailure;
}

extern "C" int _iOSStepCounterIsEnabled(int deviceId)
{
    return s_Wrapper.IsEnabled() ? 1 : 0;
}

#endif
#endif