/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * 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.
 */

#define LOG_TAG "SurfaceFlinger"

#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>

#include <cutils/memory.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/MemoryDealer.h>
#include <utils/IMemory.h>
#include <ui/PixelFormat.h>
#include <pixelflinger/pixelflinger.h>

#include "LayerBitmap.h"
#include "SurfaceFlinger.h"
#include "VRamHeap.h"


namespace android {

// ---------------------------------------------------------------------------

LayerBitmap::LayerBitmap()
    : mAllocFlags(0), mOffset(0), mSize(-1U), mAlignment(2)
{
    memset(&mSurface, 0, sizeof(mSurface));

    egl_native_pixmap_t::version = sizeof(egl_native_pixmap_t);
    egl_native_pixmap_t::width = 0;
    egl_native_pixmap_t::height = 0;
    egl_native_pixmap_t::stride = 0;
    egl_native_pixmap_t::data = NULL;
    egl_native_pixmap_t::format = 0;
    egl_native_pixmap_t::rfu[0] = 0;
    egl_native_pixmap_t::rfu[1] = 0;
    egl_native_pixmap_t::rfu[2] = 0;
    egl_native_pixmap_t::gem = 0;
    egl_native_pixmap_t::reserved = 0;
}

LayerBitmap::~LayerBitmap()
{
    mSurface.data = 0;
}

status_t LayerBitmap::init(const sp<MemoryDealer>& allocator)
{
    if (mAllocator != NULL)
        return BAD_VALUE;
    mAllocator = allocator;
    return NO_ERROR;
}

status_t LayerBitmap::setBits(uint32_t w, uint32_t h, uint32_t alignment, 
        PixelFormat format, uint32_t flags)
{
    const sp<MemoryDealer>& allocator(mAllocator);
    if (allocator == NULL)
        return NO_INIT;

    if (UNLIKELY(w == mSurface.width && h == mSurface.height &&
            format == mSurface.format))
    { // same format and size, do nothing.
        return NO_ERROR;
    }

    PixelFormatInfo info;
    getPixelFormatInfo(format, &info);

    uint32_t allocFlags = MemoryDealer::PAGE_ALIGNED;
    const uint32_t align = 4; // must match GL_UNPACK_ALIGNMENT
    const uint32_t Bpp = info.bytesPerPixel;
    uint32_t stride = (w + (alignment-1)) & ~(alignment-1);
    stride = ((stride * Bpp + (align-1)) & ~(align-1)) / Bpp;
    size_t size = info.getScanlineSize(stride) * h;
    if (allocFlags & MemoryDealer::PAGE_ALIGNED) {
        size_t pagesize = getpagesize();
        size = (size + (pagesize-1)) & ~(pagesize-1);
    }

    /* FIXME: we should be able to have a h/v stride because the user of the
     * surface might have stride limitation (for instance h/w codecs often do)
     */
    int32_t vstride = 0;

    mAlignment = alignment;
    mAllocFlags = allocFlags;
    mOffset = 0;
    if (mSize != size) {
        // would be nice to have a reallocate() api
        mBitsMemory.clear(); // free-memory
        mBitsMemory = allocator->allocate(size, allocFlags);
        mSize = size;
    } else {
        // don't erase memory if we didn't have to reallocate
        flags &= ~SECURE_BITS;
    }
    if (mBitsMemory != 0) {
        mOffset = mBitsMemory->offset();
        mSurface.data = static_cast<GGLubyte*>(mBitsMemory->pointer());
        mSurface.version = sizeof(GGLSurface);
        mSurface.width  = w;
        mSurface.height = h;
        mSurface.stride = stride;
        mSurface.vstride = vstride;
        mSurface.format = format;
        if (flags & SECURE_BITS)
            clear();

	egl_native_pixmap_t::width = w;
	egl_native_pixmap_t::height = h;
	egl_native_pixmap_t::stride = stride;
	egl_native_pixmap_t::data = mSurface.data;
	egl_native_pixmap_t::format = format;

	const sp<IMemoryHeap> heap = allocator->getMemoryHeap();
	egl_native_pixmap_t::gem = heap->getGemName();
	egl_native_pixmap_t::reserved = heap->getGemName();

	setCPUDomain();
    }

    if (mBitsMemory==0 || mSurface.data==0) {
        LOGE("not enough memory for layer bitmap "
             "size=%u (w=%d, h=%d, stride=%d, format=%d)",
             size, int(w), int(h), int(stride), int(format));
        allocator->dump("LayerBitmap");
        mSurface.data = 0;
        mSize = -1U;
        return NO_MEMORY;
    }
    return NO_ERROR;
}

void LayerBitmap::clear()
{
    // NOTE: this memset should not be necessary, at least for
    // opaque surface. However, for security reasons it's better to keep it
    // (in the case of pmem, it's possible that the memory contains old
    // data)
    if (mSurface.data) {
        memset(mSurface.data, 0, mSize);
        //if (bytesPerPixel(mSurface.format) == 4) {
        //    android_memset32((uint32_t*)mSurface.data, 0xFF0000FF, mSize);
        //} else  {
        //    android_memset16((uint16_t*)mSurface.data, 0xF800, mSize);
        //}
    }
}

status_t LayerBitmap::getInfo(surface_info_t* info) const
{
    if (mSurface.data == 0) {
        memset(info, 0, sizeof(surface_info_t));
        info->bits_offset = NO_MEMORY;
        return NO_MEMORY;
    }
    info->w     = uint16_t(width());
    info->h     = uint16_t(height());
    info->stride= uint16_t(stride());
    info->bpr   = uint16_t(stride() * bytesPerPixel(pixelFormat()));
    info->format= uint8_t(pixelFormat());
    info->flags = surface_info_t::eBufferDirty;
    info->bits_offset = ssize_t(mOffset);
    return NO_ERROR;
}

status_t LayerBitmap::resize(uint32_t w, uint32_t h)
{
    int err = setBits(w, h, mAlignment, pixelFormat(), SECURE_BITS);
    return err;
}

size_t LayerBitmap::size() const
{
    return mSize;
}

void LayerBitmap::getBitmapSurface(copybit_image_t* img) const
{
    const sp<IMemoryHeap>& mh(getAllocator()->getMemoryHeap());
    void* sbase = mh->base();
    const GGLSurface& t(surface());
    img->w = t.stride  ?: t.width;
    img->h = t.vstride ?: t.height;
    img->format = t.format;
    img->offset = intptr_t(t.data) - intptr_t(sbase);
    img->base = sbase;
    img->fd = mh->heapID();
}

#include <i915_drm.h>
/* XXX this should be a method of IMemoryHeap */
int LayerBitmap::setCPUDomain()
{
	const sp<MemoryDealer>& allocator(mAllocator);
	const sp<IMemoryHeap> heap = allocator->getMemoryHeap();
	struct drm_i915_gem_set_domain set_domain;
	uint32_t handle;
	int fd, ret;

	fd = heap->getHeapID();
	handle = heap->getGemHandle();
	if (fd < 0 || !handle) {
		/* not a GEM heap */
		return 0;
	}

	memset(&set_domain, 0, sizeof(set_domain));
	set_domain.handle = handle;
	set_domain.read_domains = I915_GEM_DOMAIN_CPU;
	set_domain.write_domain = I915_GEM_DOMAIN_CPU;

	ret = ioctl(fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain);
	if (ret)
		LOGE("failed to set LayerBitmap to CPU domain");
	return ret;
}

// ---------------------------------------------------------------------------

}; // namespace android
