/*
// 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 <common/utils/HwcTrace.h>
#include <Hwcomposer.h>
#include <common/utils/Dump.h>
#include <UeventObserver.h>

namespace android {
namespace intel {

Hwcomposer* Hwcomposer::sInstance(0);

Hwcomposer::Hwcomposer()
    : mProcs(0),
      mDrm(0),
      mPlaneManager(0),
      mBufferManager(0),
      mDisplayAnalyzer(0),
      mDisplayContext(0),
      mUeventObserver(0),
      mInitialized(false)
{
    CTRACE();

    mDisplayDevices.setCapacity(IDisplayDevice::DEVICE_COUNT);
    mDisplayDevices.clear();
}

Hwcomposer::~Hwcomposer()
{
    CTRACE();
    deinitialize();
}

bool Hwcomposer::initCheck() const
{
    return mInitialized;
}

bool Hwcomposer::prepare(size_t numDisplays,
                          hwc_display_contents_1_t** displays)
{
    bool ret = true;

    RETURN_FALSE_IF_NOT_INIT();
    ALOGTRACE("display count = %d", numDisplays);

    if (!numDisplays || !displays) {
        ELOGTRACE("invalid parameters");
        return false;
    }

    mDisplayAnalyzer->analyzeContents(numDisplays, displays);

    // disable reclaimed planes
    mPlaneManager->disableReclaimedPlanes();

    // reclaim all allocated planes if possible
    for (size_t i = 0; i < numDisplays; i++) {
        if (i >= mDisplayDevices.size()) {
            continue;
        }
        IDisplayDevice *device = mDisplayDevices.itemAt(i);
        if (!device) {
            VLOGTRACE("device %d doesn't exist", i);
            continue;
        }
        device->prePrepare(displays[i]);
    }

    for (size_t i = 0; i < numDisplays; i++) {
        if (i >= mDisplayDevices.size()) {
            continue;
        }
        IDisplayDevice *device = mDisplayDevices.itemAt(i);
        if (!device) {
            VLOGTRACE("device %d doesn't exist", i);
            continue;
        }
        ret = device->prepare(displays[i]);
        if (ret == false) {
            ELOGTRACE("failed to do prepare for device %d", i);
            continue;
        }
    }

    return ret;
}

bool Hwcomposer::commit(size_t numDisplays,
                         hwc_display_contents_1_t **displays)
{
    bool ret = true;

    RETURN_FALSE_IF_NOT_INIT();
    ALOGTRACE("display count = %d", numDisplays);

    if (!numDisplays || !displays) {
        ELOGTRACE("invalid parameters");
        return false;
    }

    mDisplayContext->commitBegin(numDisplays, displays);

    for (size_t i = 0; i < numDisplays; i++) {
        if (i >= mDisplayDevices.size()) {
            continue;
        }
        IDisplayDevice *device = mDisplayDevices.itemAt(i);
        if (!device) {
            VLOGTRACE("device %d doesn't exist", i);
            continue;
        }

        if (!device->isConnected()) {
            VLOGTRACE("device %d is disconnected", i);
            continue;
        }

        ret = device->commit(displays[i], mDisplayContext);
        if (ret == false) {
            ELOGTRACE("failed to do commit for device %d", i);
            continue;
        }
    }

    mDisplayContext->commitEnd(numDisplays, displays);
    // return true always
    return true;
}

bool Hwcomposer::vsyncControl(int disp, int enabled)
{
    RETURN_FALSE_IF_NOT_INIT();
    ALOGTRACE("disp = %d, enabled = %d", disp, enabled);

    if (disp < 0 || disp >= IDisplayDevice::DEVICE_COUNT) {
        ELOGTRACE("invalid disp %d", disp);
        return false;
    }
    if (disp >= (int) mDisplayDevices.size()) {
        return false;
    }
    IDisplayDevice *device = mDisplayDevices.itemAt(disp);
    if (!device) {
        ELOGTRACE("no device found");
        return false;
    }

    return device->vsyncControl(enabled ? true : false);
}

bool Hwcomposer::blank(int disp, int blank)
{
    RETURN_FALSE_IF_NOT_INIT();
    ALOGTRACE("disp = %d, blank = %d", disp, blank);

    if (disp < 0 || disp >= IDisplayDevice::DEVICE_COUNT) {
        ELOGTRACE("invalid disp %d", disp);
        return false;
    }
    if (disp >= (int) mDisplayDevices.size()) {
        return false;
    }
    IDisplayDevice *device = mDisplayDevices.itemAt(disp);
    if (!device) {
        ELOGTRACE("no device found");
        return false;
    }

    return device->blank(blank ? true : false);
}

bool Hwcomposer::getDisplayConfigs(int disp,
                                      uint32_t *configs,
                                      size_t *numConfigs)
{
    RETURN_FALSE_IF_NOT_INIT();

    if (disp < 0 || disp >= IDisplayDevice::DEVICE_COUNT) {
        ELOGTRACE("invalid disp %d", disp);
        return false;
    }
    if (disp >= (int) mDisplayDevices.size()) {
        return false;
    }
    IDisplayDevice *device = mDisplayDevices.itemAt(disp);
    if (!device) {
        ELOGTRACE("no device %d found", disp);
        return false;
    }

    return device->getDisplayConfigs(configs, numConfigs);
}

bool Hwcomposer::getDisplayAttributes(int disp,
                                         uint32_t config,
                                         const uint32_t *attributes,
                                         int32_t *values)
{
    RETURN_FALSE_IF_NOT_INIT();

    if (disp < 0 || disp >= IDisplayDevice::DEVICE_COUNT) {
        ELOGTRACE("invalid disp %d", disp);
        return false;
    }
    if (disp >= (int) mDisplayDevices.size()) {
        return false;
    }
    IDisplayDevice *device = mDisplayDevices.itemAt(disp);
    if (!device) {
        ELOGTRACE("no device found");
        return false;
    }

    return device->getDisplayAttributes(config, attributes, values);
}

bool Hwcomposer::compositionComplete(int disp)
{
    RETURN_FALSE_IF_NOT_INIT();

    if (disp < 0 || disp >= IDisplayDevice::DEVICE_COUNT) {
        ELOGTRACE("invalid disp %d", disp);
        return false;
    }

    mDisplayContext->compositionComplete();
    if (disp >= (int) mDisplayDevices.size()) {
        return false;
    }

    IDisplayDevice *device = mDisplayDevices.itemAt(disp);
    if (!device) {
        ELOGTRACE("no device found");
        return false;
    }

    return device->compositionComplete();
}

bool Hwcomposer::setPowerMode(int disp, int mode)
{
    RETURN_FALSE_IF_NOT_INIT();

    if (disp < 0 || disp >= IDisplayDevice::DEVICE_COUNT) {
        ELOGTRACE("invalid disp %d", disp);
        return false;
    }

    IDisplayDevice *device = mDisplayDevices.itemAt(disp);
    if (!device) {
        ELOGTRACE("no device found");
        return false;
    }

    return device->setPowerMode(mode);
}

int Hwcomposer::getActiveConfig(int disp)
{
    RETURN_NULL_IF_NOT_INIT();

    if (disp < 0 || disp >= IDisplayDevice::DEVICE_COUNT) {
        ELOGTRACE("invalid disp %d", disp);
        return -1;
    }

    IDisplayDevice *device = mDisplayDevices.itemAt(disp);
    if (!device) {
        ELOGTRACE("no device found");
        return -1;
    }

    return device->getActiveConfig();
}

bool Hwcomposer::setActiveConfig(int disp, int index)
{
    RETURN_FALSE_IF_NOT_INIT();

    if (disp < 0 || disp >= IDisplayDevice::DEVICE_COUNT) {
        ELOGTRACE("invalid disp %d", disp);
        return false;
    }

    IDisplayDevice *device = mDisplayDevices.itemAt(disp);
    if (!device) {
        ELOGTRACE("no device found");
        return false;
    }

    return device->setActiveConfig(index);
}

bool Hwcomposer::setCursorPositionAsync(int disp, int x, int y)
{
    RETURN_FALSE_IF_NOT_INIT();

    if (disp != HWC_DISPLAY_PRIMARY && disp != HWC_DISPLAY_EXTERNAL) {
        ELOGTRACE("invalid disp %d", disp);
        return false;
    }

    return mDisplayContext->setCursorPosition(disp, x, y);
}

void Hwcomposer::vsync(int disp, int64_t timestamp)
{
    RETURN_VOID_IF_NOT_INIT();

    if (mProcs && mProcs->vsync) {
        VLOGTRACE("report vsync on disp %d, timestamp %llu", disp, timestamp);
        // workaround to pretend vsync is from primary display
        // Display will freeze if vsync is from external display.
        mProcs->vsync(const_cast<hwc_procs_t*>(mProcs), IDisplayDevice::DEVICE_PRIMARY, timestamp);
    }
}

void Hwcomposer::hotplug(__attribute__((unused))int disp, bool connected)
{
    RETURN_VOID_IF_NOT_INIT();

    if (mProcs && mProcs->hotplug) {
        DLOGTRACE("report hotplug on disp %d, connected %d", disp, connected);
        mProcs->hotplug(const_cast<hwc_procs_t*>(mProcs), disp, connected);
        DLOGTRACE("hotplug callback processed and returned!");
    }

    mDisplayAnalyzer->postHotplugEvent(connected);
}

void Hwcomposer::invalidate()
{
    RETURN_VOID_IF_NOT_INIT();

    if (mProcs && mProcs->invalidate) {
        DLOGTRACE("invalidating screen...");
        mProcs->invalidate(const_cast<hwc_procs_t*>(mProcs));
    }
}

bool Hwcomposer::release()
{
    RETURN_FALSE_IF_NOT_INIT();

    return true;
}

bool Hwcomposer::dump(char *buff, int buff_len, int * /* cur_len */)
{
    RETURN_FALSE_IF_NOT_INIT();

    Dump d(buff, buff_len);

    // dump composer status
    d.append("Hardware Composer state:");
    // dump device status
    for (size_t i= 0; i < mDisplayDevices.size(); i++) {
        IDisplayDevice *device = mDisplayDevices.itemAt(i);
        if (device)
            device->dump(d);
    }

    // dump plane manager status
    if (mPlaneManager)
        mPlaneManager->dump(d);

    // dump buffer manager status
    if (mBufferManager)
        mBufferManager->dump(d);

    return true;
}

void Hwcomposer::registerProcs(hwc_procs_t const *procs)
{
    CTRACE();

    if (!procs) {
        WLOGTRACE("procs is NULL");
    }
    mProcs = procs;
}

bool Hwcomposer::initialize()
{
    CTRACE();

    // create drm
    mDrm = new Drm();
    if (!mDrm || !mDrm->initialize()) {
        DEINIT_AND_RETURN_FALSE("failed to create DRM");
    }

    // create buffer manager
    mBufferManager = createBufferManager();
    if (!mBufferManager || !mBufferManager->initialize()) {
        DEINIT_AND_RETURN_FALSE("failed to create buffer manager");
    }

    // create display plane manager
    mPlaneManager = createDisplayPlaneManager();
    if (!mPlaneManager || !mPlaneManager->initialize()) {
        DEINIT_AND_RETURN_FALSE("failed to create display plane manager");
    }

    mDisplayContext = createDisplayContext();
    if (!mDisplayContext || !mDisplayContext->initialize()) {
        DEINIT_AND_RETURN_FALSE("failed to create display context");
    }

    mUeventObserver = new UeventObserver();
    if (!mUeventObserver || !mUeventObserver->initialize()) {
        DEINIT_AND_RETURN_FALSE("failed to initialize uevent observer");
    }

    // create display device
    for (int i = 0; i < IDisplayDevice::DEVICE_COUNT; i++) {
        IDisplayDevice *device = createDisplayDevice(i, *mPlaneManager);
        if (!device || !device->initialize()) {
            DEINIT_AND_DELETE_OBJ(device);
            DEINIT_AND_RETURN_FALSE("failed to create device %d", i);
        }
        // add this device
        mDisplayDevices.insertAt(device, i, 1);
    }

    mDisplayAnalyzer = new DisplayAnalyzer();
    if (!mDisplayAnalyzer || !mDisplayAnalyzer->initialize()) {
        DEINIT_AND_RETURN_FALSE("failed to initialize display analyzer");
    }

    // all initialized, starting uevent observer
    mUeventObserver->start();

    mInitialized = true;
    return true;
}

void Hwcomposer::deinitialize()
{
    DEINIT_AND_DELETE_OBJ(mDisplayAnalyzer);

    DEINIT_AND_DELETE_OBJ(mUeventObserver);
    // destroy display devices
    for (size_t i = 0; i < mDisplayDevices.size(); i++) {
        IDisplayDevice *device = mDisplayDevices.itemAt(i);
        DEINIT_AND_DELETE_OBJ(device);
    }
    mDisplayDevices.clear();

    DEINIT_AND_DELETE_OBJ(mDisplayContext);
    DEINIT_AND_DELETE_OBJ(mPlaneManager);
    DEINIT_AND_DELETE_OBJ(mBufferManager);
    DEINIT_AND_DELETE_OBJ(mDrm);
    mInitialized = false;
}

Drm* Hwcomposer::getDrm()
{
    return mDrm;
}

DisplayPlaneManager* Hwcomposer::getPlaneManager()
{
    return mPlaneManager;
}

BufferManager* Hwcomposer::getBufferManager()
{
    return mBufferManager;
}

IDisplayContext* Hwcomposer::getDisplayContext()
{
    return mDisplayContext;
}

DisplayAnalyzer* Hwcomposer::getDisplayAnalyzer()
{
    return mDisplayAnalyzer;
}

IDisplayDevice* Hwcomposer::getDisplayDevice(int disp)
{
    if (disp < 0 || disp >= IDisplayDevice::DEVICE_COUNT) {
        ELOGTRACE("invalid disp %d", disp);
        return NULL;
    }
    if (disp >= (int) mDisplayDevices.size()) {
        return NULL;
    }
    return mDisplayDevices.itemAt(disp);
}

UeventObserver* Hwcomposer::getUeventObserver()
{
    return mUeventObserver;
}


} // namespace intel
} // namespace android