/////////////////////////////////////////////////////////////////////////////
// Name:        wxputils.cpp
// Purpose:     wxPackage API utilities
// Author:      Julian Smart, Francesco Montorsi, Daniel J. Lauk
// Modified by:
// Created:     2004-11-07
// RCS-ID:      $Id: wxputils.cpp,v 1.10 2007/01/28 14:35:29 frm Exp $
// Copyright:   (c) Julian Smart
// Licence:     wxWidgets licence
/////////////////////////////////////////////////////////////////////////////


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

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif

// includes
#include <wx/fileconf.h>
#include <wx/filename.h>
#include <wx/zipstrm.h>
#include <wx/wfstream.h>
#include <wx/ptr_scpd.h>
#include <wx/dir.h>

#include "wxp/package.h"
#include "wxp/wxp.h"


// ----------------------------------------------------------------------------
// misc
// ----------------------------------------------------------------------------

bool wxIsPlainWord(const wxString &tocheck)
{
#define ALLOWEDCHARS   "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"

    wxString str(wxT(ALLOWEDCHARS));
    for (size_t i=0; i<tocheck.Len(); i++)
        if (!str.Contains(tocheck[i]))
            return false;
    return true;
}

wxString wxGetPlainWordAllowedCharacters()
{
    return wxString(wxT(ALLOWEDCHARS));
}

wxString wxStripOutHTML(const wxString &str)
{
    bool inside_tag = false;
    wxString ret;
    const wxChar *source = str;

    for (size_t i=0, max=str.length(); i<max; i++)
    {
        switch (str[i])
        {
            case wxT('<'):
                if (inside_tag)
                {
                    wxLogDebug(wxT("Invalid HTML !"));
                    return wxEmptyString;
                }
                inside_tag = true;
                break;

            case wxT('>'):
                if (!inside_tag)
                {
                    wxLogDebug(wxT("Invalid HTML !"));
                    return wxEmptyString;
                }
                inside_tag = false;
                break;

            default:
                if (!inside_tag)
                    ret << str[i];
        }
    }

    return ret;
}


// ----------------------------------------------------------------------------
// wxArrayString <-> wxString
// ----------------------------------------------------------------------------

wxString wxArray2String(const wxArrayString &arr, const wxString &sep)
{
    wxString str;
    size_t max=arr.GetCount();

    if (max == 0)
        return wxEmptyString;

    str.Alloc(max * 10);        // 10 is a very arbitrary number
    str = arr[0];

    for (size_t i=1; i<max; i++)
        str += sep + arr[i];

    return str;
}

wxArrayString wxString2Array(const wxString &str, const wxString &sep)
{
    return wxStringTokenize(str, sep);
}



// ----------------------------------------------------------------------------
// global substitution helpers
// ----------------------------------------------------------------------------

wxString wxDoStringSubstitution(const wxString &str,
                                const wxStringHashMap &hash)
{
    wxString ret;
    for (const wxChar *p = str.c_str(); *p != wxT('\0'); p++)
    {
        switch (*p)
        {
        case wxT('$'):
            // is this a substitution marker?
            if (*(p+1) == wxT('('))
            {
                p += 2;     // skip the $( part

                // seems so...
                const wxChar *pstart = p;
                for (; *p != wxT('\0'); p++)
                    if (*p == wxT(')'))
                        break;

                if (*p != wxT(')'))
                    break;      // not valid however

                // does this key exists?
                wxString keyName(pstart, (size_t)(p - pstart)/sizeof(wxChar));
                wxStringHashMap::const_iterator key = hash.find(keyName);
                if (key == hash.end())
                {
                    // not a known key... skip it
                    ret << wxWPM_SUBSTITUTION_START_MARKER 
                        << keyName 
                        << wxWPM_SUBSTITUTION_END_MARKER;
                    break;
                }

                // do the replacement
                ret << key->second;
            }
            break;

        default:
            ret << *p;
        }
    }

    return ret;
}

void wxMergeHashMap(wxStringHashMap &out, const wxStringHashMap &hash)
{
    for (wxStringHashMap::const_iterator it = hash.begin(); it != hash.end(); ++it)
    {
        wxString key = it->first, value = it->second;
        out[key] = value;
    }
}

bool wxNormalizeSubstitutionHashMap(wxStringHashMap &hash)
{
    // having a max substitution depth saves us from being trapped
    // in an endless loop when trying to evaluate recursively-defined variables!
#define MAX_SUBSTITUTION_DEPTH  3

    bool success = true;
    for ( wxStringHashMap::const_iterator it = hash.begin(); it != hash.end(); ++it )
    {
        wxString key = it->first, value = it->second;

        size_t substCount = 0;
        while (value.Contains(wxWPM_SUBSTITUTION_START_MARKER) && 
               substCount < MAX_SUBSTITUTION_DEPTH)
        {
            // try substitute this same hashmap into this value
            value = wxDoStringSubstitution(value, hash);
            substCount++;
        }

        if (substCount > 0)
            hash[key] = value;      // update key value...

        success &= !value.Contains(wxWPM_SUBSTITUTION_START_MARKER);
    }

#ifdef __WXDEBUG__
    if (!success)
        wxLogDebug(wxT("Cannot normalize a substitution hashmap..."));
#endif

    return success;
}



// ----------------------------------------------------------------------------
// wxPortId list helpers
// ----------------------------------------------------------------------------

long wxGetFullPortIdList()
{
    return (1 << wxPORT_MAX) - 1;
}

wxString wxPortIdList2String(long n)
{
    wxString ret;
    for (size_t i=0; i < wxPORT_MAX; i++)
    {
        wxPortId cur = (wxPortId)(1 << i);
        if ((n & cur) != 0)
            ret += wxPlatformInfo::GetPortIdName(cur, false) + wxT(",");
    }
    if (ret.EndsWith(wxT(",")))
        ret.RemoveLast();
    return ret;
}

long wxString2PortIdList(const wxString &str)
{
    wxArrayString temp = wxString2Array(str);
    long ret = 0;
    for (size_t i=0; i<temp.GetCount(); i++)
    {
        wxPortId t = wxPlatformInfo::GetPortId(temp[i]);
        if (t != (wxPortId)-1)
            ret |= t;
    }
    return ret;
}
