// -*- mode: c++ -*-
//-----------------------------------------------------------------------------
//
// Copyright(C) 2016 Zohar Malamant
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
//-----------------------------------------------------------------------------

#ifndef __IMP_PIXEL__13206666
#define __IMP_PIXEL__13206666

#include "Prelude"
#include "util/PtrIterator"

/*
 * This header defines classes for low-level pixel operations,
 * as well as the Palette class.
 *
 * We distinguish pixel data into two groups: Color and Index.
 * Color pixels are pixels whose data is in-band. (ex: Rgb)
 * Index pixels are pixels whose color values can be found somewhere else, like a Palette. (ex: Index8)
 */

namespace imp {
  namespace gfx {
    class Palette;

    /**
     * \brief Exception class for pixel format errors.
     */
    struct PixelFormatError : public std::logic_error {
        using std::logic_error::logic_error;

        PixelFormatError();
    };

    enum struct PixelFormat {
        none,
        index8,
        rgb,
        rgba,
    };

    namespace detail {
      template <int, int LShift, typename std::enable_if<LShift <= 0>::type* = nullptr>
      constexpr size_t resize_component_shift(size_t x)
      {
          return x >> -LShift;
      }

      template <int FromBits, int LShift, typename std::enable_if<(LShift > 0)>::type* = nullptr>
      constexpr size_t resize_component_shift(size_t x)
      {
          return (x << LShift) + resize_component_shift<FromBits, LShift - FromBits>(x);
      }

      template <int FromDepth, int ToDepth>
      constexpr size_t resize_component(size_t x)
      {
          return resize_component_shift<FromDepth, ToDepth - FromDepth>(x);
      };
    }

    struct Index8 {
        uint8 index;
    };

#define DEFINE_COLOR_COMPONENT(ClassName, VarName)                    \
    template <size_t Depth>                                           \
    struct ClassName ## Component {                                   \
        using VarName ## _type = size_t;                              \
        static constexpr size_t VarName ## _depth = Depth;            \
        static constexpr size_t VarName ## _min = 0;                  \
        static constexpr size_t VarName ## _max = (1U << Depth) - 1U; \
        size_t VarName : Depth;                                       \
        constexpr ClassName ## Component() {}                         \
        constexpr ClassName ## Component(size_t x):                   \
                             VarName(x) {}                            \
    };                                                                \
    namespace detail {                                                \
      template <class C, typename = void>                             \
      struct ClassName ## Helper {                                    \
          constexpr static bool have = false;                         \
          constexpr static size_t depth = 0;                          \
          constexpr static size_t get(const C&) { return 0; }         \
          constexpr static void set(C&, size_t) {}                    \
      };                                                              \
      template <class C>                                              \
      struct ClassName ## Helper<C, void_t<decltype(C::VarName)>> {   \
          constexpr static bool have = true;                          \
          constexpr static size_t depth = C::VarName ## _depth;       \
          constexpr static size_t get(const C& c)                     \
          { return c.VarName; }                                       \
          constexpr static void set(C& c, size_t x)                   \
          { c.VarName = x; }                                          \
      };                                                              \
      template <class From, class To>                                 \
      constexpr void convert_ ## VarName(const From& from, To& to)    \
      {                                                               \
          using FromH = ClassName ## Helper<From>;                    \
          using ToH = ClassName ## Helper<To>;                        \
          auto val = FromH::get(from);                                \
          val = resize_component<FromH::depth, ToH::depth>(val);      \
          ToH::set(to, val);                                          \
      }                                                               \
    }

    DEFINE_COLOR_COMPONENT(Red, red)
    DEFINE_COLOR_COMPONENT(Green, green)
    DEFINE_COLOR_COMPONENT(Blue, blue)
    DEFINE_COLOR_COMPONENT(Alpha, alpha)

    template <class... Components>
    struct ColorComponents;

    template <>
    struct ColorComponents<> {
        constexpr ColorComponents() = default;
    };

    template <class C, class... Cs>
    struct ColorComponents<C, Cs...> : public C, public ColorComponents<Cs...> {
        constexpr ColorComponents() = default;

        template <class T, class... Ts>
        constexpr ColorComponents(T&& x, Ts&&... xs):
            C(std::forward<T>(x)),
            ColorComponents<Cs...>(std::forward<Ts>(xs)...) {};
    };

    template <class... Components>
    struct Color : ColorComponents<Components...> {
        constexpr Color() = default;

        constexpr Color(const Color&) = default;

        constexpr Color(Color&&) = default;

        template <class... Ts>
        constexpr Color(Ts&&... xs):
            ColorComponents<Components...>(std::forward<Ts>(xs)...) {}

        template <class... Cs>
        constexpr Color(const Color<Cs...>& other):
            Color()
        {
            detail::convert_red(other, *this);
            detail::convert_green(other, *this);
            detail::convert_blue(other, *this);
            detail::convert_alpha(other, *this);
        }

        constexpr Color& operator=(const Color&) = default;

        constexpr Color& operator=(Color&&) = default;
    };

    using Red8 = Color<RedComponent<8>>;

    struct Rgb {
        uint8 red = 0;
        uint8 green = 0;
        uint8 blue = 0;

        constexpr Rgb() = default;

        constexpr Rgb(uint8 pRed, uint8 pGreen, uint8 pBlue):
            red(pRed),
            green(pGreen),
            blue(pBlue) {}
    };

    struct Rgba {
        uint8 red = 0;
        uint8 green = 0;
        uint8 blue = 0;
        uint8 alpha = 0;

        constexpr Rgba() = default;

        constexpr Rgba(uint8 pRed, uint8 pGreen, uint8 pBlue, uint8 pAlpha):
            red(pRed),
            green(pGreen),
            blue(pBlue),
            alpha(pAlpha) {}
    };

    struct index8_tag {
        using type = Index8;
    };

    struct rgb_tag {
        using type = Rgb;
    };

    struct rgba_tag {
        using type = Rgba;
    };

    /**
     * \brief Traits class for pixels
     *
     * When defining a new pixel, specialise this template with the following static constexpr variables:
     * - `PixelFormat format`: The corresponding PixelFormat (eg. `pixel_traits<Rgb>::format == PixelFormat::rgb`)
     * - `bool color`: True if `T` describes a colour (eg. Rgb), false otherwise (eg. Index8)
     * - `size_t bytes`: Size of a single pixel in bytes
     * - `bool alpha`: Always false if `color == false`. True if pixel has alpha-channel, false otherwise
     * - `size_t pal_size`: Always 0 if `color == true`. Maximum number of colours that can be referenced
     *
     * ... as well as these types:
     * - `tag_type`: The corresponding tag struct type (eg. `Rgb` has `struct rgb_tag`)
     *
     * ... and these static constexpr functions:
     * - `<tag_type> tag()`: Convenience function that returns an instance of `tag_type`
     */
    template <class T>
	struct pixel_traits {
		static constexpr bool valid = false;
        static constexpr auto format = PixelFormat::none;
        static constexpr bool color = false;
        static constexpr size_t bytes = 0;
        static constexpr bool alpha = false;

        static constexpr size_t pal_size = 0;

        struct tag_type {};
        static constexpr auto tag() { return tag_type(); }
	};

    template <>
    struct pixel_traits<Index8> {
		static constexpr bool valid = true;
        static constexpr auto format = PixelFormat::index8;
        static constexpr bool color = false;
        static constexpr size_t bytes = 1;
        static constexpr bool alpha = false;

        static constexpr size_t pal_size = 256;

        using tag_type = index8_tag;
        static constexpr auto tag() { return tag_type(); }
    };

    template <>
    struct pixel_traits<Rgb> {
		static constexpr bool valid = true;
        static constexpr auto format = PixelFormat::rgb;
        static constexpr bool color = true;
        static constexpr size_t bytes = 3;
        static constexpr bool alpha = false;

        static constexpr size_t pal_size = 0;

        using tag_type = rgb_tag;
        static constexpr auto tag() { return tag_type(); }
    };

    template <>
    struct pixel_traits<Rgba> {
		static constexpr bool valid = true;
        static constexpr auto format = PixelFormat::rgba;
        static constexpr bool color = true;
        static constexpr size_t bytes = 4;
        static constexpr bool alpha = true;

        static constexpr size_t pal_size = 0;

        using tag_type = rgba_tag;
        static constexpr auto tag() { return tag_type(); }
    };

    /**
     * \brief Determine if a type is a pixel
     * \tparam T Type to identify
     */
    template <class T>
    constexpr bool is_pixel = pixel_traits<T>::valid;

    /**
     * \brief Determine if a type is a colour pixel
     * \tparam T Type to identify
     */
	template <class T>
	constexpr bool is_color_pixel = pixel_traits<T>::color;

    /**
     * \brief Determine if a type is an index pixel
     * \tparam T Type to identify
     */
	template <class T>
	constexpr bool is_index_pixel = is_pixel<T> && !pixel_traits<T>::color;

    /**
     * \brief A version of pixel_traits that's available at runtime
     *
     * When adding a new static constexpr field to pixel_traits, add it here
     * as well.
     */
    struct PixelInfo {
        PixelFormat format = PixelFormat::none;
        bool color = false;
        size_t bytes = 0;
        bool alpha = false;

        size_t pal_size = 0;
        int pal_mask = 0;
    };

    /**
     * \brief Get an instance of PixelInfo based on PixelFormat
     *
     * \param format Find PixelInfo that has this format
     *
     * \return An instance of PixelInfo
     *
     * \exception PixelFormatError When PixelInfo isn't found
     */
    const PixelInfo& get_pixel_info(PixelFormat format);

    /**
     * \brief Check if T and traits have the same format
     *
     * \tparam T pixel type to compare
     *
     * \param traits PixelInfo to compare
     *
     * \exception PixelFormatError If T and traits don't have the same format
     */
    template <class T>
    inline void test_pixel_format(const PixelInfo *traits)
    {
        if (pixel_traits<std::decay_t<T>>::format != traits->format)
            throw PixelFormatError();
    }

    /**
     * \brief A RandomAccessIterator pixels that supports conversions
     *
     * \tparam SrcT Type of pixels in the underlying contiguous array we're iterating over
     * \tparam DstT Type of pixels to convert to
     */
    template <class SrcT, class DstT>
    struct PixelIterator {
        static_assert(std::is_const<DstT>::value, "Non-const destination pixel type is currently unsupported");
        static_assert(pixel_traits<SrcT>::color, "Iterating over indexed pixels is currently unsupported");
        static_assert(pixel_traits<DstT>::color, "Converting to indexed pixels is currently unsupported");

        SrcT *mPtr;
        DstT mVal;

        constexpr void _convert()
        { mVal = convert_pixel(*mPtr, pixel_traits<DstT>::tag()); }

    public:
        using value_type = DstT;
        using pointer = DstT*;
        using const_pointer = const DstT*;
        using reference = DstT&;
        using const_reference = const DstT&;
        using iterator = SrcT*;
        using const_iterator = const SrcT*;
        using difference_type = std::ptrdiff_t;

        constexpr PixelIterator(iterator ptr):
            mPtr(ptr)
        {
            _convert();
        }

        constexpr PixelIterator(const PixelIterator &other):
            mPtr(other.mPtr),
            mVal(other.mVal) {}

        constexpr PixelIterator& operator=(const PixelIterator &other)
        {
            mPtr = other.mPtr;
            mVal = other.mVal;
            return *this;
        }

        constexpr iterator get()
        { return mPtr; }

        constexpr const_iterator get() const
        { return mPtr; }

        constexpr reference operator*()
        { return mVal; }

        constexpr const_reference operator*() const
        { return mVal; }

        constexpr pointer operator->()
        { return &mVal; }

        constexpr const_pointer operator->() const
        { return &mVal; }

        constexpr PixelIterator& operator++()
        {
            ++mPtr;
            _convert();
            return *this;
        }

        constexpr PixelIterator operator++(int)
        {
            auto copy = *this;
            ++(*this);
            return copy;
        }

        constexpr PixelIterator& operator--()
        {
            --mPtr;
            _convert();
            return *this;
        }

        constexpr PixelIterator operator--(int)
        {
            auto copy = *this;
            --(*this);
            return copy;
        }

        constexpr PixelIterator operator+(difference_type idx) const
        {
            auto copy = *this;
            copy.mPtr += idx;
            copy._convert();
            return copy;
        }

        constexpr PixelIterator operator-(difference_type idx) const
        {
            auto copy = *this;
            copy.mPtr -= idx;
            copy._convert();
            return copy;
        }

        constexpr PixelIterator& operator+=(difference_type idx)
        {
            mPtr += idx;
            _convert();
            return *this;
        }

        constexpr PixelIterator& operator-=(difference_type idx)
        {
            mPtr -= idx;
            _convert();
            return *this;
        }

        void swap(PixelIterator &other)
        {
            std::swap(mPtr, other.mPtr);
            std::swap(mVal, other.mVal);
        }
    };

#define __IMP_OPERATOR(Op) \
    template <class SrcT, class DstT> \
    constexpr bool operator Op(const PixelIterator<SrcT, DstT> &lhs, const PixelIterator<SrcT, DstT> &rhs) \
    { return lhs.get() Op rhs.get(); }

    __IMP_OPERATOR(==)
    __IMP_OPERATOR(!=)
    __IMP_OPERATOR(<)
    __IMP_OPERATOR(<=)
    __IMP_OPERATOR(>)
    __IMP_OPERATOR(>=)

#undef __IMP_OPERATOR

    /**
     * \brief Identity pixel iterator specialisation
     *
     * If SrcT is the same type as DstT, we can just interpret the memory as SrcT
     */
    template <class T>
    struct PixelIterator<T, T> : PtrIterator<T>
    { using PtrIterator<T>::PtrIterator; };

    /**
     * \brief A non-owning pixel container that maps between two pixel types
     */
    template <class SrcT, class DstT>
    class PixelMap {
        SrcT *mBegin;
        SrcT *mEnd;

    public:
        using iterator = PixelIterator<SrcT, DstT>;
        using const_iterator = PixelIterator<const SrcT, const DstT>;

        PixelMap(SrcT *begin, SrcT *end):
            mBegin(begin),
            mEnd(end) {}

        iterator begin()
        { return { mBegin }; }

        const_iterator begin() const
        { return { mBegin }; }

        const_iterator cbegin() const
        { return { mBegin }; }

        iterator end()
        { return { mEnd }; }

        const_iterator end() const
        { return { mEnd }; }

        const_iterator cend() const
        { return { mEnd }; }
    };

    class Palette {
        const PixelInfo *mTraits = nullptr;
        size_t mCount = 0;
        std::unique_ptr<byte[]> mData = nullptr;

    public:
        Palette() = default;

        Palette(Palette&&) noexcept = default;

        Palette(const Palette &);

        Palette(PixelFormat format, size_t count, const byte* data);

        explicit Palette(PixelFormat format, size_t count, std::unique_ptr<byte[]> data);

        template <class T, size_t N>
        Palette(T (&data)[N]):
            Palette(pixel_traits<T>::format, N, reinterpret_cast<const byte*>(data)) {}

        Palette& operator=(Palette&&) noexcept = default;

        Palette& operator=(const Palette &);

        void reset()
        { *this = Palette(); }

        bool empty() const
        { return !(mTraits && mCount && mData); }

        byte *data_ptr()
        { return mData.get(); }

        const byte *data_ptr() const
        { return mData.get(); }

        PixelFormat format() const
        { return mTraits->format; }

        const PixelInfo &traits() const
        { return *mTraits; }

        size_t count() const
        { return mCount; }

        template<class T>
        T& color_unsafe(size_t x)
        { return *(reinterpret_cast<T*>(mData.get()) + x); }

        template<class T>
        const T& color_unsafe(size_t x) const
        { return *(reinterpret_cast<const T*>(mData.get()) + x); }

        template <class SrcT, class DstT = SrcT>
        PixelMap<SrcT, DstT> map()
        {
            test_pixel_format<SrcT>(mTraits);

            auto ptr = reinterpret_cast<SrcT*>(mData.get());
            return { ptr, ptr + mCount };
        }

        template <class SrcT, class DstT = SrcT>
        PixelMap<const SrcT, const DstT> map() const
        {
            test_pixel_format<SrcT>(mTraits);

            auto ptr = reinterpret_cast<const SrcT*>(mData.get());
            return { ptr, ptr + mCount };
        }

        static std::shared_ptr<const Palette> black();

        static std::shared_ptr<const Palette> playpal();
    };

    template <class RetT = void>
    struct DefaultPixelProcessor {
        template <class T, class, class... Args>
        RetT color(Args&&...)
        { throw PixelFormatError("color pixel processor not implemented"); }

        template <class T, class PalT, class... Args>
        RetT index(Args&&...)
        { throw PixelFormatError("index pixel processor not implemented"); }
    };

    template <class RetT = void>
    struct DefaultPixelTransform {
        template <class SrcT, class, class DstT, class, class... Args>
        RetT color_to_color(Args&&...)
        { throw PixelFormatError("color_to_color pixel processor not implemented"); }

        template <class SrcT, class, class DstT, class DstPalT, class... Args>
        RetT color_to_index(Args&&...)
        { throw PixelFormatError("color_to_index pixel processor not implemented"); }

        template <class SrcT, class SrcPalT, class DstT, class, class... Args>
        RetT index_to_color(Args&&...)
        { throw PixelFormatError("index_to_color pixel processor not implemented"); }

        template <class SrcT, class SrcPalT, class DstT, class DstPalT, class... Args>
        RetT index_to_index(Args&&...)
        { throw PixelFormatError("index_to_index pixel processor not implemented"); }
    };

    constexpr int __h_process_pixel_id(PixelFormat srcFmt, PixelFormat srcPalFmt,
                                       PixelFormat dstFmt = PixelFormat::none,
                                       PixelFormat dstPalFmt = PixelFormat::none)
    {
        return (static_cast<int>(srcFmt)) | (static_cast<int>(srcPalFmt) << 8) |
            (static_cast<int>(dstFmt) << 16) | (static_cast<int>(dstPalFmt) << 24);
    }

    template <class Processor, class RetT = void>
    RetT process_pixel(PixelFormat fmt, PixelFormat palFmt, Processor proc = Processor())
    {
        int id = __h_process_pixel_id(fmt, palFmt);

#define __IMP_CASE(Type, PalType, Func) \
        case __h_process_pixel_id(pixel_traits<Type>::format, pixel_traits<PalType>::format): \
            return proc.template Func<Type, PalType>(); \
            break;

#define __IMP_CASE_C(Type) \
        __IMP_CASE(Type, void, color)

#define __IMP_CASE_I(Type, PalType) \
        __IMP_CASE(Type, PalType, index)

        switch (id)
        {
        __IMP_CASE_C(Rgb)
        __IMP_CASE_C(Rgba)
        __IMP_CASE_I(Index8, Rgb)
        __IMP_CASE_I(Index8, Rgba)

        default:
            throw PixelFormatError("process_pixel doesn't have a case for this pixel type");
        }

#undef __IMP_CASE_C
#undef __IMP_CASE_I
#undef __IMP_CASE
    };

    template <class Processor, class RetT = void>
    RetT transform_pixel(PixelFormat srcFmt, PixelFormat srcPalFmt,
                                 PixelFormat dstFmt, PixelFormat dstPalFmt, Processor &proc = Processor())
    {
        int id = __h_process_pixel_id(srcFmt, srcPalFmt, dstFmt, dstPalFmt);

#define __IMP_CASE(Src, SrcPal, Dst, DstPal, Func) \
        case __h_process_pixel_id(pixel_traits<Src>::format, pixel_traits<SrcPal>::format, \
                                  pixel_traits<Dst>::format, pixel_traits<DstPal>::format): \
            return proc.template Func<Src, SrcPal, Dst, DstPal>(); \
            break;

#define __IMP_CASE_CC(Src, Dst) \
        __IMP_CASE(Src, void, Dst, void, color_to_color)

#define __IMP_CASE_CI(Src, Dst, DstPal) \
        __IMP_CASE(Src, void, Dst, DstPal, color_to_index)

#define __IMP_CASE_IC(Src, SrcPal, Dst) \
        __IMP_CASE(Src, SrcPal, Dst, void, index_to_color)

#define __IMP_CASE_II(Src, SrcPal, Dst, DstPal) \
        __IMP_CASE(Src, SrcPal, Dst, DstPal, index_to_index)

        switch (id)
        {

        __IMP_CASE_CC(Rgb, Rgb)
        __IMP_CASE_CC(Rgb, Rgba)
        __IMP_CASE_CC(Rgba, Rgb)
        __IMP_CASE_CC(Rgba, Rgba)

        __IMP_CASE_CI(Rgb, Index8, Rgb)
        __IMP_CASE_CI(Rgb, Index8, Rgba)
        __IMP_CASE_CI(Rgba, Index8, Rgb)
        __IMP_CASE_CI(Rgba, Index8, Rgba)

        __IMP_CASE_IC(Index8, Rgb, Rgb)
        __IMP_CASE_IC(Index8, Rgb, Rgba)
        __IMP_CASE_IC(Index8, Rgba, Rgb)
        __IMP_CASE_IC(Index8, Rgba, Rgba)

        __IMP_CASE_II(Index8, Rgb, Index8, Rgb)
        __IMP_CASE_II(Index8, Rgb, Index8, Rgba)
        __IMP_CASE_II(Index8, Rgba, Index8, Rgb)
        __IMP_CASE_II(Index8, Rgba, Index8, Rgba)

        default:
            throw PixelFormatError("process_pixel_transform doesn't have a case for this combination of pixel formats");
        }

#undef __IMP_CASE_CC
#undef __IMP_CASE_CI
#undef __IMP_CASE_IC
#undef __IMP_CASE_II
#undef __IMP_CASE
    }

    constexpr bool operator==(const Rgb &lhs, const Rgb &rhs)
    { return lhs.red == rhs.red && lhs.green == rhs.green && lhs.blue == rhs.blue; }

    constexpr bool operator!=(const Rgb &lhs, const Rgb &rhs)
    { return !(lhs == rhs); }

    constexpr bool operator==(const Rgb &lhs, const Rgba &rhs)
    { return rhs.alpha == 0xff && lhs.red == rhs.red && lhs.green == rhs.green && lhs.blue == rhs.blue; }

    constexpr bool operator!=(const Rgb &lhs, const Rgba &rhs)
    { return !(lhs == rhs); }

    constexpr bool operator==(const Rgba &lhs, const Rgb &rhs)
    { return rhs == lhs; }

    constexpr bool operator!=(const Rgba &lhs, const Rgb &rhs)
    { return !(rhs == lhs); }

    constexpr bool operator==(const Rgba &lhs, const Rgba &rhs)
    { return lhs.red == rhs.red && lhs.green == rhs.green && lhs.blue == rhs.blue; }

    constexpr bool operator!=(const Rgba &lhs, const Rgba &rhs)
    { return !(lhs == rhs); }

    /* Identity convert_pixel */
    template <class T>
    constexpr T convert_pixel(const T &src, typename pixel_traits<T>::tag_type) noexcept
    { return src; }

    /* RGB convert_pixel */
    constexpr Rgba convert_pixel(const Rgb &src, rgba_tag) noexcept
    { return { src.red, src.green, src.blue, 0xff }; }

    /* RGBA convert_pixel */
    constexpr Rgb convert_pixel(const Rgba &src, rgb_tag) noexcept
    { return { src.red, src.green, src.blue }; }

    /* auto copy_pixel */
    void copy_pixel(PixelFormat srcFmt, const Palette *srcPal, const byte *src,
                    PixelFormat dstFmt, const Palette *dstPal, byte *dst);

    static_assert( is_pixel<Index8>, "");
    static_assert( is_pixel<Rgb>, "");
    static_assert( is_pixel<Rgba>, "");
    static_assert(!is_pixel<int>, "");

    static_assert(!is_color_pixel<Index8>, "");
    static_assert( is_color_pixel<Rgb>, "");
    static_assert( is_color_pixel<Rgba>, "");
    static_assert(!is_color_pixel<int>, "");

    static_assert( is_index_pixel<Index8>, "");
    static_assert(!is_index_pixel<Rgb>, "");
    static_assert(!is_index_pixel<Rgba>, "");
    static_assert(!is_index_pixel<int>, "");
  }
}

#ifndef IMP_DONT_POLLUTE_GLOBAL_NAMESPACE
using namespace imp::gfx;
#endif

#endif //__IMP_PIXEL__13206666
