#pragma once

#pragma warning(disable: 4996)  //  _CRT_SECURE_NO_WARNINGS

//==============================================================================================

//  team                        C<>DE_INSIDE.RND

//  file name                   tex_framework.h

//  completion level            2                               ( basic, moderate, complete )

//  author                      codesafer

//  date                        2017.06.28

/// since                       2016.10.17

//------------------------------+---------------------------------------------------------------


#include <cstddef>              //  std::size_t


#define CRE                     //  constructor ( place holder )


using   u8                      = unsigned  char;

using   u16                     = unsigned  short;

using   u32                     = unsigned  int;

using   uu                      = std::size_t;


//------------------------------+---------------------------------------------------------------

//  TEX color space


constexpr   auto                make_color16( u8  l, u8  r, u8  g, u8  b )

{

    return  u8( l << 3 | r << 2 | g << 1 | b );

}


enum    class                   C4 : u8

{   //                      +  R  G  B

    black   = make_color16( 0, 0, 0, 0 ),

    navy    = make_color16( 0, 0, 0, 1 ),

    green   = make_color16( 0, 0, 1, 0 ),

    teal    = make_color16( 0, 0, 1, 1 ),

    maroon  = make_color16( 0, 1, 0, 0 ),

    purple  = make_color16( 0, 1, 0, 1 ),

    olive   = make_color16( 0, 1, 1, 0 ),

    silver  = make_color16( 0, 1, 1, 1 ),

    gray    = make_color16( 1, 0, 0, 0 ),

    blue    = make_color16( 1, 0, 0, 1 ),

    lime    = make_color16( 1, 0, 1, 0 ),

    aqua    = make_color16( 1, 0, 1, 1 ),

    red     = make_color16( 1, 1, 0, 0 ),

    fuchsia = make_color16( 1, 1, 0, 1 ),

    yellow  = make_color16( 1, 1, 1, 0 ),

    white   = make_color16( 1, 1, 1, 1 ),

};


constexpr   auto                operator/( C4 fore, C4 back )

{

    return  u8( static_cast< u8 >( back ) << 4 | static_cast< u8 >( fore ) );

};


constexpr   auto                get_fore( u8 color )

{

    return  static_cast< C4 >( color & 0xF );

}


constexpr   auto                get_back( u8 color )

{

    return  static_cast< C4 >( color >> 4 );

}


constexpr   C4                  c4_to_gray[]

{

    C4::black, C4::gray,  C4::gray,  C4::gray,  C4::gray,  C4::gray,  C4::gray,  C4::silver,

    C4::gray,  C4::silver,C4::silver,C4::silver,C4::silver,C4::silver,C4::silver,C4::white,

};


constexpr   u32                 to_gray_level[]

{

    0, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, 2, 2, 2, 2, 3,

};


constexpr   C4                  gray_level_to_c4[]

{

    C4::black, C4::gray, C4::silver, C4::white,

};


constexpr   C4                  level_to_c4_clamp( u32 level )

{

    return  gray_level_to_c4[ U"001233"[ level + 1 ] ]; // 따옴표 안 숫자들 앞에 각각 back-slash 추가해주세요

}


auto&                           operator++( C4& c )

{

    return  c = level_to_c4_clamp( to_gray_level[ static_cast< u8 >( c ) ] + 1 );

};


auto&                           operator--( C4& c )

{

    return  c = level_to_c4_clamp( to_gray_level[ static_cast< u8 >( c ) ] - 1 );

};

//------------------------------+---------------------------------------------------------------


struct                          TEX

{

    u8                          color;

    char                        letter;


    CRE                         TEX() = default;


    CRE                         TEX( const u8 color, const char c )

    :   color( color ), letter( c ) {}


    CRE                         TEX( const int value )

    :   color( value & 0xFF ), letter( ( value & 0xFF00 ) >> 8 ) {}


    auto                        operator==( const TEX other ) const

    {

        return  color == other.color && letter == other.letter;

    }


    auto                        operator!=( const TEX other ) const

    {

        return  !( *this == other );

    }

};

//------------------------------+---------------------------------------------------------------


#include <array>

#include <algorithm>            //  std::min, std::max


template< int x_size = 80, int y_size = 25 >

class                           PAGE

{

    static  const   int         size            = x_size * y_size;

    using                       LINE            = TEX( & )[ x_size ];

    using                       MATRIX          = TEX( & )[ y_size ][ x_size ];


public:

    std::array< TEX, size >     buffer;

    MATRIX&                     cells           = (MATRIX&)buffer;

    u8                          text_color      = C4::silver / C4::black;

    u8                          number_color    = C4::silver / C4::black;

    u8                          number_size     = (u8)11;

    auto                        xsize() const { return  x_size; }

    auto                        ysize() const { return  y_size; }


    enum    class               TEXT_ALIGN : int

    {

        left,

        right,

    };

    ///-------------------------+---------------------------------------------------------------


    CRE                         PAGE() = default;


    CRE                         PAGE( const PAGE& page )

    {

        buffer = page.buffer;

    }


    void                        fill( const TEX tex )

    {

        buffer.fill( tex );

    }


    void                        put( const int x, const int y, const TEX tex )

    {

        cells[ y ][ x ] = tex;

    }


    void                        put( const int x, const int y, const char c )

    {

        put( x, y, { text_color, c } );

    }


    auto                        center_x( int length )

    {

        return  ( x_size - length ) >> 1;

    }


    auto                        center_x( const char* s )

    {

        return  center_x( strlen( s ) );

    }


    auto                        puts( const int x, const int y, const char* str )

    {

        int i;

        for( i = 0; str[ i ]; ++i )

            put( x + i, y, str[ i ] );

        return  x + i;

    }


    auto                        puts( const int y, const char* str )

    {

        return  puts( center_x( str ), y, str );

    }


    template< class T, uu SIZE >

    auto                        puts( const int x, const int y, const char* str,

                                      const T( &colors )[ SIZE ], const int offset = 0 )

    {

        int i;

        for( i = 0; str[ i ]; ++i )

            put( x + i, y,

                { static_cast< u8 >( colors[ ( i + offset ) % SIZE ] ), str[ i ] } );

        return  x + i;

    }


    template< class T, uu SIZE >

    auto                        puts( const int y, const char* str,

                                      const T( &colors )[ SIZE ], const int offset = 0 )

    {

        return  puts( center_x( str ), y, str, colors, offset );

    }


    template< TEXT_ALIGN align >

    auto                        to_string( const int n ) const

    {

        static  char    numstr[ 14 ];

        int* c = (int*)numstr;

        c[ 2 ] = c[ 1 ] = c[ 0 ] = 0x20202020;


        bool    neg = false;

        u32     u   = n;

        if( n < 0 ) neg = true, u = -n;


        char*   p   = numstr + ( sizeof numstr - 1 );

        char*   end = p - number_size;

        do *--p = u % 10 + '0', u /= 10; while( u );

        if( neg ) *--p = '-';


        if( align == TEXT_ALIGN::left )

            return  p;

        return  end;

    }


    template< TEXT_ALIGN align = TEXT_ALIGN::right >

    auto                        put_number( const int x, const int y,

                                            const char* s, const int n )

    {

        return  puts( puts( x, y, s ), y, to_string< align >( n ) );

    }


    void                        horz_line( const int left, const int right,

                                           const int y, const TEX tex )

    {

        for( int x = left; x < right; ++x )

            put( x, y, tex );

    }


    void                        vert_line( const int x, const int top,

                                           const int bottom, const TEX tex )

    {

        for( int y = top; y < bottom; ++y )

            put( x, y, tex );

    }


    void                        rectangle( const int left, const int top,

                                           const int right, const int bottom, TEX tex )

    {

        const   int y2  = bottom - 1;

        horz_line( left, right, top, tex );

        horz_line( left, right, y2, tex );

        for( int y = top + 1; y < y2; ++y )

            horz_line( left, right, y, tex );

    }


    void                        frame( const int left, const int top,

                                       const int right, const int bottom, TEX tex )

    {

        const   int y2  = bottom - 1;

        horz_line( left, right, top, tex );

        horz_line( left, right, y2, tex );

        vert_line( left, top + 1, y2, tex );

        vert_line( right - 1, top + 1, y2, tex );

    }


    void                        frame_bold( const int left, const int top,

                                            const int right, const int bottom, TEX tex )

    {

        const   int y2  = bottom - 1;

        frame( left, top, right, bottom, tex );

        vert_line( left + 1, top + 1, y2, tex );

        vert_line( right - 2, top + 1, y2, tex );

    }


    template< int w, int h >

    void                        draw( const int left, const int top, const PAGE< w, h >& page )

    {

        const   int l   = std::max( 0, -left );

        const   int t   = std::max( 0, -top  );

        const   int r   = std::min( w, x_size - left );

        const   int b   = std::min( h, y_size - top  );

        for( int y = t; y < b; ++y )

        {

            const   int yt = y + top;

            for( int x = l; x < r; ++x )

                if( page.cells[ y ][ x ].letter )

                    put( x + left, yt, page.cells[ y ][ x ] );

        }

    }


    void                        gray()

    {

        for( auto& tex : buffer )

            tex.color       = c4_to_gray[ static_cast< u8 >( get_fore( tex.color ) ) ] /

                              c4_to_gray[ static_cast< u8 >( get_back( tex.color ) ) ];

    }


    void                        lighten()

    {

        for( auto& tex : buffer )

        {

            auto    fore    = get_fore( tex.color );

            auto    back    = get_back( tex.color );

            tex.color       = ++fore / ++back;

        }

    }


    void                        darken()

    {

        for( auto& tex : buffer )

        {

            auto    fore    = get_fore( tex.color );

            auto    back    = get_back( tex.color );

            tex.color       = --fore / --back;

        }

    }

};

//------------------------------+---------------------------------------------------------------


#include <conio.h>              //  _kbhit

#include <Windows.h>


template< int x_size = 80, int y_size = 25 >

class                           SCREEN

{

    char                        buffer[ 8192 ];

    HANDLE                      handle;


    void                        go( const short x, const short y ) const

    {

        fflush( stdout );

        SetConsoleCursorPosition( handle, { x, y } );

    }


    auto                        color( const u8 c ) const

    {

        fflush( stdout );

        SetConsoleTextAttribute( handle, c );

        return  c;

    }


    void                        resize( short w, short h ) const

    {

        COORD       coord { w, h };

        SetConsoleScreenBufferSize( handle, coord );

        SMALL_RECT  rect = { 0,0, w - 1, h - 1 };

        SetConsoleWindowInfo( handle, TRUE, &rect );

    }


public:

    PAGE< x_size, y_size >      top_page;


    ///-------------------------+---------------------------------------------------------------


    CRE                         SCREEN( bool full_screen = false )

    :   handle( GetStdHandle( STD_OUTPUT_HANDLE ) )

    {

        setvbuf( stdout, (char*)&buffer, _IOFBF, sizeof buffer );

        if( full_screen )

            SetConsoleDisplayMode( handle, CONSOLE_FULLSCREEN_MODE, NULL );

        SetConsoleMode( handle, 0x0008 ); // DISABLE_NEWLINE_AUTO_RETURN

        cursor( false );

    }


    auto                        xsize() const { return  x_size; }


    auto                        ysize() const { return  y_size; }


    void                        cursor( const bool show ) const

    {

        CONSOLE_CURSOR_INFO info = { 1, show };

        SetConsoleCursorInfo( handle, &info );

    }


    void                        flush()

    {

        resize( x_size, y_size );

        u8  old_color = color( top_page.buffer[ 0 ].color );

        for( int y = 0; y < y_size; ++y )

        {

            go( 0, y );

            TEX* scanline = top_page.cells[ y ];

            for( int x = 0; x < x_size; ++x )

            {

                if( old_color != scanline[ x ].color )

                    old_color = color( scanline[ x ].color );

                putchar( scanline[ x ].letter );

            }

        }

        fflush( stdout );

    }


    template< class F >

    void                        fade( const int delay, F operation )

    {

        top_page.gray();

        flush();

        for( int i = 0; i < 4; ++i )

        {

            ::Sleep( delay );

            operation();

            flush();

        }

    }


    void                        fadeout( const int delay = 1000 )

    {

        fade( delay, [ this ]{ top_page.lighten(); } );

    }


    void                        fadein( const int delay = 1000 )

    {

        fade( delay, [ this ]{ top_page.darken(); } );

    }


    template< class F = void(*)() >

    auto                        waitfor_press( F f = []{} )

    {

        while( !_kbhit() ) f();

        return  getch();

    }

};

//==============================================================================================