/////////////////////////////////////////////////////////////////////////////
// Name:        packagewxp.h
// Purpose:     wxPackageXMLDescriptor
// Author:      Francesco Montorsi
// Modified by:
// Created:     2006-06-29
// RCS-ID:      $Id: packagewxp.h,v 1.8 2007/01/23 08:45:55 frm Exp $
// Copyright:   (c) Francesco Montorsi
// Licence:     wxWidgets licence
/////////////////////////////////////////////////////////////////////////////

#ifndef _PACKAGE_WXP_H_
#define _PACKAGE_WXP_H_

// Includes
#include <wx/xml/xml.h>
#include <wx/wfstream.h>
#include <wx/dynarray.h>

#include "wxp/wxp.h"

class wxArray;
class wxPackageXMLDescriptor;
extern wxPackageXMLDescriptor wxEmptyPackageXMLDescriptor;



// ----------------------------------------------------------------------------
// wxPackageXMLDescriptor
// ----------------------------------------------------------------------------

// The version of the WXP XML files recognized by the applications:
#define WXP_VERSION_NUMBER          1.1
#define WXP_VERSION_STRING          wxT("1.1")

//! An XML document which describes a wxPackage.
//! This document usually has the .WXP extension.
class wxPackageXMLDescriptor : public wxPackageXML
{
    DECLARE_DYNAMIC_CLASS(wxPackageXMLDescriptor)

public:
    wxPackageXMLDescriptor() { Reset(); }
    virtual ~wxPackageXMLDescriptor() {}

    // reset all infos
    void Reset()
    {
        m_pBasics = m_pLicensing = m_pDetails = m_pDep = m_pBuildSystem = m_pPackaging = NULL;
        m_pId = m_pDescription = m_pCategory = m_pKeywords = NULL;
        m_pWebsite = m_pCreationDate = NULL;
        m_pLicense = m_pOrganization = m_pCost = m_pCredits = NULL;
        m_pCopyright = m_pAuthors = m_pMaintainers = NULL;
        m_pStatus = m_pProgLanguage = m_pSamples = NULL;
        m_pDownloads = m_pLogo = m_pRSS = m_pScreenshots = m_pDocs = NULL;
        m_pSupportedPorts = m_pTestedPorts = NULL;
        m_pFormats = m_pOptions = m_pStages = NULL;
        m_pRoot = m_pContents = m_pFileName = m_pUpload = m_pCompress = NULL;

        for (size_t i=0; i<wxPDT_MAX; i++)
            m_pDependencies[i] = NULL;

        m_nIndentationStep = 4;
    }

public:         // getters

    //! Returns the wxPackageInfo parsed from this XML document.
    const wxPackageInfo &GetPackageInfo() const
        { return m_info; }

public:         // setters

    //! Set up the XML tree of this document with the info taken from the given
    //! package. This function will create all missing nodes.
    void SetPackageInfo(const wxPackageInfo &);

public:         // load & save

    // NB: all wxPackageXMLDescriptor API is written with the assumption that
    //     white spaces are not removed during loading. So, just make it impossible
    //     to load the document removing them:

    bool Load(const wxString &filename)
    { 
        wxFileInputStream stream(filename);
        if (!stream.Ok())
            return false;
        return Load(stream);
    }

    bool Load(wxInputStream &stream)
    {
        SetRoot(NULL);
        Reset();

        if (!wxPackageXML::Load(stream, wxT("UTF-8"), wxXMLDOC_KEEP_WHITESPACE_NODES))
            return false;
        return Parse();
    }

    // does the real parsing of the XML
    bool Parse();

    bool Save(const wxString &filename) const
        { return wxPackageXML::Save(filename, -1); }
    bool Save(wxOutputStream &stream) const
        { return wxPackageXML::Save(stream, -1); }

    wxPackageXMLDescriptor& operator=(const wxPackageXMLDescriptor& doc)
    {
        Reset();

        // use wxXmlDocument's operator=
        ((wxXmlDocument&)(*this)) = (wxXmlDocument&)doc;

        // now update all our internal pointers
        m_pBasics = FindNode(wxT("basics"));
        m_pLicensing = FindNode(wxT("licensing"));
        m_pDetails = FindNode(wxT("details"));
        m_pDep = FindNode(wxT("dependencies"));
        m_pBuildSystem = FindNode(wxT("build-system"));
        m_pPackaging = FindNode(wxT("packaging"));

        // BASIC nodes
        m_pId = FindNode(m_pBasics, wxT("id"));
        m_pDescription = FindNode(m_pBasics, wxT("description"));
        m_pCategory = FindNode(m_pBasics, wxT("category"));
        m_pKeywords = FindNode(m_pBasics, wxT("keywords"));
        m_pWebsite = FindNode(m_pBasics, wxT("website"));
        m_pCreationDate = FindNode(m_pBasics, wxT("creation-date"));

        // LICENSING
        m_pLicense = FindNode(m_pLicensing, wxT("license"));
        m_pOrganization = FindNode(m_pLicensing, wxT("organization"));
        m_pCost = FindNode(m_pLicensing, wxT("cost"));
        m_pCredits = FindNode(m_pLicensing, wxT("credits"));
        m_pCopyright = FindNode(m_pLicensing, wxT("copyright"));
        m_pAuthors = FindNode(m_pLicensing, wxT("authors"));
        m_pMaintainers = FindNode(m_pLicensing, wxT("maintainers"));

        // PACKAGING
        m_pRoot = FindNode(m_pPackaging, wxT("root"));
        m_pFileName = FindNode(m_pPackaging, wxT("filename"));
        m_pContents = FindNode(m_pPackaging, wxT("contents"));
        m_pUpload = FindNode(m_pPackaging, wxT("upload"));
        m_pCompress = FindNode(m_pPackaging, wxT("compress"));

        // DETAILS
        m_pStatus = FindNode(m_pDetails, wxT("status"));
        m_pProgLanguage = FindNode(m_pDetails, wxT("proglanguage"));
        m_pSamples = FindNode(m_pDetails, wxT("samples"));
        m_pDownloads = FindNode(m_pDetails, wxT("download"));
        m_pLogo = FindNode(m_pDetails, wxT("logo"));
        m_pRSS = FindNode(m_pDetails, wxT("rss"));
        m_pScreenshots = FindNode(m_pDetails, wxT("screenshots"));
        m_pDocs = FindNode(m_pDetails, wxT("documentation"));

        // DEPENDENCIES
        m_pSupportedPorts = FindNode(m_pDep, wxT("supported-ports"));
        m_pTestedPorts = FindNode(m_pDep, wxT("tested-ports"));
        m_pDependencies[wxPDT_REQUIRED] = 
            FindNode(m_pDep, wxT("dependency"), wxT("type"), wxT("required"));
        m_pDependencies[wxPDT_SUGGESTED] = 
            FindNode(m_pDep, wxT("dependency"), wxT("type"), wxT("suggested"));

        // BUILD SYSTEM
        m_pFormats = FindNode(m_pBuildSystem, wxT("formats"));
        m_pOptions = FindNode(m_pBuildSystem, wxT("options"));
        m_pStages = FindNode(m_pBuildSystem, wxT("stages"));

        return *this;
    }

    wxXmlNode *FindNode(const wxString &name,
                        const wxString &propname = wxEmptyString,
                        const wxString &propval = wxEmptyString) const
    {
        if (!GetRoot())
            return NULL;

        return DoFindNode(GetRoot(), name, propname, propval);
    }

    wxXmlNode *FindNode(wxXmlNode *node,
                        const wxString &name,
                        const wxString &propname = wxEmptyString,
                        const wxString &propval = wxEmptyString) const
    {
        if (!node)
            return NULL;

        return DoFindNode(node, name, propname, propval);
    }

protected:      // load helpers

    wxString GetHrefProp(const wxXmlNode *p) const;
    wxString GetLocalRefProp(const wxXmlNode *p) const;

    bool LoadBasics(const wxXmlNode *n, wxPackageInfo &);
    bool LoadPackaging(const wxXmlNode *n, wxPackageInfo &);
    bool LoadDetails(const wxXmlNode *n, wxPackageInfo &);
    bool LoadLicensing(const wxXmlNode *n, wxPackageInfo &);
    bool LoadDependencies(const wxXmlNode *n, wxPackageInfo &);
    bool LoadBuildSystem(const wxXmlNode *n, wxPackageInfo &);

    // LoadBuildSystem helpers
    bool LoadStages(const wxXmlNode *n, wxPackageInfo &p);

    // LoadDependencies helper
    bool LoadWxportTags(const wxXmlNode *port, const wxVersionRange &str,
                        wxArrayInt *ports, wxVersionRangeArray *ver);

    // LoadPackaging helper

    bool LoadContentsTag(const wxXmlNode *tag,
                         wxArrayString *dirs,
                         wxArrayString *files,
                         wxArrayString *patterns);

    // misc helpers

    // this should really go in wxXmlNode class...
    wxXmlNode *DoFindNode(wxXmlNode *node, const wxString &name,
                          const wxString &propname, const wxString &propval) const
    {
        if (node->GetName() == name)
        {
            // need to check also for a property?
            if (propname.IsEmpty() ||
                node->GetPropVal(propname, wxEmptyString) == propval)
            {
                // name matches and also the property matches (if required)
                return node;
            }
        }

        wxXmlNode *child = node->GetChildren();
        while (child) 
        {
            wxXmlNode *ret = DoFindNode(child, name, propname, propval);
            if (ret)
                return ret;     // found!

            child = child->GetNext();
        }

        // not found
        return NULL;
    }

protected:      // save helpers

    // the callback function type used by ResetEnumeration
    typedef void (*FormatNodeFnc)(wxXmlNode *n, size_t i, 
                                  const wxPackageInfo &userdata);

    void ResetEnumeration(wxXmlNode *parent, int parentIndentLevel,
                          const wxString &nodename, size_t max,
                          FormatNodeFnc f, const wxPackageInfo &pkg,
                          bool eraseChildren = true);

    void CreateSiblingIfMissing(wxXmlNode *prev, wxXmlNode **node, 
                                const wxString &name);

    void CreateChildIfMissing(wxXmlNode *parent, wxXmlNode *prev,
                              wxXmlNode **node, const wxString &name,
                              int level);

    void CreateMissingNodes();

    // sets the first CDATA or text node contents of the given node to given text
    void SetNodeContent(wxXmlNode *node, const wxString &text);

    // adds "href" and "localref" properties to the given node and init them
    // with the given wxPackageDataWithRef object
    void SetRefProp(wxXmlNode *node, const wxPackageDataWithRef &ref);

    // removes recursively all properties named "name" from the given node;
    // if "name" is empty all children are removed.
    void RemoveAllPropertiesFrom(wxXmlNode *node,
                                 const wxString &name = wxEmptyString);

    // removes recursively all children named "name" from given node;
    // if "name" is empty all children are removed.
    void RemoveAllChildrenFrom(wxXmlNode *node, 
                               const wxString &name = wxEmptyString);

protected:      // pointers to the nodes

    // when modifying these remember to update operator= and Reset() functions

    // main section nodes
    wxXmlNode *m_pBasics, *m_pLicensing, *m_pDetails, *m_pDep, *m_pBuildSystem,
              *m_pPackaging;

    // BASIC xml nodes
    wxXmlNode *m_pId, *m_pDescription, *m_pCategory, *m_pKeywords, *m_pWebsite,
              *m_pCreationDate;

    // PACKAGING xml nodes
    wxXmlNode *m_pRoot, *m_pFileName, *m_pContents, *m_pUpload, *m_pCompress;

    // LICENSING xml nodes
    wxXmlNode *m_pLicense, *m_pOrganization, *m_pCost, *m_pCredits, *m_pCopyright,
              *m_pAuthors, *m_pMaintainers;

    // DETAILS xml nodes
    wxXmlNode *m_pStatus, *m_pProgLanguage, *m_pSamples, *m_pDownloads,
              *m_pLogo, *m_pRSS, *m_pScreenshots, *m_pDocs;

    // DEPENDENCIES xml nodes
    wxXmlNode *m_pSupportedPorts, *m_pTestedPorts, *m_pDependencies[wxPDT_MAX];

    // BUILD SYSTEM xml nodes
    wxXmlNode *m_pFormats, *m_pOptions, *m_pStages;

private:

    // the class containing all info for this package:
    wxPackageInfo m_info;
};



#endif      // _PACKAGE_WXP_H_

