/*
// 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 <tangier/TngPlaneManager.h>
#include <tangier/TngPrimaryPlane.h>
#include <tangier/TngSpritePlane.h>
#include <tangier/TngOverlayPlane.h>
#include <tangier/TngCursorPlane.h>

namespace android {
namespace intel {

TngPlaneManager::TngPlaneManager()
    : DisplayPlaneManager()
{
    memset(&mZorder, 0, sizeof(mZorder));
}

TngPlaneManager::~TngPlaneManager()
{
}

bool TngPlaneManager::initialize()
{
    mSpritePlaneCount = 1;  // Sprite D
    mOverlayPlaneCount = 2;  // Overlay A & C
    mPrimaryPlaneCount = 3;  // Primary A, B, C
    mCursorPlaneCount = 3;

    return DisplayPlaneManager::initialize();
}

void TngPlaneManager::deinitialize()
{
    DisplayPlaneManager::deinitialize();
}

DisplayPlane* TngPlaneManager::allocPlane(int index, int type)
{
    DisplayPlane *plane = 0;

    switch (type) {
    case DisplayPlane::PLANE_PRIMARY:
        plane = new TngPrimaryPlane(index, index);
        break;
    case DisplayPlane::PLANE_SPRITE:
        plane = new TngSpritePlane(index, 0);
        break;
    case DisplayPlane::PLANE_OVERLAY:
        plane = new TngOverlayPlane(index, 0);
        break;
    case DisplayPlane::PLANE_CURSOR:
        plane = new TngCursorPlane(index, index /*disp */);
        break;
    default:
        ETRACE("unsupported type %d", type);
        break;
    }
    if (plane && !plane->initialize(DisplayPlane::MIN_DATA_BUFFER_COUNT)) {
        ETRACE("failed to initialize plane.");
        DEINIT_AND_DELETE_OBJ(plane);
    }

    return plane;
}

bool TngPlaneManager::isValidZOrder(int dsp, ZOrderConfig& config)
{
    // check whether it's a supported z order config
    int firstRGB = -1;
    int lastRGB = -1;
    int firstOverlay = -1;
    int lastOverlay = -1;

    for (int i = 0; i < (int)config.size(); i++) {
        const ZOrderLayer *layer = config[i];
        switch (layer->planeType) {
        case DisplayPlane::PLANE_PRIMARY:
        case DisplayPlane::PLANE_SPRITE:
            if (firstRGB == -1) {
                firstRGB = i;
                lastRGB = i;
            } else {
                lastRGB = i;
            }
            break;
        case DisplayPlane::PLANE_OVERLAY:
        case DisplayPlane::PLANE_CURSOR:
            if (firstOverlay == -1) {
                firstOverlay = i;
                lastOverlay = i;
            } else {
                lastOverlay = i;
            }
            break;
        }
    }

    if ((lastRGB < firstOverlay) || (firstRGB > lastOverlay)) {
        return true;
    } else {
        VTRACE("invalid z order config. rgb (%d, %d) yuv (%d, %d)",
               firstRGB, lastRGB, firstOverlay, lastOverlay);
        return false;
    }
}

bool TngPlaneManager::assignPlanes(int dsp, ZOrderConfig& config)
{
    // probe if plane is available
    int size = (int)config.size();
    for (int i = 0; i < size; i++) {
        const ZOrderLayer *layer = config.itemAt(i);
        if (!getFreePlanes(dsp, layer->planeType)) {
            DTRACE("no plane available for dsp %d, type %d", dsp, layer->planeType);
            return false;
        }
    }

    if (config.size() == 1 && config[0]->planeType == DisplayPlane::PLANE_SPRITE) {
        config[0]->planeType == DisplayPlane::PLANE_PRIMARY;
    }

    // allocate planes
    for (int i = 0; i < size; i++) {
        ZOrderLayer *layer = config.itemAt(i);
        layer->plane = getPlaneHelper(dsp, layer->planeType);
        if (layer->plane == NULL) {
            // should never happen!!
            ETRACE("failed to assign plane for type %d", layer->planeType);
            return false;
        }
        // sequence !!!!! enabling plane before setting zorder
        // see TngSpritePlane::enablePlane implementation!!!!
        layer->plane->enable();
    }

    // setup Z order
    for (int i = 0; i < size; i++) {
        ZOrderLayer *layer = config.itemAt(i);
        layer->plane->setZOrderConfig(config, &mZorder);
    }

    return true;
}

void* TngPlaneManager::getZOrderConfig() const
{
    return (void*)&mZorder;
}

DisplayPlane* TngPlaneManager::getPlaneHelper(int dsp, int type)
{
    RETURN_NULL_IF_NOT_INIT();

    if (dsp < 0 || dsp > IDisplayDevice::DEVICE_EXTERNAL) {
        ETRACE("Invalid display device %d", dsp);
        return 0;
    }

    int index = dsp == IDisplayDevice::DEVICE_PRIMARY ? 0 : 1;

    if (type == DisplayPlane::PLANE_PRIMARY ||
        type == DisplayPlane::PLANE_CURSOR) {
        return getPlane(type, index);
    } else if (type == DisplayPlane::PLANE_SPRITE) {
        return getAnyPlane(type);
    } else if (type == DisplayPlane::PLANE_OVERLAY) {
        // use overlay A for pipe A and overlay C for pipe B if possible
        DisplayPlane *plane = getPlane(type, index);
        if (plane == NULL) {
            plane = getPlane(type, !index);
        }
        return plane;
    } else {
        ETRACE("invalid plane type %d", type);
        return 0;
    }
}

} // namespace intel
} // namespace android