//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000-2018 Tripwire,
// Inc. Tripwire is a registered trademark of Tripwire, Inc.  All rights
// reserved.
//
// This program is free software.  The contents of this file are subject
// to 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.  You may redistribute it and/or modify it
// only in compliance with the GNU General Public License.
//
// This program is distributed in the hope that it will be useful.
// However, this program is distributed AS-IS WITHOUT ANY
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
// FOR A PARTICULAR PURPOSE.  Please 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
//
// Nothing in the GNU General Public License or any other license to use
// the code or files shall permit you to use Tripwire's trademarks,
// service marks, or other intellectual property without Tripwire's
// prior written consent.
//
// If you have any questions, please contact Tripwire, Inc. at either
// info@tripwire.org or www.tripwire.org.
//
//
// Name....: resources.h
// Date....: 05/06/99
// Creator.: Robert DiFalco (rdifalco)
//
// Abstraction for a keyed collection of resources including a
// specialization for dealing with message resources.
//

#ifndef __RESOURCES_H
#define __RESOURCES_H


//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Dependencies
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

#include <map>     // for: Resource Table Implementation
#include "debug.h" // for: ASSERT and Trace
#include "error.h" // for: eInternal


//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Classes Declared in this module
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

template<class KEY, class RSC> class cResources_;
template<class KEY, class CHR> class cMessages_;


//=============================================================================
// cResources_<KEY,REZ> -- Keyed collection of resources
//-----------------------------------------------------------------------------
// SYNOPSIS:
//  Used to maintain a table of resources that are indexed by KEY
//  type values. The <REZ> type can be any valid type that is
//  assignable.
//
// CONSTRAINTS:
//  <KEY> must be a valid key type for the std::map concept
//  <REZ> must be a valid value type for the std::map concept.
//
// INVARIANTS:
//
//
template<class KeyT, class RscT> class cResources_
{
    //--Type Definitions

private:
    typedef std::map<KeyT, RscT> Table; // Storage Rep


public:
    // Type definitions

    typedef KeyT        Key; // The resource id
    typedef KeyT&       KeyRef;
    typedef const KeyT& ConstKeyRef;

    typedef RscT         Value; // The managed resource
    typedef Value&       ValueRef;
    typedef const Value& ConstValueRef;

    typedef typename Table::value_type Pair;


    //--Interface.

public:
    // Observers

    ConstValueRef Get(ConstKeyRef) const;


    // Modifiers

    void Put(const Pair*, const Pair*);
    void Put(ConstKeyRef, Value);


    //--Implementation.

protected:
    static ConstValueRef DefaultValueRef(); // NOTE:

private:
    Table m_table;
};


//=============================================================================
// cMessages_<KEY,CHAR> -- Specialization of Resources_ for message strings
//-----------------------------------------------------------------------------
//  SYNOPSIS:
//      Specialization of cResources_<KEY,REZ> to store message strings
//      instead of generalized resource objects. In the future this can
//      be refactored to use message objects so that they can come from
//      a dynamic  source (i.e., not a string-literal) such as persistant
//      storage or some algorithm based on the concatenation of elements.
//      The cMessages_<KEY,CHAR> abstraction allows a program to be
//      character type independent so that it may use ANSI, MBCS, or
//      UNICODE within the same application instance. This is achieved by
//      not allowing the client to expect a specific character sequence
//      representation, such as the near ubiquitous const char*. For
//      example:
//
//          const char* psz = messages.GetAnsi( IDS_SOMESTRING );
//
//      The above call requires the caller to be responsible for memory
//      resources in the case of a conversion. However, there is no
//      clear way to know up front how many bytes will be required.
//      Another equally unsatisfactory solution is to allocate the
//      memory within cMessages and require the caller to release the
//      the resources when they are done with the message. If we instead
//      require calls in the following form:
//
//          std::string str = messages.GetAnsi( IDS_SOMESTRING );
//
//      We can allows assume proper resource use regardless of whether
//      a simple character pointer is returned or newly allocated
//      resources used to convert the stored message.
//
//
//  CONSTRAINTS:
//      As with all classes which manipulate character sequences, CHAR
//      must be a valid character type as defined by the STDCPP standard.
//
//
template<class KeyT, class CharT> class cMessages_ : public cResources_<KeyT, const CharT*>
{
    //--Type definitions

protected:
    typedef cResources_<KeyT, const CharT*> Resources;

public:
    typedef std::basic_string<CharT> String;
    typedef const KeyT&              ConstKeyRef;


    //--Interface.

public:
    String      Get(ConstKeyRef) const;
    std::string GetAnsi(ConstKeyRef) const;
#if SUPPORTS_WCHART
    std::wstring GetWide(ConstKeyRef) const;
#endif //SUPPORTS_WCHART
};


//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// IMPLEMENTATION: cResources_<KEY_TYPE, RESOURCE_TYPE>
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

// NOTE: Macros to ease any changes in template arguments

#define Resource_Template template<class KeyT, class RscT>
#define Resource_Class cResources_<KeyT, RscT>


Resource_Template void Resource_Class::Put(const Pair* beg, const Pair* end)
{
    for (const Pair* at = beg; at != end; ++at)
    {
        if (!m_table.insert(*at).second)
        {
            ASSERTMSG(0, "Resource key already exists!");
            // throw eInternal( "Resource key already exists!" );
        }
    }
}

// By value allows the user to Resource_table.Put( 6, "Literal" );
Resource_Template inline void Resource_Class::Put(ConstKeyRef id, Value sResource)
{
    if (m_table.insert(Pair(id, sResource)).second == false)
    {
        ASSERTMSG(0, "Resource key already exists!");
        // throw eInternal( "Resource key already exists!" );
    }
}

Resource_Template typename Resource_Class::ConstValueRef Resource_Class::Get(ConstKeyRef id) const
{
    typename Table::const_iterator at = m_table.find(id);

    if (at == m_table.end())
    {
#ifdef DEBUG
        std::cerr << "*** Error Resource_Class::Get() [" __FILE__ ":" << __LINE__ << "]: Resource not found\n";
#endif

        return DefaultValueRef();
    }
    else
    {
        return (*at).second;
    }
}

Resource_Template typename Resource_Class::ConstValueRef Resource_Class::DefaultValueRef()
{
    static Value _default;
    return _default;
}


#undef Resource_Template //template< class KeyT, class RscT >
#undef Resource_Class    //Resources_<KeyT,RscT>


//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// IMPLEMENTATION: cMessages_<KEY_TYPE, RESOURCE_TYPE>
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

// NOTE: Macros to ease any changes in template arguments

#define Message_Template template<class KeyT, class CharT>
#define Message_Class cMessages_<KeyT, CharT>


Message_Template inline typename Message_Class::String Message_Class::Get(ConstKeyRef id) const
{
    typename Message_Class::Value msg = Resources::Get(id);
    return (msg == this->DefaultValueRef()) ? String() : String(msg);
}


/// NOTE: Can't have anonymous NS in Headers

namespace tss
{
namespace rsc
{
/* WARNING:RAD -- Cannot use StringUtil or CodeConvert since
        these use ErrorMessages and String Resources! */

inline void ToNTMBS(std::string& nbs, const std::string& src)
{
    nbs.assign(src);
}

#if SUPPORTS_WCHART

inline void ToNTMBS(std::string& nbs, const std::wstring& wcs)
{
    nbs.resize(MB_CUR_MAX * wcs.size());

    size_t nWrote = ::wcstombs(const_cast<char*>(nbs.c_str()), wcs.c_str(), nbs.size() + 1);

    if (nWrote == (size_t)-1)
    {
        ASSERTMSG(false, "Conversion error in cMessages_!");
        //          throw eCharacterEncoding( TSS_GetString( cCore, core::STR_ERR_BADCHAR ) );
    }

    nbs.resize(nWrote);
}

inline void ToNTWCS(std::wstring& dst, const std::wstring& src)
{
    dst.assign(src);
}

inline void ToNTWCS(std::wstring& wcs, const std::string& nbs)
{
    wcs.resize(nbs.size());

    size_t nWrote = ::mbstowcs(const_cast<wchar_t*>(wcs.c_str()), nbs.c_str(), wcs.size() + 1);

    if (nWrote == (size_t)-1)
    {
        ASSERTMSG(false, "Conversion error in cMessages_!");
        //          throw eCharacterEncoding( TSS_GetString( cCore, core::STR_ERR_BADCHAR ) );
    }

    wcs.resize(nWrote);
}
#endif //SUPPORTS_WCHART
} // namespace rsc
} // namespace tss


Message_Template inline std::string Message_Class::GetAnsi(ConstKeyRef id) const
{
    typename Message_Class::Value msg = Resources::Get(id);

    if (msg == this->DefaultValueRef())
    {
        return std::string();
    }
    else
    {
        std::string nbs;
        tss::rsc::ToNTMBS(nbs, String(msg));
        return nbs;
    }
}


#if SUPPORTS_WCHART

Message_Template inline std::wstring Message_Class::GetWide(ConstKeyRef id) const
{
    Value msg = Resources::Get(id);

    if (msg == DefaultValueRef())
    {
        return std::string();
    }
    else
    {
        std::wstring wcs;
        tss::rsc::ToNTWCS(wcs, String(msg));
        return wcs;
    }
}

#endif //SUPPORTS_WCHART


#undef Message_Template
#undef Message_Class

#endif //__RESOURCES_H


//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// OLD CODE * OLD CODE * OLD CODE * OLD CODE * OLD CODE * OLD CODE * OLD CODE
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

#ifdef RAD_OLDCODE

//=============================================================================
// cMessages_<char & wchar_t> -- Specializations
//-----------------------------------------------------------------------------
//  SYNOPSIS:
//      MSVC does not yet support specializations. As a compromise, we fully
//      specialize on TCHR but assume a key type of "const int".
//

// for SUNPro enums are ints, not const ints
#if IS_SUNPRO
#    define ENUM_TYPE int
#else
#    define ENUM_TYPE const int
#endif

template<> class cMessages_<ENUM_TYPE, char> : public cResources_<ENUM_TYPE, const char*>
{
protected:
    typedef cResources_<ENUM_TYPE, const char*> Resources;

public:
    typedef std::string String;
    typedef int         ConstKeyRef; // Better than <const int&>


    String Get(ConstKeyRef id) const
    {
        Value msg = Resources::Get(id);
        return (msg != DefaultValueRef()) ? String(msg) : String();
    }

    String GetAnsi(ConstKeyRef id) const
    {
        return Get(id);
    }

#if SUPPORTS_WCHART
    std::wstring GetWide(ConstKeyRef id) const
    {
        Value msg = Resources::Get(id);

        if (msg == DefaultValueRef())
        {
            return std::wstring();
        }
        else
        {
            String::size_type N = String::traits_type::length(msg);

            std::wstring dst(N, 0x00);
            std::ctype<wchar_t>().widen(msg, msg + N, dst.begin());

            return dst;
        }
    }
#endif //SUPPORTS_WCHART
};

#if SUPPORTS_WCHART

template<> class cMessages_<const int, wchar_t> : public cResources_<const int, const wchar_t*>
{
protected:
    typedef cResources_<const int, const wchar_t*> Resources;

public:
    typedef std::wstring String;
    typedef int          ConstKeyRef; // Better than <const int&>


    String Get(ConstKeyRef id) const
    {
        Value msg = Resources::Get(id);
        if (msg != DefaultValueRef())
            return String(msg);
        else
            return String();
    }

    std::string GetAnsi(ConstKeyRef id) const
    {
        Value msg = Resources::Get(id);

        if (msg == DefaultValueRef())
        {
            return std::string();
        }
        else
        {
            String::size_type N = String::traits_type::length(msg);

            std::string dst(N, 0x00);
            std::ctype<CharT>().narrow(msg, msg + N, '?', dst.begin());

            return dst;
        }
    }

    String GetWide(ConstKeyRef id) const
    {
        return Get(id);
    }
};

#    endif //SUPPORTS_WCHART

#endif //RAD_OLDCODE
