//
// Copyright 2020 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// png_utils: Wrapper around libpng.
//

#ifdef UNSAFE_BUFFERS_BUILD
#    pragma allow_unsafe_libc_calls
#endif

#include "util/png_utils.h"

#include <array>
#include <cstring>

#include <png.h>

namespace angle
{
namespace
{
class ScopedFILE
{
  public:
    ScopedFILE(FILE *fp) : mFP(fp) {}
    ~ScopedFILE() { close(); }

    FILE *get() const { return mFP; }

    void close()
    {
        if (mFP)
        {
            fclose(mFP);
            mFP = nullptr;
        }
    }

  private:
    FILE *mFP;
};
}  // namespace

bool SavePNGRGB(const char *fileName,
                const char *title,
                uint32_t width,
                uint32_t height,
                const std::vector<uint8_t> &data)
{
    ScopedFILE fp(fopen(fileName, "wb"));
    if (!fp.get())
    {
        fprintf(stderr, "Error opening '%s'.\n", fileName);
        return false;
    }

    png_struct *writeStruct =
        png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
    if (!writeStruct)
    {
        fprintf(stderr, "Error on png_create_write_struct.\n");
        return false;
    }

    png_info *infoStruct = png_create_info_struct(writeStruct);
    if (!infoStruct)
    {
        fprintf(stderr, "Error on png_create_info_struct.\n");
        return false;
    }

    if (setjmp(png_jmpbuf(writeStruct)))
    {
        fp.close();
        png_free_data(writeStruct, infoStruct, PNG_FREE_ALL, -1);
        png_destroy_write_struct(&writeStruct, &infoStruct);
        return false;
    }

    png_init_io(writeStruct, fp.get());

    // Write header (8 bit colour depth)
    png_set_IHDR(writeStruct, infoStruct, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
                 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

    // Set title
    if (title != nullptr && strlen(title) > 0)
    {
        std::array<char, 50> mutableKey = {};
        strcpy(mutableKey.data(), "Title");
        std::array<char, 200> mutableText = {};
        strncpy(mutableText.data(), title, 199);

        png_text titleText;
        titleText.compression = PNG_TEXT_COMPRESSION_NONE;
        titleText.key         = mutableKey.data();
        titleText.text        = mutableText.data();
        png_set_text(writeStruct, infoStruct, &titleText, 1);
    }

    png_write_info(writeStruct, infoStruct);

    // RGB 3-byte stride.
    const uint32_t rowStride = width * 3;
    for (uint32_t row = 0; row < height; ++row)
    {
        uint32_t rowOffset = row * rowStride;
        png_write_row(writeStruct, &data[rowOffset]);
    }

    png_write_end(writeStruct, infoStruct);

    fp.close();
    png_free_data(writeStruct, infoStruct, PNG_FREE_ALL, -1);
    png_destroy_write_struct(&writeStruct, &infoStruct);
    return true;
}
}  // namespace angle
