/////////////////////////////////////////////////////////////////////////////
// Name:        packageopt.h
// Purpose:     wxPackageCommandOption, wxPackageCommandOptionArray
// Author:      Francesco Montorsi
// Modified by:
// Created:     2006-1-07
// RCS-ID:      $Id: packageopt.h,v 1.5 2007/01/28 15:56:11 frm Exp $
// Copyright:   (c) Julian Smart, Francesco Montorsi, Daniel J. Lauk
// Licence:     wxWidgets licence
/////////////////////////////////////////////////////////////////////////////

#ifndef _PACKAGEOPT_H_
#define _PACKAGEOPT_H_

// Includes
#include "wx/tokenzr.h"
#include "wxp/wxputils.h"
#include "wxp/compiler.h"
#include "wxp/packageutils.h"

// Forward declarations
class wxPackageCommandOption;
class wxPackageCommandOptionArray;
class wxPackageCommandArray;
class wxPackageCondition;
class wxPackage;



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

//! The possible types of wxPackageCommandOptions...
enum wxPackageCommandOptionCategory
{
    wxPCOC_INVALID = -1,

    wxPCOC_LISTED_VALUES,       //!< Only a finite set of values are allowed.
    wxPCOC_DIRECTORY,           //!< Any directory is allowed.
    wxPCOC_FILE,                //!< Any filename is allowed.
    wxPCOC_STRING,              //!< Any string is allowed.

    wxPCOC_MAX
};

extern wxPackageCommandOption wxEmptyPackageCommandOption;
extern wxPackageCommandOptionArray wxEmptyPackageCommandOptionArray;
extern wxPackageCommandArray wxEmptyPackageCommandArray;

wxDEFINE_STRING2ENUM(PackageCommandOptionCategory)



// ----------------------------------------------------------------------------
// wxPackageCommandOption
// ----------------------------------------------------------------------------

//! A generic option for the build system of a wxPackage.
//! The options are loaded, shown to the user for editing and then passed
//! to the build system.
class wxPackageCommandOption: public wxObject
{
    DECLARE_DYNAMIC_CLASS(wxPackageCommandOption)

public:

    //! Constructor for option with listed values.
    wxPackageCommandOption(
             const wxString &name = wxEmptyString,
             const wxString &allowedvalues = wxEmptyString,
             const wxString &value = wxEmptyString,
             const wxString &def = wxEmptyString,
             const wxString &valuesdesc = wxEmptyString,
             const wxString &desc = wxEmptyString,
             const wxString &grp = wxEmptyString,
             wxPackageCommandOptionCategory cat = wxPCOC_LISTED_VALUES,
             const wxPackageCondition &cnd = wxEmptyPackageCondition)
             : m_strName(name),
               m_strAllowedValues(allowedvalues),
               m_strDefValue(def), m_strValue(value), m_strDesc(desc),
               m_strGroup(grp), m_strValuesDesc(valuesdesc),
               m_nCat(cat), m_cond(cnd) {}

    //! Constructor for boolean option.
    wxPackageCommandOption(
             const wxString &name,
             bool value,
             bool def = true,
             const wxString &desc = wxEmptyString,
             const wxString &grp = wxEmptyString,
             const wxPackageCondition &cnd = wxEmptyPackageCondition)
             : m_strName(name),
               m_strAllowedValues(wxT("1,0")),
               m_strDefValue(def ? wxT("1") : wxT("0")),
               m_strValue(value ? wxT("1") : wxT("0")),
               m_strDesc(desc),
               m_strGroup(grp),
               m_nCat(wxPCOC_LISTED_VALUES), m_cond(cnd) {}

    virtual ~wxPackageCommandOption() {}

public:     // miscellaneous

    //! Tries to detect the category of this option using some heuristic approaches.
    //! If all checks fail, it sets the category to wxPCOC_STRING.
    void AutodetectCategory();

    //! Sets as current value the default value.
    void ResetToDefault()
        { m_strValue = m_strDefValue; }

    bool operator==(const wxPackageCommandOption &opt) const;
    bool operator!=(const wxPackageCommandOption &opt) const
        { return !(*this == opt); }

public:     // getters

    wxString GetName() const
        { return m_strName; }
    wxString GetAllowedValues() const
        { return m_strAllowedValues; }
    wxString GetDefaultValue() const
        { return m_strDefValue; }
    wxString GetValue() const
        { return m_strValue; }
    wxString GetValueDesc() const
        { return m_strValuesDesc; }

    wxString GetDescription() const
        { return m_strDesc; }
    wxString GetGroup() const
        { return m_strGroup; }

    wxPackageCommandOptionCategory GetCategory() const
        { return m_nCat; }
    wxString GetCategoryStr() const
        { return wxPackageCommandOptionCategory2String(m_nCat); }

    bool GetBooleanValue() const
    {
        wxASSERT(IsBoolean());
        return GetValue() == wxT("1");
    }

public:     // wxArrayString-friendly getters

    wxArrayString GetAllowedValuesArray() const
        { return wxString2Array(m_strAllowedValues); }
    wxArrayString GetAllowedValuesDescArray() const
        { return wxString2Array(m_strValuesDesc); }
    size_t GetDefaultValueIndex() const
        { return GetAllowedValuesArray().Index(GetDefaultValue()); }
    size_t GetValueIndex() const
        { return GetAllowedValuesArray().Index(GetValue()); }

    const wxPackageCondition &GetCondition() const
        { return m_cond; }
    wxPackageCondition &GetCondition()
        { return m_cond; }

public:     // setters

    void SetValue(const wxString &value)
        { m_strValue=value; }
    void SetDefaultValue(const wxString &value)
        { m_strDefValue=value; }

    void SetCondition(const wxPackageCondition &c)
        { m_cond=c; }

public:     // checkers

    //! Returns true if this option is of category wxPCOC_LISTED_VALUES
    //! and has only '0' and '1' as allowed values.
    bool IsBoolean() const
        { return m_strAllowedValues == wxT("0,1") ||
                 m_strAllowedValues == wxT("1,0"); }

    //! Returns true if this option has been correctly initialized.
    bool IsOk() const;

    //! Returns true if the current value is not the default value.
    bool IsChanged() const
        { return m_strValue != m_strDefValue; }

protected:
    wxString m_strName, m_strAllowedValues, m_strDefValue, m_strValue;
    wxString m_strDesc, m_strGroup, m_strValuesDesc;

    //! The category of this option: listed-value, directory, file, etc.
    wxPackageCommandOptionCategory m_nCat;

    //! The condition for this option.
    wxPackageCondition m_cond;
};

// the array
WX_DECLARE_OBJARRAY(wxPackageCommandOption, wxPackageCommandOptionArrayHelper);




// ----------------------------------------------------------------------------
// wxPackageCommandOptionArray
// ----------------------------------------------------------------------------

//! A list of options for one or more wxPackageCommand(s).
class wxPackageCommandOptionArray : public wxPackageCommandOptionArrayHelper
{
public:

    //! Default constructor does nothing.
    wxPackageCommandOptionArray() { m_bDirty=false; }
    wxPackageCommandOptionArray(const wxPackageCommandOptionArray &arr)
        : wxPackageCommandOptionArrayHelper(arr), m_bDirty(arr.m_bDirty) {}

    wxPackageCommandOptionArray &operator=(const wxPackageCommandOptionArray &arr)
    {
        *((wxPackageCommandOptionArrayHelper*)this)=arr;
        m_bDirty=arr.m_bDirty;
        m_strDesc=arr.m_strDesc;
        return *this;
    }

public:     // miscellaneous

    // define & implement IsOk(), IndexByName(), ItemByName(), Contains()
    // and ContainsItemNamed() functions
    WX_DECLARE_STD_OBJARRAY_HELPERS(wxPackageCommandOption, 
                                    wxEmptyPackageCommandOption);

    //! Returns the index of the first option with the given group name or wxNOT_FOUND.
    int IndexByGroup(const wxString &grpname) const
    {
        for (size_t i=0; i<GetCount(); i++)
            if (Item(i).GetGroup() == grpname)
                return i;
        return wxNOT_FOUND;
    }

    bool Add(const wxPackageCommandOption &opt, size_t copies = 1)
    {
        if (IndexByName(opt.GetName()) == wxNOT_FOUND)
        {
            wxPackageCommandOptionArrayHelper::Add(opt, copies);
            return true;
        }

        wxLogWarning(wxT("Discarding duplicate option named '%s'"),
                     opt.GetName().c_str());
        return false;
    }

    //! Resets all options to their default values
    void ResetToDefaults()
    {
        for (size_t i=0; i<GetCount(); i++)
            Item(i).ResetToDefault();
    }

    //! Finds an option with the same name of the given option
    //! and then replaces it with the given instance.
    //! Returns false if no matching options are found in this array.
    bool UpdateOption(const wxPackageCommandOption &opt);

    //! Returns true if the given array is a subset (or coincides) with this array.
    bool Contains(const wxPackageCommandOptionArray &arr) const;

    //! Removes all options of the given array from this array (if found).
    //! Returns true if all options have been found and removed, false otherwise.
    bool Remove(const wxPackageCommandOptionArray &arr);


public:     // getters

    wxPackageCommandOption &ItemByGroup(const wxString &grpname)
    {
        int n = IndexByGroup(grpname);
        if (n != wxNOT_FOUND)
            return wxPackageCommandOptionArrayHelper::Item(n);
        return wxEmptyPackageCommandOption;
    }

    //! Returns the groups of all options.
    wxArrayString GetAllOptionGroups() const;

    //! Returns all the possible groups for the options
    //! associated with the given (set of) wxPackageBuildSystemStage.
    wxArrayString GetOptionGroups(long stages = wxGetFullPackageBuildSystemStageList()) const;

    //! Returns only the options with values != their default values.
    wxPackageCommandOptionArray GetChangedOptions() const;

    //! Returns all options separed by a single space in the form optionname=optionvalue.
    wxString GetAsList() const;

    //! Returns the description for this array of options, if any.
    wxString GetDescription() const
        { return m_strDesc; }

    //! Returns the conditions common to all options of this array.
    wxPackageCondition GetCommonCondition() const;

public:     // setters

    void SetDescription(const wxString &str)
        { m_strDesc=str; }

public:     // substitution helpers

    //! Returns the hashmap for the substitution of the $(optionname) keywords
    //! with the relative option values.
    wxStringHashMap GetSubstitutionOptionsHashMap(long flags, 
                const wxPackageCondition &cond = wxEmptyPackageCondition) const;

    //! Does a chain of #SubstituteOptions and #SubstituteStandard.
    wxString SubstituteOptions(const wxString &str, long flags,
                               const wxPackageCondition &cond = wxEmptyPackageCondition) const
        { return wxDoStringSubstitution(str, 
                    GetSubstitutionOptionsHashMap(flags, cond)); }

public:     // dirty status

    void SetDirty(bool d = true)
        { m_bDirty = d; }
    bool IsDirty() const
        { return m_bDirty; }

protected:      // internal helpers

    wxStringHashMap GetGroupNamesHashMap(long flags, const wxPackageCondition &cond) const;
    wxStringHashMap GetTypeNamesHashMap(long flags, const wxPackageCondition &cond) const;

protected:
    // have options been changed?
    bool m_bDirty;

    // description of this array
    wxString m_strDesc;
};


#endif      // _PACKAGEOPT_H_

