#include <iostream>
#include <string>
#include <type_traits>

namespace mu
{
    namespace index_list
    {
        template<size_t... Indices>
        struct index_list
        {
            template<size_t N>
            using append = index_list<Indices..., N>;
            
            static constexpr size_t size()
            {
                return sizeof...(Indices);
            }
        };

        template<size_t Size>
        struct make_index_list
        {
            typedef typename make_index_list<Size-1>::type::template append<Size-1> type;
        };

        template<>
        struct make_index_list<0u>
        {
            typedef index_list<> type;
        };

        template<typename... Ts>
        using to_index_list = typename make_index_list<sizeof...(Ts)>::type;
    }
    
    namespace il = index_list;
}

struct empty_result
{
    static const std::string value;
};

const std::string empty_result::value;


template<size_t Width, size_t Height>
struct result_size
{
    static constexpr size_t width = Width;
    static constexpr size_t height = Height;
    static constexpr size_t square = Width * Height;
};

template<char Foreground, char Background>
struct render_rule
{
    static constexpr char background = Background;
    static constexpr char foreground = Foreground;
};

template<size_t Idx, unsigned char Block>
struct get_element
{
    static constexpr size_t value = ((Block & ((Idx < 4) ? 0xF0 : 0x0F)) >> (7 - Idx)) & 1;
};

template<size_t Id, size_t Idx, class Size, class Rule, size_t Element>
struct process_one
{
    static constexpr bool in_render_zone = Idx < Size::square;
    static constexpr bool new_line = Idx > 0 && (Idx % Size::width == 0);
    static const std::string value;
};

template<size_t Id, size_t Idx, class Size, class Rule, size_t Element>
const std::string process_one<Id, Idx, Size, Rule, Element>::value = (in_render_zone) ? (((new_line) ? (std::string() + '\n') : std::string()) +
    ((Element != 0) ? Rule::foreground : Rule::background)) : std::string();

    
/*
template<size_t Id, size_t Idx, class Size, class Rule, unsigned char Block>
struct process_block
{
    static const std::string value;
};

template<size_t Id, size_t Idx, class Size, class Rule, unsigned char Block>
const std::string process_block<Id, Idx, Size, Rule, Block>::value =
    process_one<Id, Idx, Size, Rule, get_element<0, Block>::value>::value +
    process_one<Id, Idx + 1, Size, Rule, get_element<1, Block>::value>::value +
    process_one<Id, Idx + 2, Size, Rule, get_element<2, Block>::value>::value +
    process_one<Id, Idx + 3, Size, Rule, get_element<3, Block>::value>::value +
    process_one<Id, Idx + 4, Size, Rule, get_element<4, Block>::value>::value +
    process_one<Id, Idx + 5, Size, Rule, get_element<5, Block>::value>::value +
    process_one<Id, Idx + 6, Size, Rule, get_element<6, Block>::value>::value +
    process_one<Id, Idx + 7, Size, Rule, get_element<7, Block>::value>::value;
*/

template<template <size_t> class T, size_t... Indices> struct summa;
    
template<template <size_t> class T, size_t Index, size_t... Indices>
struct summa<T, Index, Indices...>
{
    static const decltype(T<Index>::value) value;
};

template<template <size_t> class T, size_t Index, size_t... Indices>
const decltype(T<Index>::value) summa<T, Index, Indices...>::value = T<Index>::value + summa<T, Indices...>::value;

template<template <size_t> class T, size_t Index>
struct summa<T, Index>
{
    static const decltype(T<Index>::value) value;
};

template<template <size_t> class T, size_t Index>
const decltype(T<Index>::value) summa<T, Index>::value = T<Index>::value;

/*

template<Signature>
struct summa
{
    static switch (template Signature)
    {
    case template <size_t> class T, size_t Index, size_t... Indices:
        static constexpr auto value = T<Index>::value + summa<T, Indices...>::value;
        break;
    case template <size_t> class T, size_t Index:
        static constexpr auto value = T<Index>::value;
        break;
    default:;
    }
};

template<template <size_t> class T, size_t Index, size_t... Indices>
struct summa<T, Index, Indices...>
{
    static if (sizeof...(Indices) > 0)
        static constexpr auto value = T<Index>::value + summa<T, Indices...>::value;
    else
        static constexpr auto value = T<Index>::value;
};

*/
template<size_t Id, size_t Idx, class Size, class Rule, unsigned char Block>
struct process_block_
{
    template<size_t N>
    using process_one_ = process_one<Id, Idx + N, Size, Rule, get_element<N, Block>::value>;

    template<size_t... Indices>
    static constexpr auto run(const mu::il::index_list<Indices...>&) -> decltype(summa<process_one_, Indices...>::value)
    {
        return summa<process_one_, Indices...>::value;
    }
    
    static const std::string value;
};    

template<size_t Id, size_t Idx, class Size, class Rule, unsigned char Block>
const std::string process_block_<Id, Idx, Size, Rule, Block>::value =  process_block_<Id, Idx, Size, Rule, Block>::template run(mu::il::make_index_list<8>::type());
    
template<size_t Id, size_t Idx, class Size, class Rule, unsigned char... Data> struct render;

template<size_t Id, size_t Idx, class Size, class Rule, unsigned char First, unsigned char... Data>
struct render<Id, Idx, Size, Rule, First, Data...>
{
    static const std::string value;
};

/*
template<size_t Id, size_t Idx, class Size, class Rule, unsigned char First, unsigned char... Data>
const std::string render<Id, Idx, Size, Rule, First, Data...>::value = process_block<Id, Idx, Size, Rule, First>::value +
    render<Id, Idx + 8, Size, Rule, Data...>::value;
*/

template<size_t Id, size_t Idx, class Size, class Rule, unsigned char First, unsigned char... Data>
const std::string render<Id, Idx, Size, Rule, First, Data...>::value = process_block_<Id, Idx, Size, Rule, First>::value +
    render<Id, Idx + 8, Size, Rule, Data...>::value;

template<size_t Id,  size_t Idx, class Size, class Rule, unsigned char Last>
struct render<Id, Idx, Size, Rule, Last>
{
    static const std::string value;
};

/*
template<size_t Id, size_t Idx, class Size, class Rule, unsigned char Last>
const std::string render<Id, Idx, Size, Rule, Last>::value = process_block<Id, Idx, Size, Rule, Last>::value;
*/

template<size_t Id, size_t Idx, class Size, class Rule, unsigned char Last>
const std::string render<Id, Idx, Size, Rule, Last>::value = process_block_<Id, Idx, Size, Rule, Last>::value;


template<size_t Id, size_t Width, class Rule, unsigned char... Data>
struct silly_img
{
    static constexpr size_t width = Width;
    static constexpr size_t height = (Width > 0) ? (sizeof...(Data) * 8 / Width) : 0;
    static constexpr bool is_valid = width > 0 && height > 0;
    static const std::string data;

    static void show_me()
    {
        std::cout << "(" << Id << ") Size: " << width << " x " << height << "\n\n";
        std::cout << data;
    }
};

template<size_t Id, size_t Width, class Rule, unsigned char... Data>
const std::string silly_img<Id, Width, Rule, Data...>::data = std::string() +
    std::conditional< is_valid, render<Id, 0, result_size<width, height>, Rule, Data...>, empty_result>::type::value;

int main()
{
    silly_img<0, 11, render_rule<'$', ' '>, 0x31, 0x8F, 0x7B, 0xFF, 0xFF, 0xF3, 0xF8, 0x1C, 0x01, 0x00>::show_me();
    std::cout << "\n\n";
    silly_img<1, 15, render_rule<'#', '.'>, 0x31, 0x84, 0xF7, 0x9F, 0xFF, 0x97, 0xFF, 0x03, 0xF8, 0x81, 0xC3, 0x81, 0x02, 0x00>::show_me();
    std::cout << "\n\n";
    silly_img<2, 15, render_rule<'*', ' '>, 0x3F, 0xE4, 0xFF, 0x9F, 0xC0, 0x17, 0x80, 0x0F, 0x00, 0x8F, 0xF3, 0x8F, 0xF2, 0x00>::show_me();
}
