/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2021 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_PALETTE_HPP
#define MAPNIK_PALETTE_HPP

// mapnik
#include <mapnik/config.hpp>
#include <mapnik/global.hpp>
#include <mapnik/util/noncopyable.hpp>

#define USE_DENSE_HASH_MAP

#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#ifdef USE_DENSE_HASH_MAP
#include <mapnik/sparsehash/dense_hash_map>
using rgba_hash_table = google::dense_hash_map<unsigned int, unsigned char>;
#else
#include <unordered_map>
using rgba_hash_table = std::unordered_map<unsigned int, unsigned char>;
#endif
#pragma GCC diagnostic pop

// stl
#include <vector>
#include <tuple>

#define U2RED(x) ((x)&0xff)
#define U2GREEN(x) (((x)>>8)&0xff)
#define U2BLUE(x) (((x)>>16)&0xff)
#define U2ALPHA(x) (((x)>>24)&0xff)

namespace mapnik {

struct rgba;

struct MAPNIK_DECL rgb
{
    std::uint8_t r;
    std::uint8_t g;
    std::uint8_t b;

    inline rgb(std::uint8_t r_, std::uint8_t g_, std::uint8_t b_) : r(r_), g(g_), b(b_) {}
    rgb(rgba const& c);

    inline bool operator==(const rgb& y) const
    {
        return r == y.r && g == y.g && b == y.b;
    }
};

struct MAPNIK_DECL rgba
{
    std::uint8_t r;
    std::uint8_t g;
    std::uint8_t b;
    std::uint8_t a;

    inline rgba(std::uint8_t r_, std::uint8_t g_, std::uint8_t b_, std::uint8_t a_)
        : r(r_),
          g(g_),
          b(b_),
          a(a_) {}

    inline rgba(rgb const& c)
        : r(c.r),
          g(c.g),
          b(c.b),
          a(0xFF) {}

    inline rgba(unsigned const& c)
        : r(U2RED(c)),
          g(U2GREEN(c)),
          b(U2BLUE(c)),
          a(U2ALPHA(c)) {}

    inline bool operator==(rgba const& y) const
    {
        return r == y.r && g == y.g && b == y.b && a == y.a;
    }

    // ordering by mean(a,r,g,b), a, r, g, b
    struct MAPNIK_DECL mean_sort_cmp
    {
        bool operator() (const rgba& x, const rgba& y) const;
    };

    inline bool operator<(rgba const& y) const
    {
        return std::tie(r, g, b, a) < std::tie(y.r, y.g, y.b, y.a);
    }

};


class MAPNIK_DECL rgba_palette : private util::noncopyable
{
public:
    enum palette_type { PALETTE_RGBA = 0, PALETTE_RGB = 1, PALETTE_ACT = 2 };

    explicit rgba_palette(std::string const& pal, palette_type type = PALETTE_RGBA);
    rgba_palette();

    inline std::vector<rgb> const& palette() const { return rgb_pal_;}
    inline std::vector<unsigned> const& alpha_table() const { return alpha_pal_;}

    inline std::vector<rgb>& palette() { return rgb_pal_;}
    inline std::vector<unsigned>& alpha_table() { return alpha_pal_;}

    unsigned char quantize(unsigned c) const;

    bool valid() const;
    std::string to_string() const;

private:
    void parse(std::string const& pal, palette_type type);

private:
    std::vector<rgba> sorted_pal_;
    mutable rgba_hash_table color_hashmap_;

    unsigned colors_;
    std::vector<rgb> rgb_pal_;
    std::vector<unsigned> alpha_pal_;
};

} // namespace mapnik

#endif // MAPNIK_PALETTE_HPP
