/*
// Copyright (c) 2014 Intel Corporation 
//
// 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 <HwcTrace.h>
#include <SoftVsyncObserver.h>
#include <IDisplayDevice.h>

extern "C" int clock_nanosleep(clockid_t clock_id, int flags,
                           const struct timespec *request,
                           struct timespec *remain);


namespace android {
namespace intel {

SoftVsyncObserver::SoftVsyncObserver(IDisplayDevice& disp)
    : mDisplayDevice(disp),
      mDevice(IDisplayDevice::DEVICE_COUNT),
      mEnabled(false),
      mRefreshRate(60), // default 60 frames per second
      mRefreshPeriod(0),
      mLock(),
      mCondition(),
      mNextFakeVSync(0),
      mExitThread(false),
      mInitialized(false)
{
}

SoftVsyncObserver::~SoftVsyncObserver()
{
    WARN_IF_NOT_DEINIT();
}

bool SoftVsyncObserver::initialize()
{
    if (mInitialized) {
        WTRACE("object has been initialized");
        return true;
    }

    mExitThread = false;
    mEnabled = false;
    mRefreshRate = 60/mDisplayDevice.getFpsDivider();
    mDevice = mDisplayDevice.getType();
    mThread = new VsyncEventPollThread(this);
    if (!mThread.get()) {
        DEINIT_AND_RETURN_FALSE("failed to create vsync event poll thread.");
    }
    mThread->run("SoftVsyncObserver", PRIORITY_URGENT_DISPLAY);
    mInitialized = true;
    return true;
}

void SoftVsyncObserver::deinitialize()
{
    if (mEnabled) {
        WTRACE("soft vsync is still enabled");
        control(false);
    }

    mExitThread = true;
    mCondition.signal();

    if (mThread.get()) {
        mThread->requestExitAndWait();
        mThread = NULL;
    }
    mInitialized = false;
}

void SoftVsyncObserver::setRefreshRate(int rate)
{
    if (mEnabled) {
        WTRACE("too late to set refresh rate");
    } else if (rate < 1 || rate > 120) {
        WTRACE("invalid refresh rate %d", rate);
    } else {
        mRefreshRate = rate;
    }
}

bool SoftVsyncObserver::control(bool enabled)
{
    if (enabled == mEnabled) {
        WTRACE("vsync state %d is not changed", enabled);
        return true;
    }

    if (enabled) {
        mRefreshPeriod = nsecs_t(1e9 / mRefreshRate);
        mNextFakeVSync = systemTime(CLOCK_MONOTONIC) + mRefreshPeriod;
    }
    mEnabled = enabled;
    mCondition.signal();
    return true;
}

bool SoftVsyncObserver::threadLoop()
{
    { // scope for lock
        Mutex::Autolock _l(mLock);
        while (!mEnabled) {
            mCondition.wait(mLock);
            if (mExitThread) {
                ITRACE("exiting thread loop");
                return false;
            }
        }
    }


    const nsecs_t period = mRefreshPeriod * mDisplayDevice.getFpsDivider();
    const nsecs_t now = systemTime(CLOCK_MONOTONIC);
    nsecs_t next_vsync = mNextFakeVSync;
    nsecs_t sleep = next_vsync - now;
    if (sleep < 0) {
        // we missed, find where the next vsync should be
        sleep = (period - ((now - next_vsync) % period));
        next_vsync = now + sleep;
    }
    mNextFakeVSync = next_vsync + period;

    struct timespec spec;
    spec.tv_sec  = next_vsync / 1000000000;
    spec.tv_nsec = next_vsync % 1000000000;

    int err;
    do {
        err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
    } while (err < 0 && errno == EINTR);


    if (err == 0) {
        mDisplayDevice.onVsync(next_vsync);
    }

    return true;
}

} // namespace intel
} // namesapce android