#include "stdafx.h"
#include "uilayer.h"
#include "Globals.h"
#include "usefull.h"
#include "renderer.h"
#include "Logs.h"

#include <GL/glut.h>

ui_layer UILayer;

#ifdef _WIN32
extern "C"
{
	GLFWAPI HWND glfwGetWin32Window( GLFWwindow* window );
}
#endif

ui_layer::~ui_layer() {
/*
// this should be invoked manually, or we risk trying to delete the lists after the context is gone
    if( m_fontbase != -1 )
        ::glDeleteLists( m_fontbase, 96 );
*/
}

bool
ui_layer::init( GLFWwindow *Window ) {
    m_window = Window;

#ifndef _WIN32
	Global::bGlutFont = true;
#endif

	if (Global::bGlutFont)
	{
		int zi = 0;
		char zc = 0;
		char *zcp = &zc;
		glutInit(&zi, &zcp);
		WriteLog("Used font from GLUT.");
		Global::DLFont = true;
		return true;
	}

#ifdef _WIN32
    HFONT font; // Windows Font ID
    m_fontbase = ::glGenLists(96); // storage for 96 characters
    HDC hDC = ::GetDC( glfwGetWin32Window( m_window ) );
    font = ::CreateFont( -MulDiv( 10, ::GetDeviceCaps( hDC, LOGPIXELSY ), 72 ), // height of font
                        0, // width of font
                        0, // angle of escapement
                        0, // orientation angle
                        FW_MEDIUM, // font weight
                        FALSE, // italic
                        FALSE, // underline
                        FALSE, // strikeout
                        DEFAULT_CHARSET, // character set identifier
                        OUT_DEFAULT_PRECIS, // output precision
                        CLIP_DEFAULT_PRECIS, // clipping precision
                        CLEARTYPE_QUALITY, // output quality
                        DEFAULT_PITCH | FF_DONTCARE, // family and pitch
                        "Lucida Console"); // font name
    ::SelectObject(hDC, font); // selects the font we want
    if( TRUE == ::wglUseFontBitmaps( hDC, 32, 96, m_fontbase ) ) {
        // builds 96 characters starting at character 32
        WriteLog( "Display Lists font used" ); //+AnsiString(glGetError())
        WriteLog( "Font init OK" ); //+AnsiString(glGetError())
        Global::DLFont = true;
        return true;
    }
    else {
        ErrorLog( "Font init failed" );
        Global::DLFont = false;
        return true;
    }
#endif
}

void
ui_layer::render() {
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho( 0, std::max( 1, Global::iWindowWidth ), std::max( 1, Global::iWindowHeight ), 0, -1, 1 );

	glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glPushAttrib( GL_ENABLE_BIT | GL_CURRENT_BIT );
	glDisable( GL_LIGHTING );
	glDisable( GL_DEPTH_TEST );
	glDisable( GL_ALPHA_TEST );
	glEnable( GL_BLEND );

    // render code here
    render_background();
    render_texture();
    render_progress();
    render_panels();
    render_tooltip();

	glPopAttrib();
}

void
ui_layer::set_progress( float const Progress, float const Subtaskprogress ) {

    m_progress = Progress * 0.01f;
    m_subtaskprogress = Subtaskprogress * 0.01f;
}

void
ui_layer::set_background( std::string const &Filename ) {

    if( false == Filename.empty() ) {
        m_background = GfxRenderer.Fetch_Texture( Filename );
    }
    else {
        m_background = null_handle;
    }
    if( m_background != null_handle ) {
        auto const &texture = GfxRenderer.Texture( m_background );
        m_progressbottom = ( texture.width() != texture.height() );
    }
}

void
ui_layer::render_progress() {

	if( (m_progress == 0.0f) && (m_subtaskprogress == 0.0f) ) return;

	glPushAttrib( GL_ENABLE_BIT );
    glDisable( GL_TEXTURE_2D );

    glm::vec2 origin, size;
    if( m_progressbottom == true ) {
        origin = glm::vec2{ 0.0f, 768.0f - 20.0f };
        size   = glm::vec2{ 1024.0f, 20.0f };
    }
    else {
        origin = glm::vec2{ 75.0f, 640.0f };
        size   = glm::vec2{ 320.0f, 16.0f };
    }

    quad( float4( origin.x, origin.y, origin.x + size.x, origin.y + size.y ), float4(0.0f, 0.0f, 0.0f, 0.25f) );
    // secondary bar
    if( m_subtaskprogress ) {
        quad(
            float4( origin.x, origin.y, origin.x + size.x * m_subtaskprogress, origin.y + size.y),
            float4( 8.0f/255.0f, 160.0f/255.0f, 8.0f/255.0f, 0.35f ) );
    }
    // primary bar
	if( m_progress ) {
        quad(
            float4( origin.x, origin.y, origin.x + size.x * m_progress, origin.y + size.y ),
            float4( 8.0f / 255.0f, 160.0f / 255.0f, 8.0f / 255.0f, 1.0f ) );
    }

    if( false == m_progresstext.empty() ) {
        float const screenratio = static_cast<float>( Global::iWindowWidth ) / Global::iWindowHeight;
        float const width =
            ( screenratio >= (4.0f/3.0f) ?
                ( 4.0f / 3.0f ) * Global::iWindowHeight :
                Global::iWindowWidth );
        float const heightratio =
            ( screenratio >= ( 4.0f / 3.0f ) ?
                Global::iWindowHeight / 768.f :
                Global::iWindowHeight / 768.f * screenratio / ( 4.0f / 3.0f ) );
        float const height = 768.0f * heightratio;

        ::glColor4f( 216.0f / 255.0f, 216.0f / 255.0f, 216.0f / 255.0f, 1.0f );
        auto const charsize = 9.0f;
        auto const textwidth = m_progresstext.size() * charsize;
        auto const textheight = 12.0f;
        ::glRasterPos2f(
            ( 0.5f * ( Global::iWindowWidth  - width )  + origin.x * heightratio ) + ( ( size.x * heightratio - textwidth ) * 0.5f * heightratio ),
            ( 0.5f * ( Global::iWindowHeight - height ) + origin.y * heightratio ) + ( charsize ) + ( ( size.y * heightratio - textheight ) * 0.5f * heightratio ) );
        print( m_progresstext );
    }

    glPopAttrib();
}

void
ui_layer::render_panels() {

    if( m_panels.empty() ) { return; }

    glPushAttrib( GL_ENABLE_BIT );
    glDisable( GL_TEXTURE_2D );

    float const width = std::min( 4.f / 3.f, static_cast<float>(Global::iWindowWidth) / std::max( 1, Global::iWindowHeight ) ) * Global::iWindowHeight;
    float const height = Global::iWindowHeight / 768.f;

    for( auto const &panel : m_panels ) {

        int lineidx = 0;
        for( auto const &line : panel->text_lines ) {

            ::glColor4fv( &line.color.x );
            ::glRasterPos2f(
                0.5f * ( Global::iWindowWidth - width ) + panel->origin_x * height,
                panel->origin_y * height + 20.f * lineidx );
            print( line.data );
            ++lineidx;
        }
    }

    glPopAttrib();
}

void
ui_layer::render_tooltip() {

    if( m_tooltip.empty() ) { return; }

    glm::dvec2 mousepos;
    glfwGetCursorPos( m_window, &mousepos.x, &mousepos.y );

    glPushAttrib( GL_ENABLE_BIT );
    glDisable( GL_TEXTURE_2D );
    ::glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
    ::glRasterPos2f( mousepos.x + 20.0f, mousepos.y + 25.0f );
    print( m_tooltip );

    glPopAttrib();
}

void
ui_layer::render_background() {

	if( m_background == 0 ) return;
    // NOTE: we limit/expect the background to come with 4:3 ratio.
    // TODO, TBD: if we expose texture width or ratio from texture object, this limitation could be lifted
    GfxRenderer.Bind_Texture( m_background );
    auto const height { 768.0f };
    auto const &texture = GfxRenderer.Texture( m_background );
    float const width = (
        texture.width() == texture.height() ?
            1024.0f : // legacy mode, square texture displayed as 4:3 image
            texture.width() / ( texture.height() / 768.0f ) );
    quad(
        float4(
            ( 1024.0f * 0.5f ) - ( width  * 0.5f ),
            (  768.0f * 0.5f ) - ( height * 0.5f ),
            ( 1024.0f * 0.5f ) - ( width  * 0.5f ) + width,
            (  768.0f * 0.5f ) - ( height * 0.5f ) + height ),
        float4( 1.0f, 1.0f, 1.0f, 1.0f ) );
}

void
ui_layer::render_texture() {

    if( m_texture != 0 ) {
        ::glColor4f( 1.f, 1.f, 1.f, 1.f );
        ::glDisable( GL_BLEND );

        GfxRenderer.Bind_Texture( null_handle );
        ::glBindTexture( GL_TEXTURE_2D, m_texture );

        auto const size = 512.f;
        auto const offset = 64.f;

        glBegin( GL_TRIANGLE_STRIP );

        glMultiTexCoord2f( m_textureunit, 0.f, 1.f ); glVertex2f( offset, Global::iWindowHeight - offset - size );
        glMultiTexCoord2f( m_textureunit, 0.f, 0.f ); glVertex2f( offset, Global::iWindowHeight - offset );
        glMultiTexCoord2f( m_textureunit, 1.f, 1.f ); glVertex2f( offset + size, Global::iWindowHeight - offset - size );
        glMultiTexCoord2f( m_textureunit, 1.f, 0.f ); glVertex2f( offset + size, Global::iWindowHeight - offset );

        glEnd();

        ::glBindTexture( GL_TEXTURE_2D, 0 );

        ::glEnable( GL_BLEND );
    }
}

void
ui_layer::print( std::string const &Text )
{
    if( (false == Global::DLFont)
     || (true == Text.empty()) )
        return;
    
	if (Global::bGlutFont)
	{
		for (size_t i = 0; i < Text.size(); i++)
			glutBitmapCharacter(GLUT_BITMAP_8_BY_13, Text[i]);
	}
	else
	{
		::glPushAttrib(GL_LIST_BIT);

		::glListBase(m_fontbase - 32);
		::glCallLists((GLsizei)Text.size(), GL_UNSIGNED_BYTE, Text.c_str());

		::glPopAttrib();
	}
}

void
ui_layer::quad( float4 const &Coordinates, float4 const &Color ) {

    float const screenratio = static_cast<float>( Global::iWindowWidth ) / Global::iWindowHeight;
    float const width =
        ( screenratio >= ( 4.f / 3.f ) ?
            ( 4.f / 3.f ) * Global::iWindowHeight :
            Global::iWindowWidth );
    float const heightratio =
        ( screenratio >= ( 4.f / 3.f ) ?
            Global::iWindowHeight / 768.f :
            Global::iWindowHeight / 768.f * screenratio / ( 4.f / 3.f ) );
    float const height = 768.f * heightratio;
/*
    float const heightratio = Global::iWindowHeight / 768.0f;
    float const height = 768.0f * heightratio;
    float const width = Global::iWindowWidth * heightratio;
*/
    glColor4fv(&Color.x);

    glBegin( GL_TRIANGLE_STRIP );

    glMultiTexCoord2f( m_textureunit, 0.f, 1.f ); glVertex2f( 0.5f * ( Global::iWindowWidth - width ) + Coordinates.x * heightratio, 0.5f * ( Global::iWindowHeight - height ) + Coordinates.y * heightratio );
    glMultiTexCoord2f( m_textureunit, 0.f, 0.f ); glVertex2f( 0.5f * ( Global::iWindowWidth - width ) + Coordinates.x * heightratio, 0.5f * ( Global::iWindowHeight - height ) + Coordinates.w * heightratio );
    glMultiTexCoord2f( m_textureunit, 1.f, 1.f ); glVertex2f( 0.5f * ( Global::iWindowWidth - width ) + Coordinates.z * heightratio, 0.5f * ( Global::iWindowHeight - height ) + Coordinates.y * heightratio );
    glMultiTexCoord2f( m_textureunit, 1.f, 0.f ); glVertex2f( 0.5f * ( Global::iWindowWidth - width ) + Coordinates.z * heightratio, 0.5f * ( Global::iWindowHeight - height ) + Coordinates.w * heightratio );

    glEnd();
}
