/*
 * Copyright (C) 2017 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.
 */

#include <gui/LayerDebugInfo.h>

#include <android-base/stringprintf.h>

#include <ui/DebugUtils.h>

#include <binder/Parcel.h>

using namespace android;
using android::base::StringAppendF;

#define RETURN_ON_ERROR(X) do {status_t res = (X); if (res != NO_ERROR) return res;} while(false)

namespace android {

status_t LayerDebugInfo::writeToParcel(Parcel* parcel) const {
    RETURN_ON_ERROR(parcel->writeCString(mName.c_str()));
    RETURN_ON_ERROR(parcel->writeCString(mParentName.c_str()));
    RETURN_ON_ERROR(parcel->writeCString(mType.c_str()));
    RETURN_ON_ERROR(parcel->write(mTransparentRegion));
    RETURN_ON_ERROR(parcel->write(mVisibleRegion));
    RETURN_ON_ERROR(parcel->write(mSurfaceDamageRegion));
    RETURN_ON_ERROR(parcel->writeUint32(mLayerStack));
    RETURN_ON_ERROR(parcel->writeFloat(mX));
    RETURN_ON_ERROR(parcel->writeFloat(mY));
    RETURN_ON_ERROR(parcel->writeUint32(mZ));
    RETURN_ON_ERROR(parcel->writeInt32(mWidth));
    RETURN_ON_ERROR(parcel->writeInt32(mHeight));
    RETURN_ON_ERROR(parcel->write(mCrop));
    RETURN_ON_ERROR(parcel->writeFloat(mColor.r));
    RETURN_ON_ERROR(parcel->writeFloat(mColor.g));
    RETURN_ON_ERROR(parcel->writeFloat(mColor.b));
    RETURN_ON_ERROR(parcel->writeFloat(mColor.a));
    RETURN_ON_ERROR(parcel->writeUint32(mFlags));
    RETURN_ON_ERROR(parcel->writeInt32(mPixelFormat));
    RETURN_ON_ERROR(parcel->writeUint32(static_cast<uint32_t>(mDataSpace)));
    for (size_t index = 0; index < 4; index++) {
        RETURN_ON_ERROR(parcel->writeFloat(mMatrix[index / 2][index % 2]));
    }
    RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferWidth));
    RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferHeight));
    RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferStride));
    RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferFormat));
    RETURN_ON_ERROR(parcel->writeInt32(mNumQueuedFrames));
    RETURN_ON_ERROR(parcel->writeBool(mIsOpaque));
    RETURN_ON_ERROR(parcel->writeBool(mContentDirty));
    RETURN_ON_ERROR(parcel->write(mStretchEffect));
    return NO_ERROR;
}

status_t LayerDebugInfo::readFromParcel(const Parcel* parcel) {
    mName = parcel->readCString();
    RETURN_ON_ERROR(parcel->errorCheck());
    mParentName = parcel->readCString();
    RETURN_ON_ERROR(parcel->errorCheck());
    mType = parcel->readCString();
    RETURN_ON_ERROR(parcel->errorCheck());
    RETURN_ON_ERROR(parcel->read(mTransparentRegion));
    RETURN_ON_ERROR(parcel->read(mVisibleRegion));
    RETURN_ON_ERROR(parcel->read(mSurfaceDamageRegion));
    RETURN_ON_ERROR(parcel->readUint32(&mLayerStack));
    RETURN_ON_ERROR(parcel->readFloat(&mX));
    RETURN_ON_ERROR(parcel->readFloat(&mY));
    RETURN_ON_ERROR(parcel->readUint32(&mZ));
    RETURN_ON_ERROR(parcel->readInt32(&mWidth));
    RETURN_ON_ERROR(parcel->readInt32(&mHeight));
    RETURN_ON_ERROR(parcel->read(mCrop));
    mColor.r = parcel->readFloat();
    RETURN_ON_ERROR(parcel->errorCheck());
    mColor.g = parcel->readFloat();
    RETURN_ON_ERROR(parcel->errorCheck());
    mColor.b = parcel->readFloat();
    RETURN_ON_ERROR(parcel->errorCheck());
    mColor.a = parcel->readFloat();
    RETURN_ON_ERROR(parcel->errorCheck());
    RETURN_ON_ERROR(parcel->readUint32(&mFlags));
    RETURN_ON_ERROR(parcel->readInt32(&mPixelFormat));
    // \todo [2017-07-25 kraita]: Static casting mDataSpace pointer to an uint32 does work. Better ways?
    mDataSpace = static_cast<android_dataspace>(parcel->readUint32());
    RETURN_ON_ERROR(parcel->errorCheck());
    for (size_t index = 0; index < 4; index++) {
        RETURN_ON_ERROR(parcel->readFloat(&mMatrix[index / 2][index % 2]));
    }
    RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferWidth));
    RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferHeight));
    RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferStride));
    RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferFormat));
    RETURN_ON_ERROR(parcel->readInt32(&mNumQueuedFrames));
    RETURN_ON_ERROR(parcel->readBool(&mIsOpaque));
    RETURN_ON_ERROR(parcel->readBool(&mContentDirty));
    RETURN_ON_ERROR(parcel->read(mStretchEffect));
    return NO_ERROR;
}

std::string to_string(const LayerDebugInfo& info) {
    std::string result;

    StringAppendF(&result, "+ %s (%s)\n", info.mType.c_str(), info.mName.c_str());
    info.mTransparentRegion.dump(result, "TransparentRegion");
    info.mVisibleRegion.dump(result, "VisibleRegion");
    info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion");
    if (info.mStretchEffect.hasEffect()) {
        const auto& se = info.mStretchEffect;
        StringAppendF(&result,
                      "  StretchEffect width = %f, height = %f vec=(%f, %f) "
                      "maxAmount=(%f, %f)\n",
                      se.width, se.height,
                      se.vectorX, se.vectorY, se.maxAmountX, se.maxAmountY);
    }

    StringAppendF(&result, "      layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ",
                  info.mLayerStack, info.mZ, static_cast<double>(info.mX),
                  static_cast<double>(info.mY), info.mWidth, info.mHeight);

    StringAppendF(&result, "crop=%s, ", to_string(info.mCrop).c_str());
    StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", info.mIsOpaque, info.mContentDirty);
    StringAppendF(&result, "dataspace=%s, ", dataspaceDetails(info.mDataSpace).c_str());
    StringAppendF(&result, "pixelformat=%s, ", decodePixelFormat(info.mPixelFormat).c_str());
    StringAppendF(&result, "color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ",
                  static_cast<double>(info.mColor.r), static_cast<double>(info.mColor.g),
                  static_cast<double>(info.mColor.b), static_cast<double>(info.mColor.a),
                  info.mFlags);
    StringAppendF(&result, "tr=[%.2f, %.2f][%.2f, %.2f]", static_cast<double>(info.mMatrix[0][0]),
                  static_cast<double>(info.mMatrix[0][1]), static_cast<double>(info.mMatrix[1][0]),
                  static_cast<double>(info.mMatrix[1][1]));
    result.append("\n");
    StringAppendF(&result, "      parent=%s\n", info.mParentName.c_str());
    StringAppendF(&result, "      activeBuffer=[%4ux%4u:%4u,%s],", info.mActiveBufferWidth,
                  info.mActiveBufferHeight, info.mActiveBufferStride,
                  decodePixelFormat(info.mActiveBufferFormat).c_str());
    StringAppendF(&result, " queued-frames=%d", info.mNumQueuedFrames);
    result.append("\n");
    return result;
}

} // android
