/////////////////////////////////////////////////////////////////////////////
// Name:        package.h
// Purpose:     wxPackage, wxPackageArray
// Author:      Julian Smart, Francesco Montorsi, Daniel J. Lauk
// Modified by:
// Created:     2004-11-07
// RCS-ID:      $Id: package.h,v 1.13 2007/02/01 20:28:14 frm Exp $
// Copyright:   (c) Julian Smart, Francesco Montorsi, Daniel J. Lauk
// Licence:     wxWidgets licence
/////////////////////////////////////////////////////////////////////////////

#ifndef _PACKAGE_H_
#define _PACKAGE_H_

// Includes
#include "wxp/packageinfo.h"
#include "wx/md5.h"

class wxArchiveInputStream;

extern wxPackage wxEmptyPackage;
extern wxPackageArray wxEmptyPackageArray;


// ----------------------------------------------------------------------------
// wxPackageStatus
// ----------------------------------------------------------------------------

//! The status of a wxPackage as handled by the package manager.
//! All these states are sequentially assumed by a wxPackage.
enum wxPackageStatus
{
    wxPS_INVALID = -1,

    //! The package has not been downloaded yet.
    wxPS_REMOTE,

    //! The package has been downloaded; the wxPackage::GetDownloadPath()
    //! function returns a valid path to the folder where the WXZ is.
    wxPS_DOWNLOADED,

    //! The package has been decompressed; the wxPackage::GetDecompressionPath()
    //! function returns a valid path to the folder where the contents of the WXZ
    //! have been unarchived.
    //! This flag indicates if this package has been installed (and thus things
    //! like logo, screenshots, documentation can be found locally and does not
    //! need to be downloaded) or not.
    wxPS_DECOMPRESSED,

    //! The package has been built.
    wxPS_BUILT,

    //! The package has been installed.
    wxPS_INSTALLED,

    // the number of possible package states
    wxPS_MAX
};

wxDEFINE_STRING2ENUM(PackageStatus)



// ----------------------------------------------------------------------------
// wxProgressHandler
// ----------------------------------------------------------------------------

// A small interface class which acts as a callback:
// the OnUpdate() method is called when some progress is being done.
class wxProgressHandler
{
public:
    wxProgressHandler(wxObject *owner = NULL) { m_owner=owner; }
    virtual ~wxProgressHandler() {}

    // NOTE: the "progress" value is a value always comprised between
    //       zero and the maximum 1000.
    //       It's not the progress "delta" but the value of the progress
    //       (i.e. don't sum it with previous progress values).
    virtual bool OnUpdate(unsigned long progress, const wxString &text) = 0;

    wxObject *GetOwner() const
        { return m_owner; }

protected:
    wxObject *m_owner;
};



// ----------------------------------------------------------------------------
// wxPackage
// ----------------------------------------------------------------------------

//! A wxWidgets package. This class is separed from wxPackageInfo
//! just to make it clear which functions operates on the package info
//! and which do more complicated tasks.
//!
//! It does not support directly operations like 'build', 'install'
//! or 'uninstall' as such high-level functions would be useful for
//! a good GUI which needs much deeper control over these operations.
class wxPackage : public wxPackageInfo
{
    // required to get access to DoGetRecursiveDependency
    friend class wxPackageDependency;

    DECLARE_DYNAMIC_CLASS(wxPackage)

public:
    /* 30/12/2006 WARNING: don't know if the following wxPS_REMOVE => wxPS_INVALID
                           change won't break wxPM !! */

    wxPackage() : m_status(wxPS_INVALID) {}
    wxPackage(const wxPackageInfo &info, wxPackageStatus stat = wxPS_INVALID) 
        : m_status(stat)
        { *this = info; }
    virtual ~wxPackage() {}


    wxPackage& operator= (const wxPackageInfo& info)
        { *((wxPackageInfo*)this) = info; return *this; }

public:     // static

    //! The paths to the local repositories.
    static wxArrayString s_arrLocalRepo;

    //! The paths to the remote repositories.
    static wxArrayString s_arrRemoteRepo;

    // load/save

    //! Loads the paths of the local repositories from given wxConfig object & path.
    static bool LoadGlobals(wxConfigBase *, const wxString &path);

    //! Saves the local repositories paths to given wxConfig.
    static void SaveGlobals(wxConfigBase *, const wxString &path);

public:     // getters

    wxPackageStatus GetStatus() const
        { return m_status; }
    wxString GetStatusStr() const
        { return wxPackageStatus2String(m_status); }

    wxString GetDownloadPath() const
        { return m_strDownloadPath; }
    wxString GetDecompressionPath() const
        { return m_strDecompressionPath; }
    wxString GetInstallationPath() const
        { return m_strInstallationPath; }

    wxCompilerSettings &GetCompilerSettings()
        { return m_compiler; }
    wxCompilerSettings GetCompilerSettings() const
        { return m_compiler; }

public:     // WXP getters

    wxString GetRelativeWXPPath() const
        { return m_strWXPPath; }
    wxString GetAbsWXPPath() const
        { return m_strDecompressionPath + wxFileName::GetPathSeparator() +  m_strWXPPath; }

    //! Returns only the filename (and extension) of the WXP of this package.
    //! Valid only when status >= wxPS_DECOMPRESSED
    wxString GetWXPFileName() const
        { return wxFileName(m_strWXPPath).GetFullName(); }
/*
    //! Returns only the filename (and extension) of the WXZ of this package.
    wxString GetWXZFileName() const
    {
        if (m_status >= wxPS_DECOMPRESSED)
            // FIZME: this is very arbitrary: also the .tar.gz and .zip extensions could be used
            return wxFileName(m_strWXPPath).GetName() + wxT(".wxz");
        return wxFileSystem::URLToFileName(GetDownloadHref().Item(0)).GetFullName();
    }
*/
    //! Returns the size of the package once it is decompressed.
    //! This involves reading the while package's WXZ but not decompressing it.
    wxULongLong GetDecompressedSize() const;

public:     // setters

    void SetCompilerSettings(const wxCompilerSettings &sett)
        { m_compiler=sett; }

    void SetDecompressionPath(const wxString &path)
        { m_strDecompressionPath=path; }

public:     // miscellaneous

    bool IsOk() const;

    //! Returns the wxWidgets builds supported by this package.
    wxWidgetsBuildArray GetSupportedWxBuilds(bool docompilercheck = true) const;

    //! Returns *all* dependencies of this package recursively walking down the
    //! dependency tree.
    bool GetRecursiveDependencies(
                    wxPackageDependencyArray *ret,
                    const wxPackageArray &arr,
                    wxPackageDependencyType type,
                    wxPackageDependencyArray *notfound = NULL) const;

    //! Returns the list of the files matches by the inclusion criteria
    //! and not matches by the exclusion ones.
    //! If the argument is non-NULL then it's filled with the size of all
    //! the files to include.
    wxArrayString GetIncludedFiles(wxULongLong *totalsize = NULL) const;

    //! Loads the given WXZ and returns true on success.
    //! Sets the status of this package to wxPS_DOWNLOADED.
    bool LoadCompressedPackage(const wxString &wxzpath);

    //! Decompress this package in the given path.
    //! Since this can be a long operation, support for a user feedback
    //! is provided through the wxProgressHandler class.
    bool Decompress(const wxString &path, wxProgressHandler *handler = NULL);

    //! Compress this package in the given output file (if not empty) or
    //! in the file name specified by wxPackageInfo::GetFileName() if wxz is
    //! the empty string.
    //! Uses wxLog facilities to log errors / warnings.
    //! The created package will include only files listed by #GetIncludedFiles.
    //! Since this can be a long operation, support for a user feedback
    //! is provided through the wxProgressHandler class.
    bool Compress(const wxString &wxz = wxEmptyString, wxProgressHandler *handler = NULL);

    //! Uploads this package in the given path of the given server.
    //! If you provide empty strings for the various parameters they will
    //! be taken from the upload destination (see wxPackageInfo::GetUploadDestination).
    bool UploadWithFTP(const wxString &user = wxEmptyString, 
                       const wxString &password = wxEmptyString, 
                       const wxString &server = wxEmptyString, 
                       const wxString &path = wxEmptyString);

public:     // status

    //! Sets the package as remote (not downloaded yet).
    //! Optionally you can specify the path where the package is *being* downloaded.
    //! See #m_strDownloadPath documentation.
    void SetRemoteStatus(const wxString &downloadpath = wxEmptyString);

    //! Sets the package as downloaded in the given path.
    void SetDownloadedStatus(const wxString &downloadpath);

    //! Sets the package as decompressed in the given path.
    void SetDecompressedStatus(const wxString &decompressionpath);

    //! Sets the package as built.
    void SetBuiltStatus();

    //! Sets the package as installed in the given path.
    void SetInstalledStatus(const wxString &installpath);

    //! Sets the relative path to the WXP file for this package.
    //! See #m_strWXPPath for more info.
    void SetRelativeWXPPath(const wxString &path)
        { m_strWXPPath=path; }

    //! Updates the info associated with this package expanding local references
    //! if the package has been downloaded or removing them if the package is a
    //! remote package.
    void UpdatePackageInfo();

public:     // load & save

    bool LoadStatusInfo(wxConfigBase *cfg, const wxString &path);
    bool SaveStatusInfo(wxConfigBase *cfg, const wxString &path) const;

    //! Only valid when the status of this package is >= wxPS_DOWNLOADED;
    //! updates the compressed size of the package.
    bool UpdateCompressedSize()
    {
        wxASSERT(m_status >= wxPS_DOWNLOADED);

        if (m_strDownloadPath.IsEmpty())
        {
            // this happens when e.g. the package has the wxPS_DECOMPRESSED
            // status but it's a new package and thus the compressed package
            // does not exist (yet)!
            return false;
        }

        m_nCompressedSize = wxFileName(m_strDownloadPath).GetSize();
        if (m_nCompressedSize == wxInvalidSize)
            return false;
        return true;
    }

    //! Only valid when the status of this package is >= wxPS_DOWNLOADED;
    //! updates the MD5 string of the package.
    bool UpdateMD5()
    {
        wxASSERT(m_status >= wxPS_DOWNLOADED);

        if (m_strDownloadPath.IsEmpty())
        {
            // this happens when e.g. the package has the wxPS_DECOMPRESSED
            // status but it's a new package and thus the compressed package
            // does not exist (yet)!
            return false;
        }

        m_strMD5 = wxMD5::GetFileMD5(m_strDownloadPath);
        return !m_strMD5.IsEmpty();
    }

protected:         // wxPackageInfo overloads

    virtual wxPackageCommand GetCmd(const wxPackageCommand &cmd, 
                                    wxPackageBuildSystemStage stage,
                                    long flags,
                                    const wxPackageCondition &cond) const;

protected:      // internal utilities

    bool DoGetRecursiveDependencies(
                    wxPackageDependencyArray *ret,
                    const wxPackageArray &arr,
                    wxPackageDependencyType type,
                    wxPackageDependencyArray *notfound,
                    wxPackageIdArray *alreadyprocessed) const;

    //! Returns an input stream for the WXZ placed in 'wxzpath'
    wxArchiveInputStream *GetInputStream(const wxString &wxzpath) const;

    //! Adds to wxPackageInfo's substitution hashmaps a few keywords about the package status
    //! (mainly useful for writing the build system commands rather than for writing viewformats).
    virtual void DoUpdateSubstitutionHashMap(wxPackageSubstituteInfoContext ctx);


protected:          // miscellaneous

    //! The current status of this package.
    wxPackageStatus m_status;

    //! The compiler currently in use for this package.
    wxCompilerSettings m_compiler;

protected:          // paths

    //! When m_status is >= wxPS_DOWNLOADED: the path where the
    //! downloaded WXZ file has been placed.
    //! When m_status is wxPS_REMOTE: this is the path where the downloaded
    //! WXZ is going to be placed.
    //! This must be an absolute path fully qualified: path + name + extension.
    //! E.g. /home/me/packages/abc.wxz
    wxString m_strDownloadPath;

    //! Valid only when m_status is >= wxPS_DECOMPRESSED: the directory of
    //! the package containing the decompressed WXZ, without the name of the
    //! decompressed folder.
    //! This must be an absolute path.
    //! E.g.
    //!  supposing that abc.wxz has been decompressed in /home/me/packages
    //!  and that the package folder name is abc-1.0.0, a valid
    //!  decompression path is: /home/me/packages, not /home/me/packages/abc-1.0.0
    wxString m_strDecompressionPath;

    //! Valid only when m_status is >= wxPS_DECOMPRESSED: the path to the
    //! WXP file relative to the decompression folder.
    //! Thus, #m_strDecompressionPath + #m_strWXPPath == absolute location of the WXP.
    //! E.g. 
    //!  abc-1.0.0/build/abc.wxp (and in this case the relative root of the package
    //!                           should be "../../..")
    wxString m_strWXPPath;

    //! Valid only when m_status is >= wxPS_INSTALLED: the directory where
    //! the package has been installed.
    //! This must be an absolute path.
    //! E.g. /usr/local
    wxString m_strInstallationPath;
};

// the array
WX_DECLARE_OBJARRAY(wxPackage, wxPackageArrayHelper);

// a light array of wxPackages
WX_DEFINE_ARRAY(const wxPackage *, wxPackagePtrArray);





// ----------------------------------------------------------------------------
// wxPackageArray
// ----------------------------------------------------------------------------

class wxPackageArray : public wxPackageArrayHelper
{
public:
    wxPackageArray() {}

public:     // miscellaneous

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

    //! Returns the index of the first package with the given ID or wxNOT_FOUND.
    int IndexById(const wxPackageId &id) const
    {
        for (size_t i=0; i<GetCount(); i++)
            if ((const wxPackageId &)Item(i) == id)
                return i;
        return wxNOT_FOUND;
    }

    wxPackage &ItemById(const wxPackageId &id)
    {
        int n = IndexById(id);
        if (n != wxNOT_FOUND)
            return wxPackageArrayHelper::Item(n);
        return wxEmptyPackage;
    }

public:         // misc

    // Removes duplicate packages and returns the number of removed packages.
    size_t RemoveDuplicates();

    //! Finds a package with the same name and version (ID) of the given package
    //! and then replaces it with the given instance.
    //! Returns false if no matching package IDs are found in this array.
    bool UpdatePackage(const wxPackage &pkg);

    //! Creates an array of all depencies of the packages in this array,
    //! removing duplicate dependencies.
    //! Note that the array returned contains only the
    bool GetRecursiveDependencies(
                    wxPackageDependencyArray *ret,
                    const wxPackageArray &arr,
                    wxPackageDependencyType type,
                    wxPackageDependencyArray *notfound = NULL) const;

    //! Returns the packages whose primary or secondary category is of the given type
    wxPackageArray GetPackagesWhichBelongTo(wxPackageCategory cat) const;

    //! Calls wxPackage::InvalidateCachedSubstHashMap on all the packages of the array.
    void InvalidateCachedSubstHashMap(wxPackageSubstituteInfoContext ctx = wxPSIC_INVALID);

public:         // load functions

    //! Calls #LoadCompressedPackage on all WXZ found in the given directory.
    //! NB: old content of the array is preserved.
    bool LoadCompressedPackagesFrom(const wxString &path);

    //! Calls #LoadCompressedPackage on all WXZ found in the local repository
    //! paths (see wxPackage::s_arrLocalRepo).
    //! NB: old content of the array is preserved.
    bool LoadCompressedPackagesFromLocalRepo();

public:     // status load & save

    //! Calls wxPackage::LoadStatusInfo for all packages.
    bool LoadPackagesStatusInfo(wxConfigBase *, const wxString &);

    //! Calls wxPackage::SaveStatusInfo for all packages.
    bool SavePackagesStatusInfo(wxConfigBase *, const wxString &) const;
};

#endif      // _PACKAGE_H_

