/////////////////////////////////////////////////////////////////////////////
// Name:        src/common/fontmgrcmn.cpp
// Purpose:     font management for ports that don't have their own
// Author:      Vaclav Slavik
// Created:     2006-11-18
// RCS-ID:      $Id: fontmgrcmn.cpp 43550 2006-11-20 20:45:57Z VS $
// Copyright:   (c) 2001-2002 SciTech Software, Inc. (www.scitechsoft.com)
//              (c) 2006 REA Elektronik GmbH
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#include "wx/private/fontmgr.h"

#include "wx/listimpl.cpp"
#include "wx/hashmap.h"

WX_DECLARE_LIST(wxFontInstance, wxFontInstanceList);
WX_DEFINE_LIST(wxFontInstanceList)
WX_DEFINE_LIST(wxFontBundleList)
WX_DECLARE_HASH_MAP(wxString, wxFontBundle*,
                    wxStringHash, wxStringEqual,
                    wxFontBundleHash);

// ============================================================================
// implementation
// ============================================================================

// ----------------------------------------------------------------------------
// wxFontFaceBase
// ----------------------------------------------------------------------------

wxFontFaceBase::wxFontFaceBase()
    : m_refCnt(0)
{
    m_instances = new wxFontInstanceList;
    m_instances->DeleteContents(true);
}

wxFontFaceBase::~wxFontFaceBase()
{
    delete m_instances;
}

void wxFontFaceBase::Acquire()
{
    m_refCnt++;
}

void wxFontFaceBase::Release()
{
    if ( --m_refCnt == 0 )
    {
        m_instances->Clear();
    }
}

wxFontInstance *wxFontFaceBase::GetFontInstance(float ptSize, bool aa)
{
    wxASSERT_MSG( m_refCnt > 0, _T("font library not loaded!") );

    wxFontInstance *i;
    wxFontInstanceList::Node *node;

    for ( node = m_instances->GetFirst(); node; node = node->GetNext() )
    {
        i = node->GetData();
        if ( i->GetPointSize() == ptSize && i->IsAntiAliased() == aa )
            return i;
    }

    i = CreateFontInstance(ptSize, aa);
    m_instances->Append(i);
    return i;
}

// ----------------------------------------------------------------------------
// wxFontBundleBase
// ----------------------------------------------------------------------------

wxFontBundleBase::wxFontBundleBase()
{
    for (int i = 0; i < FaceType_Max; i++)
        m_faces[i] = NULL;
}

wxFontBundleBase::~wxFontBundleBase()
{
    for (int i = 0; i < FaceType_Max; i++)
        delete m_faces[i];
}

wxFontFace *wxFontBundleBase::GetFace(FaceType type) const
{
    wxFontFace *f = m_faces[type];

    wxCHECK_MSG( f, NULL, _T("no such face in font bundle") );

    f->Acquire();

    return f;
}

wxFontFace *
wxFontBundleBase::GetFaceForFont(const wxFontMgrFontRefData& font) const
{
    wxASSERT_MSG( font.GetFaceName().empty() || font.GetFaceName() == GetName(),
                  _T("calling GetFaceForFont for incompatible font") );

    int type = FaceType_Regular;

    if ( font.GetWeight() == wxBOLD )
        type |= FaceType_Bold;

    // FIXME -- this should read "if ( font->GetStyle() == wxITALIC )",
    // but since MGL neither DFB supports slant, we try to display it with
    // italic face (better than nothing...)
    if ( font.GetStyle() == wxITALIC || font.GetStyle() == wxSLANT )
    {
        if ( HasFace((FaceType)(type | FaceType_Italic)) )
            type |= FaceType_Italic;
    }

    if ( !HasFace((FaceType)type) )
    {
        for (int i = 0; i < FaceType_Max; i++)
        {
            if ( HasFace((FaceType)i) )
                return GetFace((FaceType)i);
        }

        wxFAIL_MSG( _T("no face") );
        return NULL;
    }

    return GetFace((FaceType)type);
}

// ----------------------------------------------------------------------------
// wxFontsManagerBase
// ----------------------------------------------------------------------------

wxFontsManager *wxFontsManagerBase::ms_instance = NULL;

wxFontsManagerBase::wxFontsManagerBase()
{
    m_hash = new wxFontBundleHash();
    m_list = new wxFontBundleList;
    m_list->DeleteContents(true);
}

wxFontsManagerBase::~wxFontsManagerBase()
{
    delete m_hash;
    delete m_list;
}

/* static */
wxFontsManager *wxFontsManagerBase::Get()
{
    if ( !ms_instance )
        ms_instance = new wxFontsManager();
    return ms_instance;
}

/* static */
void wxFontsManagerBase::CleanUp()
{
    wxDELETE(ms_instance);
}

wxFontBundle *wxFontsManagerBase::GetBundle(const wxString& name) const
{
    return (*m_hash)[name.Lower()];
}

wxFontBundle *
wxFontsManagerBase::GetBundleForFont(const wxFontMgrFontRefData& font) const
{
    wxFontBundle *bundle = NULL;

    wxString facename = font.GetFaceName();
    if ( !facename.empty() )
        bundle = GetBundle(facename);

    if ( !bundle )
    {
        facename = GetDefaultFacename((wxFontFamily)font.GetFamily());
        if ( !facename.empty() )
            bundle = GetBundle(facename);
    }

    if ( !bundle )
    {
       if ( m_list->GetFirst() )
           bundle = m_list->GetFirst()->GetData();
       else
           wxFAIL_MSG(wxT("Fatal error, no fonts available!"));
    }

    return bundle;
}

void wxFontsManagerBase::AddBundle(wxFontBundle *bundle)
{
    (*m_hash)[bundle->GetName().Lower()] = bundle;
    m_list->Append(bundle);
}


// ----------------------------------------------------------------------------
// wxFontMgrFontRefData
// ----------------------------------------------------------------------------

wxFontMgrFontRefData::wxFontMgrFontRefData(int size,
                                           int family,
                                           int style,
                                           int weight,
                                           bool underlined,
                                           const wxString& faceName,
                                           wxFontEncoding encoding)
{
    if ( family == wxDEFAULT )
        family = wxSWISS;
    if ( style == wxDEFAULT )
        style = wxNORMAL;
    if ( weight == wxDEFAULT )
        weight = wxNORMAL;
    if ( size == wxDEFAULT )
        size = 12;

    m_info.family = (wxFontFamily)family;
    m_info.faceName = faceName;
    m_info.style = (wxFontStyle)style;
    m_info.weight = (wxFontWeight)weight;
    m_info.pointSize = size;
    m_info.underlined = underlined;
    m_info.encoding = encoding;

    m_noAA = false;

    m_fontFace = NULL;
    m_fontBundle = NULL;
    m_fontValid = false;
}

wxFontMgrFontRefData::wxFontMgrFontRefData(const wxFontMgrFontRefData& data)
{
    m_info = data.m_info;
    m_noAA = data.m_noAA;

    m_fontFace = data.m_fontFace;
    m_fontBundle = data.m_fontBundle;
    m_fontValid = data.m_fontValid;
    if ( m_fontFace )
        m_fontFace->Acquire();
}

wxFontMgrFontRefData::~wxFontMgrFontRefData()
{
    if ( m_fontFace )
        m_fontFace->Release();
}

wxFontBundle *wxFontMgrFontRefData::GetFontBundle() const
{
    wxConstCast(this, wxFontMgrFontRefData)->EnsureValidFont();
    return m_fontBundle;
}

wxFontInstance *
wxFontMgrFontRefData::GetFontInstance(float scale, bool antialiased) const
{
    wxConstCast(this, wxFontMgrFontRefData)->EnsureValidFont();
    return m_fontFace->GetFontInstance(m_info.pointSize * scale,
                                       antialiased && !m_noAA);
}

void wxFontMgrFontRefData::SetPointSize(int pointSize)
{
    m_info.pointSize = pointSize;
    m_fontValid = false;
}

void wxFontMgrFontRefData::SetFamily(int family)
{
    m_info.family = (wxFontFamily)family;
    m_fontValid = false;
}

void wxFontMgrFontRefData::SetStyle(int style)
{
    m_info.style = (wxFontStyle)style;
    m_fontValid = false;
}

void wxFontMgrFontRefData::SetWeight(int weight)
{
    m_info.weight = (wxFontWeight)weight;
    m_fontValid = false;
}

void wxFontMgrFontRefData::SetFaceName(const wxString& faceName)
{
    m_info.faceName = faceName;
    m_fontValid = false;
}

void wxFontMgrFontRefData::SetUnderlined(bool underlined)
{
    m_info.underlined = underlined;
    m_fontValid = false;
}

void wxFontMgrFontRefData::SetEncoding(wxFontEncoding encoding)
{
    m_info.encoding = encoding;
    m_fontValid = false;
}

void wxFontMgrFontRefData::SetNoAntiAliasing(bool no)
{
    m_noAA = no;
}


void wxFontMgrFontRefData::EnsureValidFont()
{
    if ( !m_fontValid )
    {
        wxFontFace *old = m_fontFace;

        m_fontBundle = wxFontsManager::Get()->GetBundleForFont(*this);
        m_fontFace = m_fontBundle->GetFaceForFont(*this);

        if ( old )
            old->Release();
    }
}
