#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <semaphore.h>
#include <pthread.h>
#include <string.h>
#include <climits>
#include <math.h>

#include <gui/GLConsumer.h>
#include <gui/Surface.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicBufferMapper.h>

#include <camera/Camera.h>
#include <camera/ICamera.h>
#include <media/mediarecorder.h>

#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <cutils/properties.h>
#include <camera/CameraParameters.h>
#include <camera/ShotParameters.h>
#include <camera/CameraMetadata.h>
#include <system/audio.h>
#include <system/camera.h>

#include <cutils/memory.h>
#include <utils/Log.h>

#include <sys/wait.h>

#include "camera_test.h"
#include "camera_test_surfacetexture.h"

#define ASSERT(X) \
    do { \
       if(!(X)) { \
           printf("error: %s():%d", __FUNCTION__, __LINE__); \
           return; \
       } \
    } while(0);

#define ALIGN_DOWN(x, n) ((x) & (~((n) - 1)))
#define ALIGN_UP(x, n) ((((x) + (n) - 1)) & (~((n) - 1)))
#define ALIGN_WIDTH 32 // Should be 32...but the calculated dimension causes an ion crash
#define ALIGN_HEIGHT 2 // Should be 2...but the calculated dimension causes an ion crash

//temporarily define format here
#define HAL_PIXEL_FORMAT_TI_NV12 0x100
#define HAL_PIXEL_FORMAT_TI_Y8 0x103
#define HAL_PIXEL_FORMAT_TI_Y16 0x104
#define HAL_PIXEL_FORMAT_TI_UYVY 0x105

using namespace android;

static EGLint getSurfaceWidth() {
    return 512;
}

static EGLint getSurfaceHeight() {
    return 512;
}

static size_t calcBufSize(int format, int width, int height)
{
    int buf_size;

    switch (format) {
        case HAL_PIXEL_FORMAT_TI_NV12:
            buf_size = width * height * 3 /2;
            break;
        case HAL_PIXEL_FORMAT_TI_Y16:
        case HAL_PIXEL_FORMAT_TI_UYVY:
            buf_size = width * height * 2;
            break;
        // add more formats later
        default:
            buf_size = width * height * 3 /2;
            break;
    }

    return buf_size;
}

static unsigned int calcOffset(int format, unsigned int width, unsigned int top, unsigned int left)
{
    unsigned int bpp;

    switch (format) {
        case HAL_PIXEL_FORMAT_TI_NV12:
            bpp = 1;
            break;
        case HAL_PIXEL_FORMAT_TI_UYVY:
        case HAL_PIXEL_FORMAT_TI_Y16:
            bpp = 2;
            break;
        // add more formats later
        default:
            bpp = 1;
            break;
    }

    return top * width + left * bpp;
}

static int getHalPixFormat(const char *format)
{
    int pixformat = HAL_PIXEL_FORMAT_TI_NV12;
    if ( NULL != format ) {
        if ( strcmp(format, CameraParameters::PIXEL_FORMAT_BAYER_RGGB) == 0 ) {
            pixformat = HAL_PIXEL_FORMAT_TI_Y16;
        } else if ( strcmp(format, CameraParameters::PIXEL_FORMAT_YUV420SP) == 0 ) {
            pixformat = HAL_PIXEL_FORMAT_TI_NV12;
        } else if ( strcmp(format, CameraParameters::PIXEL_FORMAT_YUV422I) == 0 ) {
            pixformat = HAL_PIXEL_FORMAT_TI_UYVY;
        } else {
            pixformat = HAL_PIXEL_FORMAT_TI_NV12;
        }
    }

    return pixformat;
}

static int getUsageFromANW(int format)
{
    int usage = GRALLOC_USAGE_SW_READ_RARELY |
                GRALLOC_USAGE_SW_WRITE_NEVER;

    switch (format) {
        case HAL_PIXEL_FORMAT_TI_NV12:
        case HAL_PIXEL_FORMAT_TI_Y16:
            // This usage flag indicates to gralloc we want the
            // buffers to come from system heap
            usage |= GRALLOC_USAGE_PRIVATE_0;
            break;
        default:
            // No special flags needed
            break;
    }
    return usage;
}

static status_t writeCroppedNV12(unsigned int offset,
                                 unsigned int stride,
                                 unsigned int bufWidth,
                                 unsigned int bufHeight,
                                 const Rect &crop,
                                 int fd,
                                 unsigned char *buffer)
{
    unsigned char *luma = NULL, *chroma = NULL, *src = NULL;
    unsigned int uvoffset;
    int write_size;

    if (!buffer || !crop.isValid()) {
        return BAD_VALUE;
    }

    src = buffer;
    // offset to beginning of uv plane
    uvoffset =  stride * bufHeight;
    // offset to beginning of valid region of uv plane
    uvoffset += (offset - (offset % stride)) / 2 + (offset % stride);

    // start of valid luma region
    luma = src + offset;
    // start of valid chroma region
    chroma = src + uvoffset;

    // write luma line x line
    unsigned int height = crop.height();
    unsigned int width = crop.width();
    write_size = width;
    for (unsigned int i = 0; i < height; i++) {
        if (write_size != write(fd, luma, width)) {
            printf("Bad Write error (%d)%s\n",
                    errno, strerror(errno));
            return UNKNOWN_ERROR;
        }
        luma += stride;
    }

    // write chroma line x line
    height /= 2;
    write_size = width;
    for (unsigned int i = 0; i < height; i++) {
        if (write_size != write(fd, chroma, width)) {
            printf("Bad Write error (%d)%s\n",
                    errno, strerror(errno));
            return UNKNOWN_ERROR;
        }
        chroma += stride;
    }

    return NO_ERROR;
}

static status_t writeCroppedUYVY(unsigned int offset,
                                 unsigned int stride,
                                 unsigned int bufWidth,
                                 unsigned int bufHeight,
                                 const Rect &crop,
                                 int fd,
                                 unsigned char *buffer)
{
    unsigned char *src = NULL;
    int write_size;

    if (!buffer) {
        return BAD_VALUE;
    }

    src = buffer + offset;
    int height = crop.height();
    int width = crop.width();
    write_size = width*2;
    for (unsigned int i = 0; i < height; i++) {
        if (write_size != write(fd, src, width*2)) {
            printf("Bad Write error (%d)%s\n",
                    errno, strerror(errno));
            return UNKNOWN_ERROR;
        }
        src += stride*2;
    }

    return NO_ERROR;
}

static status_t copyCroppedNV12(unsigned int offset,
                                unsigned int strideSrc,
                                unsigned int strideDst,
                                unsigned int bufWidth,
                                unsigned int bufHeight,
                                const Rect &crop,
                                void *bufferSrc,
                                void *bufferDst)
{
    unsigned char *lumaSrc = NULL, *chromaSrc = NULL;
    unsigned char *lumaDst = NULL, *chromaDst = NULL;
    unsigned int uvoffset;
    int write_size;

    if (!bufferSrc || !bufferDst) {
        return BAD_VALUE;
    }

    uvoffset = strideSrc * crop.height();
    uvoffset += (offset - (offset % strideSrc)) / 2 + (offset % strideSrc);

    lumaSrc = static_cast<unsigned char *>(bufferSrc) + offset;
    chromaSrc = static_cast<unsigned char *>(bufferSrc) + uvoffset;

    int height = crop.height();
    int width = crop.width();

    uvoffset =  strideDst * height;

    lumaDst = static_cast<unsigned char *>(bufferDst);
    chromaDst = static_cast<unsigned char *>(bufferDst) + uvoffset;

    write_size = width;
    for (unsigned int i = 0; i < height; i++) {
        memcpy(lumaDst, lumaSrc, width);
        lumaSrc += strideSrc;
        lumaDst += strideDst;
    }

    height /= 2;
    write_size = width;
    for (unsigned int i = 0; i < height; i++) {
        memcpy(chromaDst, chromaSrc, width);
        chromaSrc += strideSrc;
        chromaDst += strideDst;
    }

    return NO_ERROR;
}

static status_t copyCroppedPacked16(unsigned int offset,
                                    unsigned int stride,
                                    unsigned int bufWidth,
                                    unsigned int bufHeight,
                                    const Rect &crop,
                                    void *bufferSrc,
                                    void *bufferDst)
{
    unsigned char *src = NULL, *dst = NULL;

    if (!bufferSrc || !bufferDst) {
        return BAD_VALUE;
    }

    src = static_cast<unsigned char *>(bufferSrc) + offset;
    dst = static_cast<unsigned char *>(bufferDst);

    int height = crop.height();
    int width = crop.width();
    for (unsigned int i = 0; i < height; i++) {
        memcpy(dst, src, width*2);
        src += stride*2;
        dst += width*2;
    }

    return NO_ERROR;
}

void GLSurface::initialize(int display) {
    mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    ASSERT(EGL_SUCCESS == eglGetError());
    ASSERT(EGL_NO_DISPLAY != mEglDisplay);

    EGLint majorVersion;
    EGLint minorVersion;
    ASSERT(eglInitialize(mEglDisplay, &majorVersion, &minorVersion));
    ASSERT(EGL_SUCCESS == eglGetError());

    EGLint numConfigs = 0;
    ASSERT(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mGlConfig,
                1, &numConfigs));
    ASSERT(EGL_SUCCESS == eglGetError());

    if (display) {
        mComposerClient = new SurfaceComposerClient;
        ASSERT(NO_ERROR == mComposerClient->initCheck());
        mSurfaceControl = mComposerClient->createSurface(
                String8("Test Surface"), 0,
                800, 480, HAL_PIXEL_FORMAT_YCrCb_420_SP, 0);

        ASSERT(mSurfaceControl != NULL);
        ASSERT(mSurfaceControl->isValid());

        SurfaceComposerClient::openGlobalTransaction();
        ASSERT(NO_ERROR == mSurfaceControl->setLayer(0x7FFFFFFF));
        ASSERT(NO_ERROR == mSurfaceControl->show());
        SurfaceComposerClient::closeGlobalTransaction();

        sp<ANativeWindow> window = mSurfaceControl->getSurface();
        mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
                window.get(), NULL);
    } else {
        EGLint pbufferAttribs[] = {
            EGL_WIDTH, getSurfaceWidth(),
            EGL_HEIGHT, getSurfaceHeight(),
            EGL_NONE };
        mEglSurface = eglCreatePbufferSurface(mEglDisplay, mGlConfig,
                pbufferAttribs);
    }
    ASSERT(EGL_SUCCESS == eglGetError());
    ASSERT(EGL_NO_SURFACE != mEglSurface);

    mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT,
            getContextAttribs());
    ASSERT(EGL_SUCCESS == eglGetError());
    ASSERT(EGL_NO_CONTEXT != mEglContext);

    ASSERT(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
            mEglContext));
    ASSERT(EGL_SUCCESS == eglGetError());

    EGLint w, h;
    ASSERT(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &w));
    ASSERT(EGL_SUCCESS == eglGetError());
    ASSERT(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &h));
    ASSERT(EGL_SUCCESS == eglGetError());

    glViewport(0, 0, w, h);
    ASSERT(GLenum(GL_NO_ERROR) == glGetError());
}

void GLSurface::deinit() {
    if (mComposerClient != NULL) {
        mComposerClient->dispose();
    }

    if (mEglContext != EGL_NO_CONTEXT) {
        eglDestroyContext(mEglDisplay, mEglContext);
    }

    if (mEglSurface != EGL_NO_SURFACE) {
        eglDestroySurface(mEglDisplay, mEglSurface);
    }
    if (mEglDisplay != EGL_NO_DISPLAY) {
        eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
               EGL_NO_CONTEXT);
        eglTerminate(mEglDisplay);
    }
    ASSERT(EGL_SUCCESS == eglGetError());
}

EGLint const* GLSurface::getConfigAttribs() {
    static EGLint sDefaultConfigAttribs[] = {
        EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_ALPHA_SIZE, 8,
        EGL_DEPTH_SIZE, 16,
        EGL_STENCIL_SIZE, 8,
        EGL_NONE };

    return sDefaultConfigAttribs;
}

EGLint const* GLSurface::getContextAttribs() {
    static EGLint sDefaultContextAttribs[] = {
        EGL_CONTEXT_CLIENT_VERSION, 2,
        EGL_NONE };

    return sDefaultContextAttribs;
}

void GLSurface::loadShader(GLenum shaderType, const char* pSource, GLuint* outShader) {
    GLuint shader = glCreateShader(shaderType);
    ASSERT(GLenum(GL_NO_ERROR) == glGetError());
    if (shader) {
        glShaderSource(shader, 1, &pSource, NULL);
        ASSERT(GLenum(GL_NO_ERROR) == glGetError());
        glCompileShader(shader);
        ASSERT(GLenum(GL_NO_ERROR) == glGetError());
        GLint compiled = 0;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
        ASSERT(GLenum(GL_NO_ERROR) == glGetError());
        if (!compiled) {
            GLint infoLen = 0;
            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
            ASSERT(GLenum(GL_NO_ERROR) == glGetError());
            if (infoLen) {
                char* buf = (char*) malloc(infoLen);
                if (buf) {
                    glGetShaderInfoLog(shader, infoLen, NULL, buf);
                    printf("Shader compile log:\n%s\n", buf);
                    free(buf);
                }
            } else {
                char* buf = (char*) malloc(0x1000);
                if (buf) {
                    glGetShaderInfoLog(shader, 0x1000, NULL, buf);
                    printf("Shader compile log:\n%s\n", buf);
                    free(buf);
                }
            }
            glDeleteShader(shader);
            shader = 0;
        }
    }
    ASSERT(shader != 0);
    *outShader = shader;
}

void GLSurface::createProgram(const char* pVertexSource, const char* pFragmentSource,
            GLuint* outPgm) {
    GLuint vertexShader, fragmentShader;
    {
        loadShader(GL_VERTEX_SHADER, pVertexSource, &vertexShader);
    }
    {
        loadShader(GL_FRAGMENT_SHADER, pFragmentSource, &fragmentShader);
    }

    GLuint program = glCreateProgram();
    ASSERT(GLenum(GL_NO_ERROR) == glGetError());
    if (program) {
        glAttachShader(program, vertexShader);
        ASSERT(GLenum(GL_NO_ERROR) == glGetError());
        glAttachShader(program, fragmentShader);
        ASSERT(GLenum(GL_NO_ERROR) == glGetError());
        glLinkProgram(program);
        GLint linkStatus = GL_FALSE;
        glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
        if (linkStatus != GL_TRUE) {
            GLint bufLength = 0;
            glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
            if (bufLength) {
                char* buf = (char*) malloc(bufLength);
                if (buf) {
                    glGetProgramInfoLog(program, bufLength, NULL, buf);
                    printf("Program link log:\n%s\n", buf);
                    free(buf);
                }
            }
            glDeleteProgram(program);
            program = 0;
        }
    }
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    ASSERT(program != 0);
    *outPgm = program;
}

// GLConsumer specific
sp<GLConsumer> SurfaceTextureBase::getST() {
     return mST;
}

void SurfaceTextureBase::initialize(int tex_id, EGLenum tex_target) {
    mTexId = tex_id;
    sp<BufferQueue> bq = new BufferQueue();
    mST = new GLConsumer(bq, tex_id, tex_target);
    mSTC = new Surface(mST);
    mANW = mSTC;
}

void SurfaceTextureBase::deinit() {
    mANW.clear();
    mSTC.clear();

    mST->abandon();
    mST.clear();
}

void SurfaceTextureBase::getId(const char **name) {
    sp<ANativeWindow> windowTapOut = mSTC;

    *name = NULL;
    if (windowTapOut.get()) {
        windowTapOut->perform(windowTapOut.get(), NATIVE_WINDOW_GET_ID, name);
    }

    windowTapOut.clear();
}

// GLConsumer with GL specific

void SurfaceTextureGL::initialize(int display, int tex_id) {
    GLSurface::initialize(display);
    SurfaceTextureBase::initialize(tex_id, GL_TEXTURE_EXTERNAL_OES);

    const char vsrc[] =
        "attribute vec4 vPosition;\n"
        "varying vec2 texCoords;\n"
        "uniform mat4 texMatrix;\n"
        "void main() {\n"
        "  vec2 vTexCoords = 0.5 * (vPosition.xy + vec2(1.0, 1.0));\n"
        "  texCoords = (texMatrix * vec4(vTexCoords, 0.0, 1.0)).xy;\n"
        "  gl_Position = vPosition;\n"
        "}\n";

    const char fsrc[] =
        "#extension GL_OES_EGL_image_external : require\n"
        "precision mediump float;\n"
        "uniform samplerExternalOES texSampler;\n"
        "varying vec2 texCoords;\n"
        "void main() {\n"
        "  gl_FragColor = texture2D(texSampler, texCoords);\n"
        "}\n";

    {
        createProgram(vsrc, fsrc, &mPgm);
    }

    mPositionHandle = glGetAttribLocation(mPgm, "vPosition");
    ASSERT(GLenum(GL_NO_ERROR) == glGetError());
    ASSERT(-1 != mPositionHandle);
    mTexSamplerHandle = glGetUniformLocation(mPgm, "texSampler");
    ASSERT(GLenum(GL_NO_ERROR) == glGetError());
    ASSERT(-1 != mTexSamplerHandle);
    mTexMatrixHandle = glGetUniformLocation(mPgm, "texMatrix");
    ASSERT(GLenum(GL_NO_ERROR) == glGetError());
    ASSERT(-1 != mTexMatrixHandle);
}

void SurfaceTextureGL::deinit() {
    SurfaceTextureBase::deinit();
    GLSurface::deinit();
}

// drawTexture draws the GLConsumer over the entire GL viewport.
void SurfaceTextureGL::drawTexture() {
    const GLfloat triangleVertices[] = {
        -1.0f, 1.0f,
        -1.0f, -1.0f,
        1.0f, -1.0f,
        1.0f, 1.0f,
    };

    glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0,
            triangleVertices);
    ASSERT(GLenum(GL_NO_ERROR) == glGetError());
    glEnableVertexAttribArray(mPositionHandle);
    ASSERT(GLenum(GL_NO_ERROR) == glGetError());

    glUseProgram(mPgm);
    glUniform1i(mTexSamplerHandle, 0);
    ASSERT(GLenum(GL_NO_ERROR) == glGetError());
    glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTexId);
    ASSERT(GLenum(GL_NO_ERROR) == glGetError());

    // XXX: These calls are not needed for GL_TEXTURE_EXTERNAL_OES as
    // they're setting the defautls for that target, but when hacking things
    // to use GL_TEXTURE_2D they are needed to achieve the same behavior.
    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER,
            GL_LINEAR);
    ASSERT(GLenum(GL_NO_ERROR) == glGetError());
    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER,
            GL_LINEAR);
    ASSERT(GLenum(GL_NO_ERROR) == glGetError());
    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S,
            GL_CLAMP_TO_EDGE);
    ASSERT(GLenum(GL_NO_ERROR) == glGetError());
    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T,
            GL_CLAMP_TO_EDGE);
    ASSERT(GLenum(GL_NO_ERROR) == glGetError());

    GLfloat texMatrix[16];
    mST->getTransformMatrix(texMatrix);
    glUniformMatrix4fv(mTexMatrixHandle, 1, GL_FALSE, texMatrix);

    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
    ASSERT(GLenum(GL_NO_ERROR) == glGetError());

    eglSwapBuffers(mEglDisplay, mEglSurface);
}

// buffer source stuff
void BufferSourceThread::handleBuffer(sp<GraphicBuffer> &graphic_buffer, uint8_t *buffer,
                                        unsigned int count, const Rect &crop) {
    int size;
    buffer_info_t info;
    unsigned int offset = 0;
    int fd = -1;
    char fn[256];

    if (!graphic_buffer.get()) {
        printf("Invalid graphic_buffer!\n");
        return;
    }

    size = calcBufSize((int)graphic_buffer->getPixelFormat(),
                              graphic_buffer->getWidth(),
                              graphic_buffer->getHeight());
    if (size <= 0) {
        printf("Can't get size!\n");
        return;
    }

    if (!buffer) {
        printf("Invalid mapped buffer!\n");
        return;
    }

    info.size = size;
    info.width = graphic_buffer->getWidth();
    info.height = graphic_buffer->getHeight();
    info.format = graphic_buffer->getPixelFormat();
    info.buf = graphic_buffer;
    info.crop = crop;

    {
        Mutex::Autolock lock(mReturnedBuffersMutex);
        if (mReturnedBuffers.size() >= kReturnedBuffersMaxCapacity) mReturnedBuffers.removeAt(0);
    }

    // re-calculate size and offset
    size = calcBufSize((int) graphic_buffer->getPixelFormat(), crop.width(), crop.height());
    offset = calcOffset((int) graphic_buffer->getPixelFormat(), info.width, crop.top, crop.left);

    // Do not write buffer to file if we are streaming capture
    // It adds too much latency
    if (!mRestartCapture) {
        fn[0] = 0;
        sprintf(fn, "/sdcard/img%03d.raw", count);
        fd = open(fn, O_CREAT | O_WRONLY | O_TRUNC, 0777);
        if (fd >= 0) {
            if (HAL_PIXEL_FORMAT_TI_NV12 == info.format) {
                writeCroppedNV12(offset, info.width, info.width, info.height,
                                 crop, fd, buffer);
            } else if (HAL_PIXEL_FORMAT_TI_UYVY == info.format) {
                writeCroppedUYVY(offset, info.width, info.width, info.height,
                                 crop, fd, buffer);
            } else if (size != write(fd, buffer + offset, size)) {
                printf("Bad Write int a %s error (%d)%s\n", fn, errno, strerror(errno));
            }
            printf("%s: buffer=%08X, size=%d stored at %s\n"
                   "\tRect: top[%d] left[%d] right[%d] bottom[%d] width[%d] height[%d] offset[%d] stride[%d]\n",
                        __FUNCTION__, (int)buffer, size, fn,
                        crop.top, crop.left, crop.right, crop.bottom,
                        crop.width(), crop.height(),
                        offset, info.width);
            close(fd);
        } else {
            printf("error opening or creating %s\n", fn);
        }
    }
}

Rect BufferSourceThread::getCrop(sp<GraphicBuffer> &graphic_buffer, const float *mtx) {
    Rect crop(graphic_buffer->getWidth(), graphic_buffer->getHeight());

    // calculate crop rectangle from tranformation matrix
    float sx, sy, tx, ty, h, w;
    unsigned int rect_x, rect_y;
    /*   sx, 0, 0, 0,
         0, sy, 0, 0,
         0, 0, 1, 0,
         tx, ty, 0, 1 */

    sx = mtx[0];
    sy = mtx[5];
    tx = mtx[12];
    ty = mtx[13];
    w = float(graphic_buffer->getWidth());
    h = float(graphic_buffer->getHeight());

    unsigned int bottom = (unsigned int)(h - (ty * h + 1));
    unsigned int left = (unsigned int)(tx * w -1);
    rect_y = (unsigned int)(fabsf(sy) * h);
    rect_x = (unsigned int)(fabsf(sx) * w);

    // handle v-flip
    if (sy < 0.0f) {
        bottom = h - bottom;
    }

    // handle h-flip
    if (sx < 0.0f) {
        left = w - left;
    }

    unsigned int top = bottom - rect_y;
    unsigned int right = left + rect_x;

    Rect updatedCrop(left, top, right, bottom);
    if (updatedCrop.isValid()) {
        crop = updatedCrop;
    } else {
        printf("Crop for buffer %d is not valid: "
               "left=%u, top=%u, right=%u, bottom=%u. "
               "Will use default.\n",
               mCounter,
               left, top, right, bottom);
    }

    return crop;
}

void BufferSourceInput::setInput(buffer_info_t bufinfo, const char *format, ShotParameters &params) {
    ANativeWindowBuffer* anb;
    GraphicBufferMapper &mapper = GraphicBufferMapper::get();
    int pixformat = HAL_PIXEL_FORMAT_TI_NV12;
    size_t tapInMinUndequeued = 0;

    int aligned_width, aligned_height;

    pixformat = bufinfo.format;

    // Aligning is not needed for Bayer
    if ( ( pixformat == HAL_PIXEL_FORMAT_TI_Y16 ) ||
         ( pixformat == HAL_PIXEL_FORMAT_TI_UYVY ) ) {
        aligned_width = bufinfo.crop.right - bufinfo.crop.left;
    } else {
        aligned_width = ALIGN_UP(bufinfo.crop.right - bufinfo.crop.left, ALIGN_WIDTH);
    }
    aligned_height = bufinfo.crop.bottom - bufinfo.crop.top;
    printf("aligned width: %d height: %d \n", aligned_width, aligned_height);

    if (mWindowTapIn.get() == 0) {
        return;
    }

    native_window_set_usage(mWindowTapIn.get(),
                            getUsageFromANW(pixformat));
    mWindowTapIn->perform(mWindowTapIn.get(),
                          NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
                          &tapInMinUndequeued);;
    native_window_set_buffer_count(mWindowTapIn.get(), tapInMinUndequeued);
    native_window_set_buffers_geometry(mWindowTapIn.get(),
                  aligned_width, aligned_height, bufinfo.format);

    // if buffer dimensions are the same as the aligned dimensions, then we can
    // queue the buffer directly to tapin surface. if the dimensions are different
    // then the aligned ones, then we have to copy the buffer into our own buffer
    // to make sure the stride of the buffer is correct
    if ((aligned_width != bufinfo.width) || (aligned_height != bufinfo.height) ||
        ( pixformat == HAL_PIXEL_FORMAT_TI_Y16 ) ||
        ( pixformat == HAL_PIXEL_FORMAT_TI_UYVY) ) {
        void *dest[3] = { 0 };
        void *src[3] = { 0 };
        Rect bounds(aligned_width, aligned_height);

        mWindowTapIn->dequeueBuffer(mWindowTapIn.get(), &anb);
        mapper.lock(anb->handle, GRALLOC_USAGE_SW_WRITE_OFTEN, bounds, dest);
        // copy buffer to input buffer if available
        if (bufinfo.buf.get()) {
            bufinfo.buf->lock(GRALLOC_USAGE_SW_READ_OFTEN, src);
        }
        if (src[0]) {
            switch (pixformat) {
                case HAL_PIXEL_FORMAT_TI_Y16:
                case HAL_PIXEL_FORMAT_TI_UYVY:
                    copyCroppedPacked16(bufinfo.offset,
                                        bufinfo.width,
                                        bufinfo.width,
                                        bufinfo.height,
                                        bufinfo.crop,
                                        src[0],
                                        dest[0]);
                    break;
                case HAL_PIXEL_FORMAT_TI_NV12:
                    copyCroppedNV12(bufinfo.offset,
                                    bufinfo.width,
                                    aligned_width,
                                    bufinfo.width,
                                    bufinfo.height,
                                    bufinfo.crop,
                                    src[0],
                                    dest[0]);
                    break;
                default:
                    printf("Pixel format 0x%x not supported\n", pixformat);
                    exit(1);
                    break;
            }
        }
        if (bufinfo.buf.get()) {
            bufinfo.buf->unlock();
        }

        mapper.unlock(anb->handle);
    } else {
        mWindowTapIn->perform(mWindowTapIn.get(), NATIVE_WINDOW_ADD_BUFFER_SLOT, &bufinfo.buf);
        anb = bufinfo.buf->getNativeBuffer();
    }

    mWindowTapIn->queueBuffer(mWindowTapIn.get(), anb);

    {
        sp<ANativeWindow> windowTapIn = mWindowTapIn;
        const char* id = NULL;

        if (windowTapIn.get()) {
            windowTapIn->perform(windowTapIn.get(), NATIVE_WINDOW_GET_ID, &id);
        }

        if (id) {
            params.set(KEY_TAP_IN_SURFACE, id);
        } else {
            params.remove(KEY_TAP_IN_SURFACE);
        }

        windowTapIn.clear();
    }
}

void BufferSourceThread::showMetadata(sp<IMemory> data) {
    static nsecs_t prevTime = 0;
    nsecs_t currTime = 0;

    ssize_t offset;
    size_t size;

    if ( NULL == data.get() ) {
        printf("No Metadata!");
        return;
    }

    sp<IMemoryHeap> heap = data->getMemory(&offset, &size);
    camera_metadata_t * meta = static_cast<camera_metadata_t *> (heap->base());

    printf("         frame nmber: %d\n", meta->frame_number);
    printf("         shot number: %d\n", meta->shot_number);
    printf("         analog gain: %d req: %d range: %d~%d dev: %d err: %d\n",
           meta->analog_gain,
           meta->analog_gain_req,
           meta->analog_gain_min,
           meta->analog_gain_max,
           meta->analog_gain_dev,
           meta->analog_gain_error);
    printf("       exposure time: %d req: %d range: %d~%d dev: %d err: %d\n",
           meta->exposure_time,
           meta->exposure_time_req,
           meta->exposure_time_min,
           meta->exposure_time_max,
           meta->exposure_time_dev,
           meta->exposure_time_error);
    printf("     EV compensation: req: %d dev: %d\n",
           meta->exposure_compensation_req,
           meta->exposure_dev);
    printf("            awb gain: %d\n", meta->analog_gain);
    printf("         awb offsets: %d\n", meta->offset_b);
    printf("     awb temperature: %d\n", meta->awb_temp);

    printf("   LSC table applied: %d\n", meta->lsc_table_applied);
    if ( meta->lsc_table_applied ) {
        uint8_t *lscTable = (uint8_t *)meta + meta->lsc_table_offset;
        printf("LSC Table Size:%d Data[0:7]: %d:%d:%d:%d:%d:%d:%d:%d\n",
                meta->lsc_table_size,
                lscTable[0],
                lscTable[1],
                lscTable[2],
                lscTable[3],
                lscTable[4],
                lscTable[5],
                lscTable[6],
                lscTable[7]);
    }

    printf("    Faces detected: %d\n", meta->number_of_faces);

    currTime = meta->timestamp;
    printf("      timestamp (ns): %llu\n", currTime);
    if (prevTime) printf("inter-shot time (ms): %llu\n", (currTime - prevTime) / 1000000l);
    prevTime = currTime;
}