/////////////////////////////////////////////////////////////////////////////
// Name:        packagecmd.cpp
// Purpose:     wxPackageCommand, wxPackageCommandArray, wxPackageCommandSet
// Author:      Francesco Montorsi
// Modified by:
// Created:     2006-11-07
// RCS-ID:      $Id: packagecmd.cpp,v 1.4 2007/01/28 15:56:11 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 <wx/filename.h>
#include <wx/process.h>

#include "wxp/packagecmd.h"



// RTTI
IMPLEMENT_DYNAMIC_CLASS(wxPackageCommand, wxObject)


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


// global objects
wxPackageCommand wxEmptyPackageCommand;
wxPackageCommandArray wxEmptyPackageCommandArray;



// ----------------------------------------------------------------------------
// wxPackageCommand
// ----------------------------------------------------------------------------

wxString wxPackageCommand::GetProgram() const
{
    // NB: do not access m_strCmd directly but rather use GetCommand()
    //     which performs substitutions inside it
    wxString cmd = GetCommand();

#ifdef __WXMSW__
    size_t n = cmd.Find(wxT(".exe"));
    cmd = cmd.Left(n == wxNOT_FOUND ? 0 : n+4);
#else
    cmd = cmd.BeforeFirst(wxT(' '));
#endif

    return cmd;
}

wxString wxPackageCommand::GetProgramWithPath(const wxCompilerSettings &sett) const
{
    return sett.GetToolPath(GetProgram());
}

wxString wxPackageCommand::GetProgramArguments() const
{
    // NB: do not access m_strCmd directly but rather use GetCommand()
    //     which performs substitutions inside it
    wxString cmd = GetCommand();
#ifdef __WXMSW__
    size_t n = cmd.Find(wxT(".exe"));
    return cmd.Mid(n == wxNOT_FOUND ? 0 : n+4);
#else
    return cmd.AfterFirst(wxT(' '));
#endif
}

void wxPackageCommand::UpdateWorkingDirectory(const wxString &decompressionpath) const
{
    // set our current directory (previous commands could have changed it)
    // NB: the + wxT("/") is important to force wxFileName to consider the
    //     first argument a directory path
    wxFileName path(GetWorkingPath() + wxT("/"), wxPATH_UNIX);

    if (path.MakeAbsolute(decompressionpath) && path.DirExists())
        wxSetWorkingDirectory(path.GetFullPath());
    else
        wxSetWorkingDirectory(decompressionpath);
}

long wxPackageCommand::Run(wxProcess *proc) const
{
    if (proc) proc->Redirect();

    // NB: do not access m_strCmd directly but rather use GetCommand()
    //     which performs substitutions inside it
    wxString cmd = GetCommand();
    wxLogDebug(wxT("Launching command '%s' in dir '%s'"), 
               cmd.c_str(), wxGetCwd().c_str());

    // launch it !
    return wxExecute(cmd, wxEXEC_ASYNC|wxEXEC_MAKE_GROUP_LEADER, proc);
}


// ----------------------------------------------------------------------------
// wxPackageCommandArray
// ----------------------------------------------------------------------------

wxPackageCommand wxPackageCommandArray::GetSubstitutedCmd(size_t n) const
{
    wxPackageCommand ret(Item(n));

    // GetCommand() will do substitutions using wxPackageCommand's own keyword hashmap
    wxString text(ret.GetCommand());
    text.Replace(wxT("$(target)"), GetSelectedTarget());

    ret.SetCommand(text);
    return ret;
}

wxPackageCommandArray wxPackageCommandArray::GetValidCommandsFor(const wxPackage &pkg) const
{
    wxPackageCommandArray ret;
    for (size_t i=0; i<GetCount(); i++)
        if (Item(i).GetCondition().IsValidForPackage(pkg))
            ret.Add(Item(i));

    ret.CopyTargets(*this);
    return ret;
}




// ----------------------------------------------------------------------------
// wxPackageCommandSet
// ----------------------------------------------------------------------------

bool wxPackageCommandSet::IsUsedBy(const wxPackageCommandArray *arrays,
                                   wxStringHashMap &touse) const
{
    // VERY IMPORTANT:
    // We will say that the given commandset is "contained"
    // in this array if the two coincide. This is because in this way
    // we avoid problems regarding e.g. the command order.
    // in fact, if there are commands in this array which do not belong to
    // the given commandset, interleaved with those which belong, then
    // it's impossible to correctly separe user's commands from the commandset!
//    if (GetCount() != arr.GetCount())
//        return false;

    size_t checked[wxPBSS_MAX] = { 0, 0, 0, 0 };
    for (size_t j=0; j<GetCount(); j++)
    {
        for (size_t i=0; i<wxPBSS_MAX; i++)
        {
            if (Item(j).GetCondition().IsValidForStage(wxIdx2PackageBuildSystemStage(i)))
            {
                // our j-th command applies to the i-th stage.
                // checked[i] is the number of our commands valid for the i-th stage
                // found up to now; we must check that the checked[i]-th command of
                // array[i] is identic to our j-th item:

                if (arrays[i].GetCount() <= checked[i])
                    return false;       // not enough commands in user's array

                // order is important!
                if (arrays[i].Item(checked[i]) != Item(j))
                    return false;
                else
                    checked[i]++;
            }
        }
    }

    // when we loaded the commandset (see wxPackageXMLDescriptor::LoadStages)
    // we put the hashmap of commandset vars into all commands as keywords to
    // be substituted; so just pick one command:
    wxASSERT(GetCount() > 0);
    touse = arrays[0].Item(0).GetKeywords();

    return true;
}
