/* AbiSource Application Framework
 * Copyright (C) 1998 AbiSource, Inc.
 * Copyright (C) 2009 Hubert Figuiere
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "ut_string.h"
#include "ut_debugmsg.h"
#include "xap_Dlg_FontChooser.h"
#include "ut_string_class.h"
#include "gr_Graphics.h"
#include "gr_Painter.h"

/*****************************************************************/

// your typographer's standard nonsense latin font phrase
#define PREVIEW_ENTRY_DEFAULT_STRING	"Lorem ipsum dolor sit amet, consectetaur adipisicing..."

XAP_Dialog_FontChooser::XAP_Dialog_FontChooser(XAP_DialogFactory * pDlgFactory, XAP_Dialog_Id id)
	: XAP_Dialog_NonPersistent(pDlgFactory,id, "interface/dialogfont")
{
	m_answer				= a_CANCEL;
	m_pGraphics				= NULL;
	m_pFontPreview          = NULL;
	m_bUnderline			= false;
	m_bOverline				= false;
	m_bStrikeout			= false;
	m_bTopline		    	= false;
	m_bBottomline			= false;
	m_bHidden               = false;
	m_bSuperScript			= false;
	m_bSubScript			= false;
	m_bChangedFontFamily	= false;
	m_bChangedTextTransform	= false;
	m_bChangedFontSize		= false;
	m_bChangedFontWeight	= false;
	m_bChangedFontStyle		= false;
	m_bChangedColor			= false;
	m_bChangedBGColor       = false;
	m_bChangedUnderline		= false;
	m_bChangedOverline		= false;
	m_bChangedStrikeOut		= false;
	m_bChangedTopline		= false;
	m_bChangedBottomline   	= false;
	m_bChangedHidden        = false;
	m_bChangedSuperScript	= false;
	m_bChangedSubScript		= false;

	UT_UCS4_cloneString_char (&m_drawString, PREVIEW_ENTRY_DEFAULT_STRING);
}

XAP_Dialog_FontChooser::~XAP_Dialog_FontChooser(void)
{
	FREEP(m_drawString);
	DELETEP(m_pFontPreview);
}

void XAP_Dialog_FontChooser::setDrawString(const UT_UCSChar * str)
{
	FREEP(m_drawString);
	UT_sint32 len = UT_UCS4_strlen(str);
	if(len <= 0)
	{
		UT_UCS4_cloneString_char (&m_drawString, PREVIEW_ENTRY_DEFAULT_STRING);
	}
	else
	{
	    UT_UCS4_cloneString(&m_drawString, str);
	}
}

void XAP_Dialog_FontChooser::setGraphicsContext(GR_Graphics * pGraphics)
{
	m_pGraphics = pGraphics;
}

void XAP_Dialog_FontChooser::_createFontPreviewFromGC(GR_Graphics * gc,
													  UT_uint32 width,
													  UT_uint32 height)
{
	UT_ASSERT(gc);

	m_pFontPreview = new XAP_Preview_FontPreview(gc,m_sColorBackground.c_str());
	UT_return_if_fail(m_pFontPreview);

	m_pFontPreview->setWindowSize(width, height);
	m_pFontPreview->setVecProperties( & m_mapProps);
}

void XAP_Dialog_FontChooser::addOrReplaceVecProp(const std::string & sProp, const std::string & sVal)
{
	m_mapProps[sProp] = sVal;
}

/*!
 * This method updates the drawing area in the dialog.
 */
void XAP_Dialog_FontChooser::event_previewExposed(const UT_UCSChar * pszChars)
{
	UT_UCSChar * pszNew = NULL;
	if(!pszChars || UT_UCS4_strlen(pszChars) <= 0)
	{
		//FREEP(pszChars); // we should not g_free it here
		UT_UCS4_cloneString_char (&pszNew, PREVIEW_ENTRY_DEFAULT_STRING);
		if (!pszNew)
			return;

		m_pFontPreview->setDrawString(pszNew);
	}
	else
	{
		m_pFontPreview->setDrawString(pszChars);
	}
	m_pFontPreview->draw();
	
	FREEP(pszNew);
}


/*!
 * This method clears the drawing area in the dialog.
 */
void XAP_Dialog_FontChooser::event_previewClear(void)
{
	m_pFontPreview->clearScreen();
}

/*!
 * This method returns a std::string of the const char * value associated with the
 * the property szProp. Stolen from ap_Dialog_Lists.
 * It assumes properties and values are stored the array like this:
 * vecProp(n)   :   vecProp(n+1)
 * "property"   :   "value"
 */
const std::string XAP_Dialog_FontChooser::getVal(const std::string & sProp) const
{
	PropMap::const_iterator iter = m_mapProps.find(sProp);
	if(iter == m_mapProps.end()) {
		return "";
	}
	return iter->second;
}

/*!
 * This method sets all the local properties from a vector of pointers
 * to const gchar * strings of Property - Value pairs.
 * This method wipes out all the old values and clears all the bools
 * assciated with them.
 */
void XAP_Dialog_FontChooser::setAllPropsFromVec(const UT_Vector & vProps)
{
	UT_sint32 remCount = vProps.getItemCount();
	if(remCount <= 0)
		return;
	// BAD BAD, we have wrong count
	UT_ASSERT_HARMLESS(remCount % 2 == 0);
	if(remCount % 2) {
		remCount--;
	}
	m_mapProps.clear();
	UT_sint32 i = 0;
	for(i=0; i< remCount; i+=2)
	{
		m_mapProps.insert(std::make_pair((const char*)vProps.getNthItem(i), 
										 (const char*)vProps.getNthItem(i+1)));
	}
//
// Do the Text decorations
//
	const std::string sDecor = getVal("text-decoration");
	m_bUnderline = (NULL != strstr(sDecor.c_str(),"underline"));
	m_bOverline = (NULL != strstr(sDecor.c_str(),"overline"));
	m_bStrikeout = (NULL != strstr(sDecor.c_str(),"line-through"));
	m_bTopline = (NULL != strstr(sDecor.c_str(),"topline"));
	m_bBottomline = (NULL != strstr(sDecor.c_str(),"bottomline"));

	const std::string sDisplay = getVal("display");
	m_bHidden = !strcmp(sDisplay.c_str(),"none");
	
	const std::string sPosition = getVal("text-position");
	m_bSuperScript = strcmp(sPosition.c_str(),"superscript")==0;
	m_bSubScript = strcmp(sPosition.c_str(),"subscript")==0;
}

void XAP_Dialog_FontChooser::setFontFamily(const std::string& sFontFamily)
{
	m_sFontFamily = sFontFamily;
	addOrReplaceVecProp("font-family",sFontFamily);
}

void XAP_Dialog_FontChooser::setTextTransform(const std::string& sTextTransform)
{
	m_sTextTransform = sTextTransform;
	addOrReplaceVecProp("text-transform",sTextTransform);
}

void XAP_Dialog_FontChooser::setFontSize(const std::string& sFontSize)
{
	m_sFontSize = sFontSize;
	addOrReplaceVecProp("font-size",sFontSize);
}

void XAP_Dialog_FontChooser::setFontWeight(const std::string& sFontWeight)
{
	m_sFontWeight = sFontWeight;
	addOrReplaceVecProp("font-weight",sFontWeight);
}

void XAP_Dialog_FontChooser::setFontStyle(const std::string& sFontStyle)
{
	m_sFontStyle = sFontStyle;
	addOrReplaceVecProp("font-style",sFontStyle);
}

void XAP_Dialog_FontChooser::setColor(const std::string& sColor)
{
	m_sColor = sColor;
	addOrReplaceVecProp("color",sColor);
}

void XAP_Dialog_FontChooser::setBGColor(const std::string& sBGColor)
{
	m_sBGColor = sBGColor;
	addOrReplaceVecProp("bgcolor",sBGColor);
}

void XAP_Dialog_FontChooser::setSuperScript(bool bSuperScript)
{
	static char none[] = "superscript";
	static char empty[]  = "";

	if(bSuperScript)
	{
		addOrReplaceVecProp("text-position",none);
	}
	else
	{
		addOrReplaceVecProp("text-position",empty);
	}
	m_bSuperScript = bSuperScript;
	
}

void XAP_Dialog_FontChooser::setSubScript(bool bSubScript)
{
	static char none[] = "subscript";
	static char empty[]  = "";

	if(bSubScript)
	{
		addOrReplaceVecProp("text-position",none);
	}
	else
	{
		addOrReplaceVecProp("text-position",empty);
	}
	m_bSubScript = bSubScript;
	
}

void XAP_Dialog_FontChooser::setHidden(bool bHidden)
{
	static char none[] = "none";
	static char empty[]  = "";

	if(bHidden)
	{
		addOrReplaceVecProp("display",none);
	}
	else
	{
		addOrReplaceVecProp("display",empty);
	}
	m_bHidden = bHidden;
}

void XAP_Dialog_FontChooser::setBackGroundColor(const gchar * pBackground)
{
	m_sColorBackground = pBackground;
}

void XAP_Dialog_FontChooser::setFontDecoration(bool bUnderline, bool bOverline, bool bStrikeOut, bool bTopline, bool bBottomline)
{
	m_bUnderline = bUnderline;
	m_bOverline = bOverline;
	m_bStrikeout = bStrikeOut;
	m_bTopline = bTopline;
	m_bBottomline = bBottomline;

	static gchar s[50];
	UT_String decors;
	decors.clear();
	if(bUnderline)
		decors += "underline ";
	if(bStrikeOut)
		decors += "line-through ";
	if(bOverline)
		decors += "overline ";
	if(bTopline)
		decors += "topline ";
	if(bBottomline)
		decors += "bottomline ";
	if(!bUnderline && !bStrikeOut && !bOverline && !bTopline && !bBottomline)
		decors = "none";
	sprintf(s,"%s",decors.c_str());
	addOrReplaceVecProp("text-decoration",(const gchar *) s);
}

XAP_Dialog_FontChooser::tAnswer XAP_Dialog_FontChooser::getAnswer(void) const
{
	return m_answer;
}

bool XAP_Dialog_FontChooser::getChangedFontFamily(const gchar ** pszFontFamily) const
{
	bool bchanged = didPropChange(m_sFontFamily,getVal("font-family"));
	bool useVal = (bchanged && !m_bChangedFontFamily);
	if (pszFontFamily && useVal)
		*pszFontFamily = getVal("font-family").c_str();
	else if(pszFontFamily)
		*pszFontFamily = m_sFontFamily.c_str();
	return bchanged;
}

bool XAP_Dialog_FontChooser::getChangedTextTransform(const gchar ** pszTextTransform) const
{
	bool bchanged = didPropChange(m_sTextTransform,getVal("text-transform"));
	bool useVal = (bchanged && !m_bChangedTextTransform);
	if (pszTextTransform && useVal)
		*pszTextTransform = getVal("text-transform").c_str();
	else if(pszTextTransform)
		*pszTextTransform = m_sTextTransform.c_str();
	return bchanged;
}

bool XAP_Dialog_FontChooser::getChangedFontSize(const gchar ** pszFontSize) const
{
	bool bchanged = didPropChange(m_sFontSize,getVal("font-size"));
	bool useVal = (bchanged && !m_bChangedFontSize);
	if (pszFontSize && useVal)
		*pszFontSize = getVal("font-size").c_str();
	else if(pszFontSize)
		*pszFontSize = m_sFontSize.c_str();
	return bchanged;
}

bool XAP_Dialog_FontChooser::getChangedFontWeight(const gchar ** pszFontWeight) const
{
	bool bchanged = didPropChange(m_sFontWeight,getVal("font-weight"));
	bool useVal = (bchanged && !m_bChangedFontWeight);
	if (pszFontWeight && useVal)
		*pszFontWeight = getVal("font-weight").c_str();
	else if(pszFontWeight)
		*pszFontWeight = m_sFontWeight.c_str();
	return bchanged;
}

bool XAP_Dialog_FontChooser::getChangedFontStyle(const gchar ** pszFontStyle) const
{
	bool bchanged = didPropChange(m_sFontStyle,getVal("font-style"));
	bool useVal = (bchanged && !m_bChangedFontStyle);
	if (pszFontStyle && useVal)
		*pszFontStyle = getVal("font-style").c_str();
	else if(pszFontStyle)
		*pszFontStyle = m_sFontStyle.c_str();
	return bchanged;
}

bool XAP_Dialog_FontChooser::getChangedBGColor(const gchar ** pszBGColor) const
{
	bool bchanged = didPropChange(m_sBGColor,getVal("bgcolor"));
	bool useVal = (bchanged && !m_bChangedBGColor);
	if (pszBGColor && useVal)
		*pszBGColor = getVal("bgcolor").c_str();
	else if(pszBGColor)
		*pszBGColor = m_sBGColor.c_str();
	return bchanged;
}


bool XAP_Dialog_FontChooser::getChangedColor(const gchar ** pszColor) const
{
	bool bchanged = didPropChange(m_sColor,getVal("color"));
	bool useVal = (bchanged && !m_bChangedColor);
	if (pszColor && useVal)
		*pszColor = getVal("color").c_str();
	else if(pszColor)
		*pszColor = m_sColor.c_str();
	return bchanged;
}

/*!
 * Compare two prop values and gracefully handle the cases of NULL pointers
 */
bool XAP_Dialog_FontChooser::didPropChange(const std::string & v1, const std::string & v2) const
{
	if(v1.empty() && v2.empty())
		return false;
	else if(v1.empty() || v2.empty())
		return true;
	return v1 != v2;
}

bool XAP_Dialog_FontChooser::getChangedHidden(bool * pbHidden) const
{
	if (pbHidden)
		*pbHidden = m_bHidden;
	return m_bChangedHidden;
}

bool XAP_Dialog_FontChooser::getChangedSuperScript(bool * pbSuperScript) const
{
	if (pbSuperScript)
		*pbSuperScript = m_bSuperScript;
	return m_bChangedSuperScript;
}

bool XAP_Dialog_FontChooser::getChangedSubScript(bool * pbSubScript) const
{
	if (pbSubScript)
		*pbSubScript = m_bSubScript;
	return m_bChangedSubScript;
}

bool XAP_Dialog_FontChooser::getChangedUnderline(bool * pbUnderline) const
{
	if (pbUnderline)
		*pbUnderline = m_bUnderline;
	return m_bChangedUnderline;
}

bool XAP_Dialog_FontChooser::getChangedOverline(bool * pbOverline) const
{
	if (pbOverline)
		*pbOverline = m_bOverline;
	return m_bChangedOverline;
}

bool XAP_Dialog_FontChooser::getChangedStrikeOut(bool * pbStrikeOut) const
{
	if (pbStrikeOut)
		*pbStrikeOut = m_bStrikeout;
	return m_bChangedStrikeOut;
}

bool XAP_Dialog_FontChooser::getChangedTopline(bool * pbTopline) const
{
	if (pbTopline)
		*pbTopline = m_bTopline;
	return m_bChangedTopline;
}

bool XAP_Dialog_FontChooser::getChangedBottomline(bool * pbBottomline) const
{
	if (pbBottomline)
		*pbBottomline = m_bBottomline;
	return m_bChangedBottomline;
}

/////////////////////////////////////////////////////////////////////////

XAP_Preview_FontPreview::XAP_Preview_FontPreview(GR_Graphics * gc, const gchar * pszClrBackground)
	: XAP_Preview(gc),
		m_pFont(NULL),
		m_iAscent(0),
		m_iDescent(0),
		m_iHeight(0)
{
	if(pszClrBackground != NULL && strcmp(pszClrBackground,"transparent")!=0)
		UT_parseColor(pszClrBackground,m_clrBackground);
	else
		UT_setColor(m_clrBackground,255,255,255);

}

XAP_Preview_FontPreview::~XAP_Preview_FontPreview()
{
}

/*!
 * This method assigns a pointer to a
 *  vector with Char * strings of span-level properties
 * The vector has const gchar * string in the order
 * (n) Property (n+1) Value
 *
 * This code stolen from ap_Dialog_Lists.cpp
 */
void XAP_Preview_FontPreview::setVecProperties( const XAP_Dialog_FontChooser::PropMap * vFontProps)
{
	m_mapProps = vFontProps;
}

/*!
 * This method returns a pointer to the const char * value associated with the
 * the property szProp. Stolen from ap_Dialog_Lists.
 */
const std::string XAP_Preview_FontPreview::getVal(const std::string & sProp)
{
	XAP_Dialog_FontChooser::PropMap::const_iterator iter = m_mapProps->find(sProp);
	if(iter == m_mapProps->end()) {
		return "";
	}
	return iter->second;
}

/*
 *
 * Finally draw the characters in the preview.
 *
 */
void XAP_Preview_FontPreview::draw(const UT_Rect *clip)
{
	UT_UNUSED(clip);
//
// Get text decorations.
//
	bool isUnder,isOver,isStrike;

	const std::string sDecor = getVal("text-decoration");
	if(!sDecor.empty())
	{
		isUnder = (NULL != strstr(sDecor.c_str(),"underline"));
		isOver = (NULL != strstr(sDecor.c_str(),"overline"));
		isStrike = (NULL != strstr(sDecor.c_str(),"line-through"));
	}
	else
	{
		isUnder = false;
		isOver = false;
		isStrike = false;
	}

//
// Do foreground and background colors.
//
	UT_RGBColor FGcolor(0,0,0);
	const std::string sFGColor = getVal("color");
	if(!sFGColor.empty())
		UT_parseColor(sFGColor.c_str(),FGcolor);
	UT_RGBColor BGcolor(m_clrBackground);
	const std::string sBGColor = getVal("bgcolor");
	if(!sBGColor.empty() && strcmp(sBGColor.c_str(),"transparent") != 0)
		UT_parseColor(sBGColor.c_str(),BGcolor);
//
// Get the font and bold/italic- ness
//
	//GR_Font * pFont;
	std::string sFamily = getVal("font-family");
	std::string sStyle = getVal("font-style");
	std::string sVariant = getVal("font-variant");
	std::string sStretch = getVal("font-stretch");
	std::string sSize = getVal("font-size");
	std::string sWeight = getVal("font-weight");

	if(sFamily.empty())
		sFamily = "Times New Roman";

	if(sStyle.empty())
		sStyle = "normal";

	if(sVariant.empty())
		sVariant = "normal";

	if(sStretch.empty())
		sStretch = "normal";

	if(sSize.empty())
		sSize="12pt";

	if(sWeight.empty())
		sWeight = "normal";

	m_pFont = m_gc->findFont(sFamily.c_str(), sStyle.c_str(),
							 sVariant.c_str(), sWeight.c_str(),
							 sStretch.c_str(), sSize.c_str(),
							 NULL);

	UT_ASSERT_HARMLESS(m_pFont);
	if(!m_pFont)
	{
		clearScreen();
		return;
	}

	m_gc->setFont(m_pFont);		

	m_iAscent = m_gc->getFontAscent(m_pFont);
	m_iDescent = m_gc->getFontDescent(m_pFont);
	m_iHeight = m_gc->getFontHeight(m_pFont);
	
//
// Clear the screen!
//
	clearScreen();
//
// Calculate the draw coordinates position
//
	UT_sint32 iWinWidth = m_gc->tlu(getWindowWidth());
	UT_sint32 iWinHeight = m_gc->tlu(getWindowHeight());
	UT_sint32 iTop = (iWinHeight - m_iHeight)/2;
	UT_sint32 len = UT_UCS4_strlen(m_pszChars);
	UT_sint32 twidth = m_gc->measureString(m_pszChars,0,len,NULL);
	UT_sint32 iLeft = (iWinWidth - twidth)/2;
//
// Fill the background color
//
	GR_Painter painter(m_gc);

	if(!sBGColor.empty())
		painter.fillRect(BGcolor,iLeft,iTop,twidth,m_iHeight);
//
// Do the draw chars at last!
//
	m_gc->setColor(FGcolor);
	painter.drawChars(m_pszChars, 0, len, iLeft, iTop);

//
// Do the decorations
//
	if(isUnder)
	{
		UT_sint32 iDrop = iTop + m_iAscent + m_iDescent/3;
		painter.drawLine(iLeft,iDrop,iLeft+twidth,iDrop);
	}
	if(isOver)
	{
		UT_sint32 iDrop = iTop + m_gc->tlu(1) + (UT_MAX(m_gc->tlu(10),m_iAscent) - m_gc->tlu(10))/8;
		painter.drawLine(iLeft,iDrop,iLeft+twidth,iDrop);
	}
	if(isStrike)
	{
		UT_sint32 iDrop = iTop + m_iAscent * 2 /3;
		painter.drawLine(iLeft,iDrop,iLeft+twidth,iDrop);
	}

	// bad hardcoded color, but this will probably [ <-this assumption is the bad thing :) ] never be different anyway
	m_gc->setColor(UT_RGBColor(0,0,0));
	painter.drawLine(0, 0, m_gc->tlu(getWindowWidth()), 0);
	painter.drawLine(m_gc->tlu(getWindowWidth()) - m_gc->tlu(1), 0, m_gc->tlu(getWindowWidth()) - m_gc->tlu(1),
		       m_gc->tlu(getWindowHeight()));
	painter.drawLine(m_gc->tlu(getWindowWidth()) - m_gc->tlu(1), m_gc->tlu(getWindowHeight()) - m_gc->tlu(1), 0,
		       m_gc->tlu(getWindowHeight()) - m_gc->tlu(1));
	painter.drawLine(0, m_gc->tlu(getWindowHeight()) - m_gc->tlu(1), 0, 0);
}

void XAP_Preview_FontPreview::clearScreen(void)
{
	UT_sint32 iWidth = m_gc->tlu(getWindowWidth());
	UT_sint32 iHeight = m_gc->tlu(getWindowHeight());

	GR_Painter painter(m_gc);

	// clear the whole drawing area, except for the border
	painter.fillRect(m_clrBackground, 0 + m_gc->tlu(1), 0 + m_gc->tlu(1), iWidth - m_gc->tlu(2), iHeight - m_gc->tlu(2));
}
