/////////////////////////////////////////////////////////////////////////////
// Name:        packageutils.h
// Purpose:     wxPackageDataWithRef, wxPackageId, wxPackageIdArray,
//              wxPackageCondition, wxPackageHTMLOutputFormat
// Author:      Francesco Montorsi
// Modified by:
// Created:     2006-11-07
// RCS-ID:      $Id: packageutils.h,v 1.3 2007/01/28 14:35:29 frm Exp $
// Copyright:   (c) Francesco Montorsi
// Licence:     wxWidgets licence
/////////////////////////////////////////////////////////////////////////////

#ifndef _PACKAGEUTILS_H_
#define _PACKAGEUTILS_H_

// Includes
#include "wx/string.h"
#include "wx/log.h"
#include "wx/platinfo.h"

#include "wxp/version.h"
#include "wxp/compiler.h"

class wxPackage;
class wxPackageDataWithRef;
class wxPackageId;
class wxPackageIdArray;
class wxPackageCondition;
class wxPackageHTMLOutputFormat;

extern wxPackageDataWithRef wxEmptyPackageDataWithRef;
extern wxPackageId wxEmptyPackageId;
extern wxPackageIdArray wxEmptyPackageIdArray;
extern wxPackageCondition wxEmptyPackageCondition;
extern wxPackageHTMLOutputFormat wxEmptyPackageHTMLOutputFormat;


// ----------------------------------------------------------------------------
// enums
// ----------------------------------------------------------------------------

//! The possible SubstituteInfo() contexts.
enum wxPackageSubstituteInfoContext
{
    wxPSIC_INVALID = -1,

    wxPSIC_WXHTML,      // SubstituteInfo should output HTML compatible with wxHtmlWindow
    wxPSIC_XHTML,       // SubstituteInfo should output XHTML compliant code

    wxPSIC_MAX
};

wxDEFINE_STRING2ENUM(PackageSubstituteInfoContext)


// ----------------------------------------------------------------------------
// wxPackageDataWithRef
// ----------------------------------------------------------------------------

//! Some kind of string data with a local and a remote reference associated.
//! The local reference should be used when the package which owns the data
//! has been downloaded while the remote reference should be used when the
//! associated package has not been downloaded yet.
class wxPackageDataWithRef : public wxObject
{
public:
    wxPackageDataWithRef(
        const wxString &data = wxEmptyString,
        const wxString &href = wxEmptyString,
        const wxString &localref = wxEmptyString)
        : m_strData(data), m_strHref(href), m_strLocalRef(localref) {}

public:     // getters

    wxString GetData() const
        { return m_strData; }
    wxString GetHref() const
        { return m_strHref; }
    wxString GetLocalRef() const
        { return m_strLocalRef; }

    //! Returns the local reference if it's non-empty; otherwise the href.
    wxString GetBestRef() const
    {
        if (m_strLocalRef.StartsWith(wxT("file:")) ||
            wxFileExists(m_strLocalRef))
            return m_strLocalRef;
        return m_strHref;
    }

public:     // setters

    void SetData(const wxString &data)
        { m_strData=data; }
    void SetHref(const wxString &data)
        { m_strHref=data; }
    void SetLocalRef(const wxString &data)
        { m_strLocalRef=data; }

public:     // misc

    //! Makes this local reference absolute using the given path.
    void ExpandLocalRef(const wxString &path);

    //! Expands this local reference to a combined protocol URL.
    //! I.e. if you pass to this function the absolute path "/dir1/file.wxz"
    //! of a WXZ file, this function will set the local reference "dir2/dir3/file"
    //! to "file:/dir1/file.wxz#zip:dir2/dir3/file".
    void ExpandLocalRefToWXZRef(const wxString &wxzpath);

    //! To be used for remote packages. Clears the local reference so that
    //! the href will be used.
    void RemoveLocalRef()
        { m_strLocalRef.Empty(); }

protected:
    wxString m_strData, m_strHref, m_strLocalRef;
};

// the array
WX_DECLARE_OBJARRAY(wxPackageDataWithRef, wxPackageDataWithRefArray);



// ----------------------------------------------------------------------------
// wxPackageId
// ----------------------------------------------------------------------------

//! Identifies univocally a package by its name (case insensitive) and
//! its version.
class wxPackageId : public wxObject
{
public:
    wxPackageId(const wxString &name = wxEmptyString,
                const wxVersion &ver = wxEmptyVersion)
        : m_strName(name), m_version(ver) {}
    virtual ~wxPackageId() {}

public:     // operators

    bool operator== (const wxPackageId &i) const
        { return m_strName.CmpNoCase(i.m_strName) == 0 && m_version==i.m_version; }
    bool operator!= (const wxPackageId &i) const
        { return !(*this == i); }

public:     // getters

    wxString GetName() const { return m_strName; }
    wxVersion GetVersion() const { return m_version; }

public:     // setters

    void SetName(const wxString &name)
        { m_strName=name; }
    void SetVersion(const wxVersion &ver)
        { m_version=ver; }

    bool IsOk() const
    {
        return !m_strName.IsEmpty() && !m_strName.Contains(wxT(' ')) &&
               m_version.IsOk();
    }

protected:
    wxString m_strName;
    wxVersion m_version;
};

// the array
WX_DECLARE_OBJARRAY(wxPackageId, wxPackageIdArrayHelper);



// ----------------------------------------------------------------------------
// wxPackageIdArray
// ----------------------------------------------------------------------------

class wxPackageIdArray : public wxPackageIdArrayHelper
{
public:

    //! Default constructor does nothing.
    wxPackageIdArray() {}

public:

    //! Returns true if all IDs are valid.
    bool IsOk() const
    {
        for (size_t i=0; i<GetCount(); i++)
            if (!Item(i).IsOk())
                return false;
        return true;
    }

    int Index(const wxPackageId &p) const
    {
        for (size_t i=0; i<GetCount(); i++)
            if (Item(i) == p)
                return i;
        return wxNOT_FOUND;
    }

    void Dump()
    {
        for (size_t i=0; i<GetCount(); i++)
            wxLogDebug(wxT("%s %s"), Item(i).GetName().c_str(),
                       Item(i).GetVersion().GetAsString().c_str());
    }
};



// ----------------------------------------------------------------------------
// wxPackageCondition
// ----------------------------------------------------------------------------

//! A generic condition (represented in WXP syntax by an <if> block)
//! about the platforms, compiler formats, build systems and stages
//! to which something applies.
class wxPackageCondition : public wxObject
{
    DECLARE_DYNAMIC_CLASS(wxPackageCondition)

public:
    wxPackageCondition(long plat = 0,
                       long formats = 0,
                       long buildsys = 0,
                       long stages = 0)
    {
        SetFormats(formats);
        SetPlatforms(plat);
        SetBuildSystems(buildsys);
        SetStages(stages);
    }
    virtual ~wxPackageCondition() {}


    bool operator==(const wxPackageCondition &cond) const;
    bool operator!=(const wxPackageCondition &cond) const
        { return !(*this == cond); }

public:     // getters

    long GetFormats() const
        { return m_format; }
    long GetPlatforms() const
        { return m_platform; }
    long GetBuildSystems() const
        { return m_buildSys; }
    long GetStages() const
        { return m_stages; }

    // a more readable way for the call to wxPlatformInfo:
    static long GetCurrentPlatform()
        { return wxPlatformInfo::Get().GetPortId(); }

public:     // string getters

    wxString GetFormatsStr() const
        { return wxPackageCompilerFormatList2String(m_format); }
    wxString GetBuildSystemsStr() const
        { return wxPackageBuildSystemTypeList2String(m_buildSys); }
    wxString GetStagesStr() const
        { return wxPackageBuildSystemStageList2String(m_stages); }
    wxString GetPlatformsStr() const
    {
        wxString ret;
        for (size_t i=0; i < wxPORT_MAX; i++)
        {
            wxPortId cur = (wxPortId)(1 << i);
            if ((m_platform & cur) != 0)
                ret += wxPlatformInfo::GetPortIdName(cur, false) + wxT(",");
        }

        if (ret.EndsWith(wxT(",")))
            ret.RemoveLast();
        return ret;
    }

public:     // setters

    void SetFormats(long fmt)
        { m_format=fmt; }
    void SetPlatforms(long plat)
        { m_platform=plat; }
    void SetBuildSystems(long build)
        { m_buildSys=build; }
    void SetStages(long stages)
        { m_stages=stages; }

public:     // checkers

    //! Returns true if this option is valid for currently-running platform.
    bool IsValidForCurrentPlatform() const
        { return (m_platform & GetCurrentPlatform()) != 0; }

    //! Returns true if this condition is true for given compiler.
    bool IsValidForCompiler(wxPackageCompilerFormat fmt) const
        { return (m_format & fmt) != 0; }

    //! Returns true if this condition is true for given build system.
    bool IsValidForBuildSys(wxPackageBuildSystemType fmt) const
        { return (m_buildSys & fmt) != 0; }

    //! Returns true if this condition is true for the given stage.
    bool IsValidForStage(wxPackageBuildSystemStage stage) const
        { return (m_stages & stage) != 0; }

    //! Returns true if this condition is true for the given set of stages
    //! (union through ORs of wxPackageBuildSystemStage values).
    bool IsValidForStages(long stages) const
        { return (m_stages & stages) != 0; }

    //! Returns true if this condition is true for the given set of compilers
    //! (union through ORs of wxPackageCompilerFormat values).
    bool IsValidForCompilers(long fmt) const
        { return (m_format & fmt) != 0; }

    //! Returns true if this condition is true for the current configuration
    //! of the given package.
    bool IsValidForPackage(const wxPackage &pkg) const;

    //! Resets this condition so that it's always verified.
    void SetAlwaysTrue();

    //! Returns true if this condition is always true.
    bool IsAlwaysTrue() const
    {
        // m_platform check was disabled ----- WHY ??
        return m_platform == wxGetFullPortIdList() &&
                m_format == wxGetFullPackageCompilerFormatList() &&
                m_buildSys == wxGetFullPackageBuildSystemTypeList() &&
                m_stages == wxGetFullPackageBuildSystemStageList();
    }

    //! Returns true if this condition is always false.
    bool IsAlwaysFalse() const
    {
        return m_platform == 0 &&
                m_format == 0 &&
                m_buildSys == 0 &&
                m_stages == 0;
    }

    //! Returns true if this condition and the given one have at least
    //! 1 platform, 1 format, 1 build system and 1 stage in common.
    bool Matches(const wxPackageCondition &cond) const
    {
        return (m_format & cond.m_format) != 0 &&
               (m_platform & cond.m_platform) != 0 &&
               (m_buildSys & cond.m_buildSys) != 0 &&
               (m_stages & cond.m_stages) != 0;
    }

    //! Removes from *this all compiler formats, all platforms, all
    //! build systems and all stages which are NOT matched by the given
    //! package condition.
    //! Note that *this will be in general different from 'cond' after
    //! using this function (since it does _not_ just copy the cond's fields!).
    void IntersecateWith(const wxPackageCondition &cond)
    {
        m_format &= cond.m_format;
        m_platform &= cond.m_platform;
        m_buildSys &= cond.m_buildSys;
        m_stages &= cond.m_stages;
    }

    // being OK for a package condition means being not always false:
    bool IsOk() const
        { return !IsAlwaysFalse(); }

protected:

    //! A combined list of the wxPortID(s) to which 
    //! the owner of this condition applies.
    //! If zero it applies to all ports.
    //! NOTE: this info is saved by load functions to allow a perfect save later.
    long m_platform;

    //! A combined list of the wxPackageCompilerFormat(s) to which
    //! the owner of this condition applies.
    //! If zero it applies to all formats.
    long m_format;

    //! A combined list of the wxPackageBuildSystemType(s) to which
    //! the owner of this condition applies.
    //! If zero it applies to all build systems.
    long m_buildSys;

    //! A combined list of the wxPackageBuildSystemStage(s) to which
    //! the owner of this condition applies.
    //! If zero it applies to all stages.
    long m_stages;
};



// ----------------------------------------------------------------------------
// wxPackageHTMLOutputFormat
// ----------------------------------------------------------------------------

//! This class is able to provide a string with formatted HTML for a
//! wxPackageInfo class.
class wxPackageHTMLOutputFormat
{
    friend class wxPackageHTMLOutputFormatXMLDescriptor;

public:
    wxPackageHTMLOutputFormat(const wxString &name = wxEmptyString,
                              const wxString &desc = wxEmptyString,
                              const wxString &summary = wxEmptyString,
                              const wxString &details = wxEmptyString,
                              wxPackageSubstituteInfoContext ctx = wxPSIC_INVALID)
    {
        m_strName=name;
        m_strDescription=desc;
        m_strSummary=summary;
        m_strDetails=details;
        m_ctx=ctx;
    }

    bool operator == (const wxPackageHTMLOutputFormat &fmt) const
    {
        return m_strSummary == fmt.m_strSummary &&
               m_strDetails == fmt.m_strDetails &&
               m_strName == fmt.m_strName &&
               m_strDescription == fmt.m_strDescription &&
               m_ctx == fmt.m_ctx;
    }

    bool operator != (const wxPackageHTMLOutputFormat &fmt) const
        { return !(*this == fmt); }

public:     // getters

    wxString GetName() const
        { return m_strName; }
    wxString GetDescription() const
        { return m_strDescription; }

    wxString GetSummary() const
        { return m_strSummary; }
    wxString GetDetails() const
        { return m_strDetails; }

    wxPackageSubstituteInfoContext GetContext() const
        { return m_ctx; }
    wxString GetContextStr() const
        { return wxPackageSubstituteInfoContext2String(m_ctx); }

    wxString GetSummaryFor(const wxPackageInfo &pkg) const;
    wxString GetDetailsFor(const wxPackageInfo &pkg) const;

public:

    bool IsOk() const
    {
        return !m_strSummary.IsEmpty() &&
               !m_strDetails.IsEmpty();
    }

    bool Load(const wxString &dirname);

protected:

    // info about this view format:
    wxString m_strName, m_strDescription;
    wxPackageSubstituteInfoContext m_ctx;

    // the templates for generating the HTML output:
    wxString m_strSummary, m_strDetails;
};

// the hashmap for storing view formats
WX_DECLARE_STRING_HASH_MAP( wxPackageHTMLOutputFormat,
                            wxPackageHTMLOutputFormatHashMap );


#endif      // _PACKAGEUTILS_H_

