/////////////////////////////////////////////////////////////////////////////
// Name:        packagedep.cpp
// Purpose:     wxPackageDependency, wxPackageDependencyArray
// Author:      Francesco Montorsi
// Modified by:
// Created:     2006-06-29
// RCS-ID:      $Id: packagedep.cpp,v 1.1.1.1 2006/12/12 09:39:35 frm Exp $
// Copyright:   (c) Francesco Montorsi
// 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 "wxp/packagedep.h"
#include "wxp/packagecmd.h"
#include "wxp/package.h"

// definitions

wxString wxPackageDependencyTypeName[] =
{
    _T("Required"),
    _T("Suggested"),
    _T("RequiredButNotPackaged")
};

// RTTI
IMPLEMENT_DYNAMIC_CLASS(wxPackageDependency, wxObject)



// wx array implementations
#include <wx/arrimpl.cpp> // this is a magic incantation which must be done!
WX_DEFINE_OBJARRAY(wxPackageDependencyArrayHelper);


wxPackageDependency wxEmptyPackageDependency;
wxPackageDependencyArray wxEmptyPackageDependencyArray;



// ----------------------------------------------------------------------------
// string2enum helpers
// ----------------------------------------------------------------------------

wxIMPLEMENT_STRING2ENUM(PackageDependencyType, wxPDT_MAX)



// ----------------------------------------------------------------------------
// wxPackageDependency
// ----------------------------------------------------------------------------

bool wxPackageDependency::IsSatisfiedBy(const wxPackageId &pkg) const
{
    // do this check regardless of the type of dependency represented
    // by this object: be it required, suggested or even required but
    // not packaged (in this last case it could still be successful
    // because e.g. the package which represents that dependency could
    // have been created since the creation of pkg)...

    if (m_strName.CmpNoCase(pkg.GetName()) != 0)
        return false;

    // check version
    if (!m_version.Contains(pkg.GetVersion()))
        return false;

    return true;
}

bool wxPackageDependency::IsSatisfiedByOneOf(const wxPackageArray &arr) const
{
    for (size_t i=0; i<arr.GetCount(); i++)
        if (IsSatisfiedBy(arr[i]))
            return true;
    return false;
}

int wxPackageDependency::GetBestMatch(const wxPackageArray &arr) const
{
    int best = wxNOT_FOUND;
    wxVersion bestdiff(9,9,9);

    for (size_t i=0; i < arr.GetCount(); i++)
    {
        if (IsSatisfiedBy(arr[i]) &&
            (best == wxNOT_FOUND || arr[i].GetVersion() > arr[best].GetVersion()))
        {
            // this package has a more recent version
            best = i;

            // FIXME: add a break here if the array is surely not
            // containing any duplicate package
        }
    }

    return best;
}

bool wxPackageDependency::DoGetRecursiveDependencies(wxPackageDependencyArray *ret, const wxPackageArray &arr,
        wxPackageDependencyType type, wxPackageDependencyArray *notfound, wxPackageIdArray *alreadyprocessed) const
{
    wxASSERT(type == wxPDT_INVALID || type == m_nType);

    int n = GetBestMatch(arr);
    if (n == wxNOT_FOUND)
    {
        if (notfound)
        {
            // do not add duplicates !
            if (!notfound->Contains(*this))
                notfound->Add(*this);
        }

        return false;
    }

    // NB: the check on alreadyprocessed is done by wxPackage::GetRecursiveDependency
    return arr[n].DoGetRecursiveDependencies(ret, arr, type, notfound, alreadyprocessed);
}



// ----------------------------------------------------------------------------
// wxPackageDependencyArray
// ----------------------------------------------------------------------------

wxPackageDependencyArray wxPackageDependencyArray::GetDependenciesOfType(wxPackageDependencyType t) const
{
    wxPackageDependencyArray ret;
    for (size_t i=0; i<GetCount(); i++)
        if (Item(i).GetType() == t)
            ret.Add(Item(i));
    return ret;
}

wxPackageDependencyArray wxPackageDependencyArray::GetDependenciesNotAvailableIn(const wxPackageArray &arr) const
{
    wxPackageDependencyArray ret(*this);
    for (size_t i=0; i<ret.GetCount(); i++)
    {
        if (ret[i].IsSatisfiedByOneOf(arr))
        {
            // this dependency can be satisfied using the given array of
            // packages... just remove it then
            ret.RemoveAt(i);
            i--;
        }
    }

    return ret;
}

wxPackageDependencyArray wxPackageDependencyArray::GetDependenciesAvailableIn(const wxPackageArray &arr) const
{
    wxPackageDependencyArray ret(*this);
    for (size_t i=0; i<ret.GetCount(); i++)
    {
        if (!ret[i].IsSatisfiedByOneOf(arr))
        {
            // this dependency cannot be satisfied using the given array of
            // packages... just remove it then
            ret.RemoveAt(i);
            i--;
        }
    }

    return ret;
}

wxPackageDependencyArray wxPackageDependencyArray::GetConflictingDependencies() const
{
    wxPackageDependencyArray ret;

    for (size_t i=0; i<GetCount(); i++)
    {
        for (size_t n=i; n<GetCount(); n++)
        {
            if (Item(i).IsPartiallySatisfiedBy(Item(n).GetName()))
            {
                // we have two dependencies with the same name...
                // let's check if it's possible to satisfy both of them
                // contemporaneously:

                if (Item(i).GetVersion().OverlapsWith(Item(n).GetVersion()))
                    continue;       // yes; these are not "conflicting"

                // if the two version ranges do not overlap, it's impossible
                // to satisfy both these dependencies thus they are conflicting!
                ret.Add(Item(i));
                ret.Add(Item(n));
            }
        }
    }

    return ret;
}

bool wxPackageDependencyArray::DoGetRecursiveDependencies(wxPackageDependencyArray *ret, const wxPackageArray &arr,
        wxPackageDependencyType type, wxPackageDependencyArray *notfound, wxPackageIdArray *alreadyprocessed) const
{
    bool success = true;
    for (size_t i=0; i<GetCount(); i++)
    {
        // expand recursively the dependencies for our i-th dependency
        if (type == wxPDT_INVALID || type == Item(i).GetType())
            success &= Item(i).DoGetRecursiveDependencies(ret, arr, type,
                                                          notfound, alreadyprocessed);
    }

    return success;
}

