//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
// copyright            : (C) 2008 by Eran Ifrah
// file name            : drawingutils.cpp
//
// -------------------------------------------------------------------------
// A
//              _____           _      _     _ _
//             /  __ \         | |    | |   (_) |
//             | /  \/ ___   __| | ___| |    _| |_ ___
//             | |    / _ \ / _  |/ _ \ |   | | __/ _ )
//             | \__/\ (_) | (_| |  __/ |___| | ||  __/
//              \____/\___/ \__,_|\___\_____/_|\__\___|
//
//                                                  F i l e
//
//    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.
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
#include <wx/app.h>
#include <wx/panel.h>
#include "wx/settings.h"
#include "drawingutils.h"
#include "wx/dc.h"

#ifdef __WXGTK__
#include <gtk/gtk.h>
#endif
//////////////////////////////////////////////////
// Colour methods to convert HSL <-> RGB
//////////////////////////////////////////////////
static float cl_min(float x, float y, float z)
{
	float m = x < y ? x : y;
	m = m < z ? m : z;
	return m;
}

static float cl_max(float x, float y, float z)
{
	float m = x > y ? x : y;
	m = m > z ? m : z;
	return m;
}

static void RGB_2_HSL(float r, float g, float b, float *h, float *s, float *l)
{
	float var_R = ( r / 255.0 );                     //RGB from 0 to 255
	float var_G = ( g / 255.0 );
	float var_B = ( b / 255.0 );

	float var_Min = cl_min( var_R, var_G, var_B );    //Min. value of RGB
	float var_Max = cl_max( var_R, var_G, var_B );    //Max. value of RGB
	float del_Max = var_Max - var_Min;             //Delta RGB value

	*l = ( var_Max + var_Min ) / 2.0;

	if ( del_Max == 0 ) {                   //This is a gray, no chroma...
		*h = 0;                                //HSL results from 0 to 1
		*s = 0;
	} else {                                 //Chromatic data...
		if ( *l < 0.5 ) *s = del_Max / ( var_Max + var_Min );
		else *s = del_Max / ( 2.0 - var_Max - var_Min );

		float del_R = ( ( ( var_Max - var_R ) / 6.0 ) + ( del_Max / 2.0 ) ) / del_Max;
		float del_G = ( ( ( var_Max - var_G ) / 6.0 ) + ( del_Max / 2.0 ) ) / del_Max;
		float del_B = ( ( ( var_Max - var_B ) / 6.0 ) + ( del_Max / 2.0 ) ) / del_Max;

		if      ( var_R == var_Max ) *h = del_B - del_G;
		else if ( var_G == var_Max ) *h = ( 1.0 / 3.0 ) + del_R - del_B;
		else if ( var_B == var_Max ) *h = ( 2.0 / 3.0 ) + del_G - del_R;

		if ( *h < 0 ) *h += 1;
		if ( *h > 1 ) *h -= 1;
	}
}

static float Hue_2_RGB( float v1, float v2, float vH )             //Function Hue_2_RGB
{
	if ( vH < 0 ) vH += 1;
	if ( vH > 1 ) vH -= 1;
	if ( ( 6.0 * vH ) < 1 ) return ( v1 + ( v2 - v1 ) * 6.0 * vH );
	if ( ( 2.0 * vH ) < 1 ) return ( v2 );
	if ( ( 3.0 * vH ) < 2 ) return ( v1 + ( v2 - v1 ) * ( ( 2.0 / 3.0 ) - vH ) * 6.0 );
	return ( v1 );
}

static void HSL_2_RGB(float h, float s, float l, float *r, float *g, float *b)
{
	if ( s == 0 ) {                     //HSL from 0 to 1
		*r = l * 255.0;                      //RGB results from 0 to 255
		*g = l * 255.0;
		*b = l * 255.0;
	} else {
		float var_2;
		if ( l < 0.5 ) var_2 = l * ( 1.0 + s );
		else           var_2 = ( l + s ) - ( s * l );

		float var_1 = 2.0 * l - var_2;

		*r = 255.0 * Hue_2_RGB( var_1, var_2, h + ( 1.0 / 3.0 ) );
		*g = 255.0 * Hue_2_RGB( var_1, var_2, h );
		*b = 255.0 * Hue_2_RGB( var_1, var_2, h - ( 1.0 / 3.0 ) );
	}
}

//-------------------------------------------------------------------------------------------------
// helper functions
//-------------------------------------------------------------------------------------------------

wxColor DrawingUtils::LightColour(const wxColour& color, float percent)
{
	if(percent == 0){
		return color;
	}

	float h, s, l, r, g, b;
	RGB_2_HSL(color.Red(), color.Green(), color.Blue(), &h, &s, &l);

	// reduce the Lum value
	l += (float)((percent * 5.0)/100.0);
	if (l > 1.0) l = 1.0;

	HSL_2_RGB(h, s, l, &r, &g, &b);
	return wxColour((unsigned char)r, (unsigned char)g, (unsigned char)b);
}

void DrawingUtils::TruncateText(wxDC& dc, const wxString& text, const int &maxWidth, wxString& fixedText)
{
	int textH, textW;
	int rectSize = maxWidth + 4; //error size
	int textLen = (int)text.Length();
	wxString tempText = text;

	fixedText = wxT("");
	dc.GetTextExtent(text, &textW, &textH);
	if (rectSize >= textW) {
		fixedText = text;
		return;
	}

	// The text does not fit in the designated area,
	// so we need to truncate it a bit
	wxString suffix = wxT("..");
	int w, h;
	dc.GetTextExtent(suffix, &w, &h);
	rectSize -= w;

	for (int i=textLen; i>=0; i--) {
		dc.GetTextExtent(tempText, &textW, &textH);
		if (rectSize > textW) {
			fixedText = tempText;
			fixedText += wxT("..");
			return;
		}
		tempText = tempText.RemoveLast();
	}
}

void DrawingUtils::PaintStraightGradientBox(wxDC& dc,
        const wxRect& rect,
        const wxColour& startColor,
        const wxColour& endColor,
        bool  vertical)
{
	int rd, gd, bd, high = 0;
	rd = endColor.Red() - startColor.Red();
	gd = endColor.Green() - startColor.Green();
	bd = endColor.Blue() - startColor.Blue();

	/// Save the current pen and brush
	wxPen savedPen = dc.GetPen();
	wxBrush savedBrush = dc.GetBrush();

	if ( vertical )
		high = rect.GetHeight()-1;
	else
		high = rect.GetWidth()-1;

	if ( high < 1 )
		return;

	for (int i = 0; i <= high; ++i) {
		int r = startColor.Red() +  ((i*rd*100)/high)/100;
		int g = startColor.Green() + ((i*gd*100)/high)/100;
		int b = startColor.Blue() + ((i*bd*100)/high)/100;

		wxPen p(wxColor(r, g, b));
		dc.SetPen(p);

		if ( vertical )
			dc.DrawLine(rect.x, rect.y+i, rect.x+rect.width, rect.y+i);
		else
			dc.DrawLine(rect.x+i, rect.y, rect.x+i, rect.y+rect.height);
	}

	/// Restore the pen and brush
	dc.SetPen( savedPen );
	dc.SetBrush( savedBrush );
}

void DrawingUtils::DrawVerticalButton(wxDC& dc,
                                      const wxRect& rect,
                                      const bool &focus,
                                      const bool &leftTabs,
                                      bool vertical,
                                      bool hover  )
{
	wxColour lightGray = GetGradient();

	// Define the rounded rectangle base on the given rect
	// we need an array of 9 points for it
	wxColour topStartColor(wxT("WHITE"));
	wxColour topEndColor(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));

	// Define the middle points
	if ( focus ) {
		PaintStraightGradientBox(dc, rect, topStartColor, topEndColor, vertical);
	} else {
		wxRect r1;
		wxRect r2;

		topStartColor = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
		topEndColor = lightGray;

		if (leftTabs) {
			r1 = wxRect(rect.x, rect.y, rect.width, rect.height/4);
			r2 = wxRect(rect.x, rect.y+rect.height/4, rect.width, (rect.height*3)/4);
			PaintStraightGradientBox(dc, r1, topEndColor, topStartColor, vertical);
			PaintStraightGradientBox(dc, r2, topStartColor, topStartColor, vertical);

		} else {
			r1 = wxRect(rect.x, rect.y, rect.width, (rect.height*3)/4);
			r2 = wxRect(rect.x, rect.y+(rect.height*3)/4, rect.width, rect.height/4);
			PaintStraightGradientBox(dc, r1, topStartColor, topStartColor, vertical);
			PaintStraightGradientBox(dc, r2, topStartColor, topEndColor, vertical);
		}

	}

	dc.SetBrush( *wxTRANSPARENT_BRUSH );
}

void DrawingUtils::DrawHorizontalButton(wxDC& dc, const wxRect& rect, const bool &focus, const bool &upperTabs, bool vertical, bool hover)
{
	wxColour lightGray = GetGradient();
	wxColour topStartColor(wxT("WHITE"));
	wxColour topEndColor(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));

	// Define the middle points
	if ( focus ) {
		if (upperTabs) {
			PaintStraightGradientBox(dc, rect, topStartColor, topEndColor, vertical);
		} else {
			PaintStraightGradientBox(dc, rect, topEndColor, topStartColor, vertical);
		}
	} else {

		topStartColor = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
		topEndColor = lightGray;

		wxRect r1;
		wxRect r2;

		if (upperTabs) {
			r1 = wxRect(rect.x, rect.y, rect.width, rect.height/4);
			r2 = wxRect(rect.x, rect.y+rect.height/4, rect.width, (rect.height*3)/4);
			PaintStraightGradientBox(dc, r1, topEndColor, topStartColor, vertical);
			PaintStraightGradientBox(dc, r2, topStartColor, topStartColor, vertical);

		} else {
			r1 = wxRect(rect.x, rect.y, rect.width, (rect.height*3)/4);
			r2 = wxRect(rect.x, rect.y+(rect.height*3)/4, rect.width, rect.height/4);
			PaintStraightGradientBox(dc, r1, topStartColor, topStartColor, vertical);
			PaintStraightGradientBox(dc, r2, topStartColor, topEndColor, vertical);
		}
	}

	dc.SetBrush( *wxTRANSPARENT_BRUSH );
}

bool DrawingUtils::IsDark(const wxColour& color)
{
	int evg = (color.Red() + color.Green() + color.Blue())/3;
	if (evg < 127)
		return true;

	return false;
}

float DrawingUtils::GetDdkShadowLightFactor()
{
#if defined (__WXGTK__)
	return 12.0;
#else
	return 8.0;
#endif
}

float DrawingUtils::GetDdkShadowLightFactor2()
{
#if defined (__WXGTK__)
	return 9.0;
#else
	return 3.0;
#endif
}


wxColour DrawingUtils::GetGradient()
{
//#if defined (__WXGTK__)
	return  LightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 4.0);
//	return  wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
//#else
//	return LightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW), GetDdkShadowLightFactor());
//#endif
}


wxColor DrawingUtils::DarkColour(const wxColour& color, float percent)
{
	if(percent == 0){
		return color;
	}

	float h, s, l, r, g, b;
	RGB_2_HSL(color.Red(), color.Green(), color.Blue(), &h, &s, &l);

	// reduce the Lum value
	l -= (float)((percent * 5.0)/100.0);
	if (l < 0) l = 0.0;

	HSL_2_RGB(h, s, l, &r, &g, &b);
	return wxColour((unsigned char)r, (unsigned char)g, (unsigned char)b);
}

wxColor DrawingUtils::GetPanelBgColour()
{
#ifdef __WXGTK__
	static bool     intitialized(false);
	static wxColour bgColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));

	if( !intitialized ) {
		// try to get the background colour from a menu
		GtkWidget *menu = gtk_window_new(GTK_WINDOW_TOPLEVEL);
		GtkStyle   *def = gtk_rc_get_style( menu );
		if(!def)
			def = gtk_widget_get_default_style();

		if(def) {
			GdkColor col = def->bg[GTK_STATE_NORMAL];
			bgColour = wxColour(col);
		}
		gtk_widget_destroy( menu );
		intitialized = true;
	}
	return bgColour;
#else
	return wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
#endif
}

wxColor DrawingUtils::GetTextCtrlTextColour()
{
#ifdef __WXGTK__
	static bool     intitialized(false);
	static wxColour textColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));

	if( !intitialized ) {
		// try to get the background colour from a menu
		GtkWidget *textCtrl = gtk_text_view_new();
		GtkStyle   *def = gtk_rc_get_style( textCtrl );
		if(!def)
			def = gtk_widget_get_default_style();

		if(def) {
			GdkColor col = def->text[GTK_STATE_NORMAL];
			textColour = wxColour(col);
		}
		gtk_widget_destroy( textCtrl );
		intitialized = true;
	}
	return textColour;
#else
	return wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
#endif
}

wxColor DrawingUtils::GetMenuTextColour()
{
#ifdef __WXGTK__
	static bool     intitialized(false);
	static wxColour textColour(wxSystemSettings::GetColour(wxSYS_COLOUR_MENUTEXT));

	if( !intitialized ) {
		// try to get the background colour from a menu
		GtkWidget *menuBar = gtk_menu_new();
		GtkStyle   *def = gtk_rc_get_style( menuBar );
		if(!def)
			def = gtk_widget_get_default_style();

		if(def) {
			GdkColor col = def->text[GTK_STATE_NORMAL];
			textColour = wxColour(col);
		}
		gtk_widget_destroy( menuBar );
		intitialized = true;
	}
	return textColour;
#else
	return wxSystemSettings::GetColour(wxSYS_COLOUR_MENUTEXT);
#endif
}

wxColor DrawingUtils::GetMenuBarBgColour()
{
#ifdef __WXGTK__
	static bool     intitialized(false);
	static wxColour textColour(wxSystemSettings::GetColour(wxSYS_COLOUR_MENUBAR));

	if( !intitialized ) {
		// try to get the background colour from a menu
		GtkWidget *menuBar = gtk_menu_bar_new();
		GtkStyle   *def = gtk_rc_get_style( menuBar );
		if(!def)
			def = gtk_widget_get_default_style();

		if(def) {
			GdkColor col = def->bg[GTK_STATE_NORMAL];
			textColour = wxColour(col);
		}
		gtk_widget_destroy( menuBar );
		intitialized = true;
	}
	return textColour;
#else
	return wxSystemSettings::GetColour(wxSYS_COLOUR_MENUBAR);
#endif
}
