/*
 * Copyright (C) 2010 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 "OpenGLRenderer"

#include <cstring>

#include <utils/Log.h>

#include "Patch.h"

namespace android {
namespace uirenderer {

///////////////////////////////////////////////////////////////////////////////
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////

Patch::Patch(const uint32_t xCount, const uint32_t yCount):
        xCount(xCount + 2), yCount(yCount + 2) {
    verticesCount = (xCount + 2) * (yCount + 2);
    vertices = new TextureVertex[verticesCount];

    // 2 triangles per patch, 3 vertices per triangle
    indicesCount = (xCount + 1) * (yCount + 1) * 2 * 3;
    indices = new uint16_t[indicesCount];

    const uint32_t xNum = xCount + 1;
    const uint32_t yNum = yCount + 1;

    uint16_t* startIndices = indices;
    uint32_t n = 0;
    for (uint32_t y = 0; y < yNum; y++) {
        for (uint32_t x = 0; x < xNum; x++) {
            *startIndices++ = n;
            *startIndices++ = n + 1;
            *startIndices++ = n + xNum + 2;

            *startIndices++ = n;
            *startIndices++ = n + xNum + 2;
            *startIndices++ = n + xNum + 1;

            n += 1;
        }
        n += 1;
    }
}

Patch::~Patch() {
    delete indices;
    delete vertices;
}

///////////////////////////////////////////////////////////////////////////////
// Vertices management
///////////////////////////////////////////////////////////////////////////////

void Patch::updateVertices(const SkBitmap* bitmap, float left, float top, float right,
        float bottom, const int32_t* xDivs,  const int32_t* yDivs, const uint32_t width,
        const uint32_t height) {
    const uint32_t xStretchCount = (width + 1) >> 1;
    const uint32_t yStretchCount = (height + 1) >> 1;

    float xStretch = 0;
    float yStretch = 0;
    float xStretchTex = 0;
    float yStretchTex = 0;

    const float meshWidth = right - left;

    const float bitmapWidth = float(bitmap->width());
    const float bitmapHeight = float(bitmap->height());

    if (xStretchCount > 0) {
        uint32_t stretchSize = 0;
        for (uint32_t i = 1; i < width; i += 2) {
            stretchSize += xDivs[i] - xDivs[i - 1];
        }
        xStretchTex = (stretchSize / bitmapWidth) / xStretchCount;
        const float fixed = bitmapWidth - stretchSize;
        xStretch = (right - left - fixed) / xStretchCount;
    }

    if (yStretchCount > 0) {
        uint32_t stretchSize = 0;
        for (uint32_t i = 1; i < height; i += 2) {
            stretchSize += yDivs[i] - yDivs[i - 1];
        }
        yStretchTex = (stretchSize / bitmapHeight) / yStretchCount;
        const float fixed = bitmapHeight - stretchSize;
        yStretch = (bottom - top - fixed) / yStretchCount;
    }

    float vy = 0.0f;
    float ty = 0.0f;
    TextureVertex* vertex = vertices;

    generateVertices(vertex, 0.0f, 0.0f, xDivs, width, xStretch, xStretchTex,
            meshWidth, bitmapWidth);
    vertex += width + 2;

    for (uint32_t y = 0; y < height; y++) {
        if (y & 1) {
            vy += yStretch;
            ty += yStretchTex;
        } else {
            const float step = float(yDivs[y]);
            vy += step;
            ty += step / bitmapHeight;
        }
        generateVertices(vertex, vy, ty, xDivs, width, xStretch, xStretchTex,
                meshWidth, bitmapWidth);
        vertex += width + 2;
    }

    generateVertices(vertex, bottom - top, 1.0f, xDivs, width, xStretch, xStretchTex,
            meshWidth, bitmapWidth);
}

inline void Patch::generateVertices(TextureVertex* vertex, float y, float v,
        const int32_t xDivs[], uint32_t xCount, float xStretch, float xStretchTex,
        float width, float widthTex) {
    float vx = 0.0f;
    float tx = 0.0f;

    TextureVertex::set(vertex, vx, y, tx, v);
    vertex++;

    for (uint32_t x = 0; x < xCount; x++) {
        if (x & 1) {
            vx += xStretch;
            tx += xStretchTex;
        } else {
            const float step = float(xDivs[x]);
            vx += step;
            tx += step / widthTex;
        }

        TextureVertex::set(vertex, vx, y, tx, v);
        vertex++;
    }

    TextureVertex::set(vertex, width, y, 1.0f, v);
    vertex++;
}

///////////////////////////////////////////////////////////////////////////////
// Debug tools
///////////////////////////////////////////////////////////////////////////////

void Patch::dump() {
    LOGD("Vertices [");
    for (uint32_t y = 0; y < yCount; y++) {
        char buffer[512];
        buffer[0] = '\0';
        uint32_t offset = 0;
        for (uint32_t x = 0; x < xCount; x++) {
            TextureVertex* v = &vertices[y * xCount + x];
            offset += sprintf(&buffer[offset], " (%.4f,%.4f)-(%.4f,%.4f)",
                    v->position[0], v->position[1], v->texture[0], v->texture[1]);
        }
        LOGD("  [%s ]", buffer);
    }
    LOGD("]\nIndices [ ");
    char buffer[4096];
    buffer[0] = '\0';
    uint32_t offset = 0;
    for (uint32_t i = 0; i < indicesCount; i++) {
        offset += sprintf(&buffer[offset], "%d ", indices[i]);
    }
    LOGD("  %s\n]", buffer);
}

}; // namespace uirenderer
}; // namespace android