/*
 *  Copyright (c) 2000-2022 Inria
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *  * Neither the name of the ALICE Project-Team nor the names of its
 *  contributors may be used to endorse or promote products derived from this
 *  software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 *  Contact: Bruno Levy
 *
 *     https://www.inria.fr/fr/bruno-levy
 *
 *     Inria,
 *     Domaine de Voluceau,
 *     78150 Le Chesnay - Rocquencourt
 *     FRANCE
 *
 */


#include <geogram/image/image_serializer_stb.h>
#include <geogram/image/image_library.h>
#include <geogram/basic/file_system.h>
#include <geogram/basic/string.h>
#include <geogram/basic/logger.h>

#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION

// Use internal linkage for symbols from stb_image as it is a very
// commonly embedded library.
// Making these symbols visible causes duplicate symbol problems
// if geogram is linked statically together with another library or
// executable that also embeds stb_image.
#define STB_IMAGE_STATIC
#define STB_IMAGE_WRITE_STATIC

// [Bruno] I got too many complaints in STB so I "close my eyes" :-)
#ifdef __GNUC__
#ifndef __ICC
#pragma GCC diagnostic ignored "-Wpragmas"
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wfloat-conversion"
#pragma GCC diagnostic ignored "-Wsign-conversion"
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#pragma GCC diagnostic ignored "-Wdouble-promotion"
#pragma GCC diagnostic ignored "-Wunused-function"
#endif

#ifdef __clang__
#pragma GCC diagnostic ignored "-Wdisabled-macro-expansion"
#pragma GCC diagnostic ignored "-Wcast-align"
#pragma GCC diagnostic ignored "-Wmissing-prototypes"
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wreserved-id-macro"
#pragma GCC diagnostic ignored "-Wcomma"
#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
#pragma GCC diagnostic ignored "-Wcast-qual"
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
#endif

#ifdef _MSC_VER
#pragma warning( disable : 4505 )
#pragma warning( disable : 4244 )
#endif

#include <geogram/third_party/stb_image/stb_image.h>
#include <geogram/third_party/stb_image/stb_image_write.h>

namespace GEO {

    ImageSerializerSTB::ImageSerializerSTB(bool read, bool write) :
        read_(read),
        write_(write)
    {
    }

    Image* ImageSerializerSTB::serialize_read(const std::string& file_name) {
        std::string extension = String::to_lowercase(
            FileSystem::extension(file_name)
        );
        if(
            extension != "png" &&
            extension != "jpg" &&
            extension != "jpeg" &&
            extension != "tga" &&
            extension != "bmp"
        ) {
            return nullptr;
        }

        Image* result = nullptr;
        int width, height, bpp;
        int desired_bpp = 4;
        unsigned char* data = stbi_load(
            file_name.c_str(), &width, &height, &bpp, desired_bpp
        );
        if(data == nullptr) {
            Logger::err("Image")
                << file_name << " : could not load file." << std::endl;
        } else {
            result = new Image(
                Image::RGBA,
                Image::BYTE, index_t(width), index_t(height)
            );
            // bpp: how many bytes per pixel there was in the file
            // desired_bpp set to 4, thus stbi expands it to r,g,b,a always.
            Memory::copy(result->base_mem(), data, size_t(width*height*4));
            stbi_image_free(data);
            // Seems that STB has inverse convention as mine for image Y axis,
            result->flip_vertically();
        }

        return result;
    }

    bool ImageSerializerSTB::serialize_write(
        const std::string& file_name, const Image* image_in
    ) {
        bool result = true;
        std::string extension = String::to_lowercase(
            FileSystem::extension(file_name)
        );

        // Seems that STB has inverse convention as mine for image Y axis,
        // so we flip before saving and flip back after.

        Image* image = const_cast<Image*>(image_in);
        image->flip_vertically();

        int w = int(image->width());
        int h = int(image->height());
        int comp = int(image->components_per_pixel());
        const char* data = (const char*)(image->base_mem());

        if(extension == "png") {
            result = (stbi_write_png(file_name.c_str(), w, h, comp, data, w*comp) != 0);
        } else if(extension == "bmp") {
            result = (stbi_write_bmp(file_name.c_str(), w, h, comp, data) != 0);
        } else if(extension == "jpeg" || extension == "jpg") {
            result = (stbi_write_jpg(file_name.c_str(), w, h, comp, data, 80) != 0);
        } else if(extension == "tga") {
            result = (stbi_write_tga(file_name.c_str(), w, h, comp, data) != 0);
        }

        image->flip_vertically();
        return result;
    }

    bool ImageSerializerSTB::binary() const {
        return true;
    }

    bool ImageSerializerSTB::streams_supported() const {
        return false;
    }

    bool ImageSerializerSTB::read_supported() const {
        return read_;
    }

    bool ImageSerializerSTB::write_supported() const {
        return write_;
    }

    /*****************************************************/

    ImageSerializerSTBRead::ImageSerializerSTBRead() :
        ImageSerializerSTB(true, false) {
    }

    ImageSerializerSTBRead::~ImageSerializerSTBRead() {
    }

    /*****************************************************/

    ImageSerializerSTBReadWrite::ImageSerializerSTBReadWrite() :
        ImageSerializerSTB(true, true) {
    }

    ImageSerializerSTBReadWrite::~ImageSerializerSTBReadWrite() {
    }

    /*****************************************************/

}
