#include "sdk_precomp.h"
#include <wx/confbase.h>
#include <wx/fileconf.h>
#include <wx/log.h>
#include <wx/intl.h>
#include <wx/filename.h>
#include <wx/msgdlg.h>
#include "manager.h"
#include "projectmanager.h"
#include "messagemanager.h"
#include "macrosmanager.h"
#include "cbproject.h"
#include "projectloader.h"
#include "compilerfactory.h"
#include "globals.h"
#include "customvars.h"

ProjectLoader::ProjectLoader(cbProject* project)
    : m_pProject(project),
    m_Upgraded(false),
    m_OpenDirty(false)
{
	//ctor
}

ProjectLoader::~ProjectLoader()
{
	//dtor
}

bool ProjectLoader::Open(const wxString& filename)
{
    MessageManager* pMsg = Manager::Get()->GetMessageManager();
    if (!pMsg)
        return false;

    pMsg->DebugLog(_("Loading project file..."));
    TiXmlDocument doc(filename.mb_str());
    if (!doc.LoadFile())
        return false;

    pMsg->DebugLog(_("Parsing project file..."));
    TiXmlElement* root;
    TiXmlElement* proj;

    root = doc.FirstChildElement("CodeBlocks_project_file");
    if (!root)
    {
        // old tag
        root = doc.FirstChildElement("Code::Blocks_project_file");
        if (!root)
        {
            pMsg->DebugLog(_("Not a valid Code::Blocks project file..."));
            return false;
        }
    }
    proj = root->FirstChildElement("Project");
    if (!proj)
    {
        pMsg->DebugLog(_("No 'Project' element in file..."));
        return false;
    }

    DoProjectOptions(proj);
    DoBuild(proj);
    DoCompilerOptions(proj);
    DoResourceCompilerOptions(proj);
    DoLinkerOptions(proj);
    DoIncludesOptions(proj);
    DoLibsOptions(proj);
    DoExtraCommands(proj);
    DoUnits(proj);

    TiXmlElement* version = root->FirstChildElement("FileVersion");
    if (!version)
    {
        // pre 1.1 version
        ConvertVersion_Pre_1_1();
        // format changed also:
        // removed <IncludeDirs> and <LibDirs> elements and added them as child elements
        // in <Compiler> and <Linker> elements respectively
        // so set m_Upgraded to true, irrespectively of libs detection...
        m_Upgraded = true;
    }
    else
    {
        // do something important based on version
//        wxString major = version->Attribute("major");
//        wxString minor = version->Attribute("minor");
    }

    return true;
}

void ProjectLoader::ConvertVersion_Pre_1_1()
{
    // ask to detect linker libraries and move them to the new
    // CompileOptionsBase linker libs container
    wxString msg;
    msg.Printf(_("Project \"%s\" was saved with an earlier version of Code::Blocks.\n"
                "In the current version, link libraries are treated separately from linker options.\n"
                "Do you want to auto-detect the libraries \"%s\" is using and configure it accordingly?"),
                m_pProject->GetTitle().c_str(),
                m_pProject->GetTitle().c_str());
    if (wxMessageBox(msg, _("Question"), wxICON_QUESTION | wxYES_NO) == wxYES)
    {
        // project first
        ConvertLibraries(m_pProject);

        for (int i = 0; i < m_pProject->GetBuildTargetsCount(); ++i)
        {
            ConvertLibraries(m_pProject->GetBuildTarget(i));
            m_Upgraded = true;
        }
    }
}

void ProjectLoader::ConvertLibraries(CompileTargetBase* object)
{
    wxArrayString linkerOpts = object->GetLinkerOptions();
    wxArrayString linkLibs = object->GetLinkLibs();

    int compilerIdx = object->GetCompilerIndex();
    Compiler* compiler = CompilerFactory::Compilers[compilerIdx];
    wxString linkLib = compiler->GetSwitches().linkLibs;
    wxString libExt = compiler->GetSwitches().libExtension;
    size_t libExtLen = libExt.Length();

    size_t i = 0;
    while (i < linkerOpts.GetCount())
    {
        wxString opt = linkerOpts[i];
        if (!linkLib.IsEmpty() && opt.StartsWith(linkLib))
        {
            opt.Remove(0, 2);
            wxString ext = compiler->GetSwitches().libExtension;
            if (!ext.IsEmpty())
                ext = _T(".") + ext;
            linkLibs.Add(compiler->GetSwitches().libPrefix + opt + ext);
            linkerOpts.RemoveAt(i, 1);
        }
        else if (opt.Length() > libExtLen && opt.Right(libExtLen) == libExt)
        {
            linkLibs.Add(opt);
            linkerOpts.RemoveAt(i, 1);
        }
        else
            ++i;
    }

    object->SetLinkerOptions(linkerOpts);
    object->SetLinkLibs(linkLibs);
}

void ProjectLoader::DoProjectOptions(TiXmlElement* parentNode)
{
    TiXmlElement* node = parentNode->FirstChildElement("Option");
    if (!node)
        return; // no options

    wxString title;
    wxString makefile;
    bool makefile_custom = false;
    int defaultTarget = 0;
    int activeTarget = -1;
    int compilerIdx = 0;

    // loop through all options
    while (node)
    {
        if (node->Attribute("title"))
            title = _U(node->Attribute("title"));

        if (node->Attribute("makefile"))
            makefile = _U(node->Attribute("makefile"));

        if (node->Attribute("makefile_is_custom"))
            makefile_custom = strncmp(node->Attribute("makefile_is_custom"), "1", 1) == 0;

        if (node->Attribute("default_target"))
            defaultTarget = atoi(node->Attribute("default_target"));

        if (node->Attribute("active_target"))
            activeTarget = atoi(node->Attribute("active_target"));

        if (node->Attribute("compiler"))
            compilerIdx = GetValidCompilerIndex(atoi(node->Attribute("compiler")), _("project"));

        node = node->NextSiblingElement("Option");
    }

    m_pProject->SetTitle(title);
    m_pProject->SetMakefile(makefile);
    m_pProject->SetMakefileCustom(makefile_custom);
    m_pProject->SetDefaultExecuteTargetIndex(defaultTarget);
    m_pProject->SetActiveBuildTarget(activeTarget);
    m_pProject->SetCompilerIndex(compilerIdx);
}

void ProjectLoader::DoBuild(TiXmlElement* parentNode)
{
    TiXmlElement* node = parentNode->FirstChildElement("Build");
    while (node)
    {
        DoBuildTarget(node);
        DoEnvironment(node, m_pProject);
        node = node->NextSiblingElement("Build");
    }
}

void ProjectLoader::DoBuildTarget(TiXmlElement* parentNode)
{
    TiXmlElement* node = parentNode->FirstChildElement("Target");
    if (!node)
        return; // no options

    while (node)
    {
        ProjectBuildTarget* target = 0L;
        wxString title = _U(node->Attribute("title"));
        if (!title.IsEmpty())
            target = m_pProject->AddBuildTarget(title);

        if (target)
        {
            Manager::Get()->GetMessageManager()->DebugLog(_("Loading target %s"), title.c_str());
            DoBuildTargetOptions(node, target);
            DoCompilerOptions(node, target);
            DoResourceCompilerOptions(node, target);
            DoLinkerOptions(node, target);
            DoIncludesOptions(node, target);
            DoLibsOptions(node, target);
            DoExtraCommands(node, target);
            DoEnvironment(node, target);
        }

        node = node->NextSiblingElement("Target");
    }
}

void ProjectLoader::DoBuildTargetOptions(TiXmlElement* parentNode, ProjectBuildTarget* target)
{
    TiXmlElement* node = parentNode->FirstChildElement("Option");
    if (!node)
        return; // no options

    bool use_console_runner = true;
    wxString output;
    wxString working_dir;
    wxString obj_output;
    wxString deps_output;
    wxString deps;
    wxString added;
    int type = -1;
    int compilerIdx = m_pProject->GetCompilerIndex();
    wxString parameters;
    wxString hostApplication;
    bool includeInTargetAll = true;
    bool createStaticLib = false;
    bool createDefFile = false;
    int projectCompilerOptionsRelation = 3;
    int projectLinkerOptionsRelation = 3;
    int projectIncludeDirsRelation = 3;
    int projectLibDirsRelation = 3;

    while (node)
    {
        if (node->Attribute("use_console_runner"))
            use_console_runner = strncmp(node->Attribute("use_console_runner"), "0", 1) != 0;

        if (node->Attribute("output"))
            output = _U(node->Attribute("output"));

        if (node->Attribute("working_dir"))
            working_dir = _U(node->Attribute("working_dir"));

        if (node->Attribute("object_output"))
            obj_output = _U(node->Attribute("object_output"));

        if (node->Attribute("deps_output"))
            deps_output = _U(node->Attribute("deps_output"));

        if (node->Attribute("external_deps"))
            deps = _U(node->Attribute("external_deps"));

        if (node->Attribute("additional_output"))
            added = _U(node->Attribute("additional_output"));

        if (node->Attribute("type"))
            type = atoi(node->Attribute("type"));

        if (node->Attribute("compiler"))
            compilerIdx = GetValidCompilerIndex(atoi(node->Attribute("compiler")), _("build target"));

        if (node->Attribute("parameters"))
            parameters = _U(node->Attribute("parameters"));

        if (node->Attribute("host_application"))
            hostApplication = _U(node->Attribute("host_application"));

        if (node->Attribute("includeInTargetAll"))
            includeInTargetAll = atoi(node->Attribute("includeInTargetAll")) != 0;

        if (node->Attribute("createDefFile"))
            createDefFile = atoi(node->Attribute("createDefFile")) != 0;

        if (node->Attribute("createStaticLib"))
            createStaticLib = atoi(node->Attribute("createStaticLib")) != 0;

        if (node->Attribute("projectCompilerOptionsRelation"))
            projectCompilerOptionsRelation = atoi(node->Attribute("projectCompilerOptionsRelation"));

        if (node->Attribute("projectLinkerOptionsRelation"))
            projectLinkerOptionsRelation = atoi(node->Attribute("projectLinkerOptionsRelation"));

        if (node->Attribute("projectIncludeDirsRelation"))
            projectIncludeDirsRelation = atoi(node->Attribute("projectIncludeDirsRelation"));

        if (node->Attribute("projectLibDirsRelation"))
            projectLibDirsRelation = atoi(node->Attribute("projectLibDirsRelation"));

        node = node->NextSiblingElement("Option");
    }

    if (type != -1)
    {
        target->SetTargetType((TargetType)type); // type *must* come before output filename!
        target->SetOutputFilename(output); // because if no filename defined, one will be suggested based on target type...
        target->SetUseConsoleRunner(use_console_runner);
        if (!working_dir.IsEmpty())
            target->SetWorkingDir(working_dir);
        if (!obj_output.IsEmpty())
            target->SetObjectOutput(obj_output);
        if (!deps_output.IsEmpty())
            target->SetDepsOutput(deps_output);
        target->SetExternalDeps(deps);
        target->SetAdditionalOutputFiles(added);
        target->SetCompilerIndex(compilerIdx);
        target->SetExecutionParameters(parameters);
        target->SetHostApplication(hostApplication);
        target->SetIncludeInTargetAll(includeInTargetAll);
        target->SetCreateDefFile(createDefFile);
        target->SetCreateStaticLib(createStaticLib);
        target->SetOptionRelation(ortCompilerOptions, (OptionsRelation)projectCompilerOptionsRelation);
        target->SetOptionRelation(ortLinkerOptions, (OptionsRelation)projectLinkerOptionsRelation);
        target->SetOptionRelation(ortIncludeDirs, (OptionsRelation)projectIncludeDirsRelation);
        target->SetOptionRelation(ortLibDirs, (OptionsRelation)projectLibDirsRelation);
    }
}

void ProjectLoader::DoCompilerOptions(TiXmlElement* parentNode, ProjectBuildTarget* target)
{
    TiXmlElement* node = parentNode->FirstChildElement("Compiler");
    if (!node)
        return; // no options

    TiXmlElement* child = node->FirstChildElement("Add");
    while (child)
    {
        wxString option = _U(child->Attribute("option"));
        wxString dir = _U(child->Attribute("directory"));
        if (!option.IsEmpty())
        {
            if (target)
                target->AddCompilerOption(option);
            else
                m_pProject->AddCompilerOption(option);
        }
        if (!dir.IsEmpty())
        {
            if (target)
                target->AddIncludeDir(dir);
            else
                m_pProject->AddIncludeDir(dir);
        }

        child = child->NextSiblingElement("Add");
    }
}

void ProjectLoader::DoResourceCompilerOptions(TiXmlElement* parentNode, ProjectBuildTarget* target)
{
    TiXmlElement* node = parentNode->FirstChildElement("ResourceCompiler");
    if (!node)
        return; // no options

    TiXmlElement* child = node->FirstChildElement("Add");
    while (child)
    {
        wxString dir = _U(child->Attribute("directory"));
        if (!dir.IsEmpty())
        {
            if (target)
                target->AddResourceIncludeDir(dir);
            else
                m_pProject->AddResourceIncludeDir(dir);
        }

        child = child->NextSiblingElement("Add");
    }
}

void ProjectLoader::DoLinkerOptions(TiXmlElement* parentNode, ProjectBuildTarget* target)
{
    TiXmlElement* node = parentNode->FirstChildElement("Linker");
    if (!node)
        return; // no options

    TiXmlElement* child = node->FirstChildElement("Add");
    while (child)
    {
        wxString option = _U(child->Attribute("option"));
        wxString dir = _U(child->Attribute("directory"));
        wxString lib = _U(child->Attribute("library"));
        if (!option.IsEmpty())
        {
            if (target)
                target->AddLinkerOption(option);
            else
                m_pProject->AddLinkerOption(option);
        }
        if (!lib.IsEmpty())
        {
            if (target)
                target->AddLinkLib(lib);
            else
                m_pProject->AddLinkLib(lib);
        }
        if (!dir.IsEmpty())
        {
            if (target)
                target->AddLibDir(dir);
            else
                m_pProject->AddLibDir(dir);
        }

        child = child->NextSiblingElement("Add");
    }
}

void ProjectLoader::DoIncludesOptions(TiXmlElement* parentNode, ProjectBuildTarget* target)
{
    TiXmlElement* node = parentNode->FirstChildElement("IncludeDirs");
    if (!node)
        return; // no options

    TiXmlElement* child = node->FirstChildElement("Add");
    while (child)
    {
        wxString option = _U(child->Attribute("option"));
        if (!option.IsEmpty())
        {
            if (target)
                target->AddIncludeDir(option);
            else
                m_pProject->AddIncludeDir(option);
        }

        child = child->NextSiblingElement("Add");
    }
}

void ProjectLoader::DoLibsOptions(TiXmlElement* parentNode, ProjectBuildTarget* target)
{
    TiXmlElement* node = parentNode->FirstChildElement("LibDirs");
    if (!node)
        return; // no options

    TiXmlElement* child = node->FirstChildElement("Add");
    while (child)
    {
        wxString option = _U(child->Attribute("option"));
        if (!option.IsEmpty())
        {
            if (target)
                target->AddLibDir(option);
            else
                m_pProject->AddLibDir(option);
        }

        child = child->NextSiblingElement("Add");
    }
}

void ProjectLoader::DoExtraCommands(TiXmlElement* parentNode, ProjectBuildTarget* target)
{
    TiXmlElement* node = parentNode->FirstChildElement("ExtraCommands");
    while (node)
    {
        CompileOptionsBase* base = target ? target : (CompileOptionsBase*)m_pProject;
        TiXmlElement* child = node->FirstChildElement("Mode");
        while (child)
        {
            wxString mode = _U(child->Attribute("before"));
            if (mode == _T("always"))
                base->SetAlwaysRunPreBuildSteps(true);
            mode = _U(child->Attribute("after"));
            if (mode == _T("always"))
                base->SetAlwaysRunPostBuildSteps(true);

            child = child->NextSiblingElement("Mode");
        }

        child = node->FirstChildElement("Add");
        while (child)
        {
            wxString before;
            wxString after;

            if (child->Attribute("before"))
                before = _U(child->Attribute("before"));
            if (child->Attribute("after"))
                after = _U(child->Attribute("after"));

            if (!before.IsEmpty())
                base->AddCommandsBeforeBuild(before);
            if (!after.IsEmpty())
                base->AddCommandsAfterBuild(after);

            child = child->NextSiblingElement("Add");
        }
        node = node->NextSiblingElement("ExtraCommands");
    }
}

void ProjectLoader::DoEnvironment(TiXmlElement* parentNode, CompileOptionsBase* base)
{
	if (!base)
        return;
    CustomVars& vars = base->GetCustomVars();

    TiXmlElement* node = parentNode->FirstChildElement("Environment");
    while (node)
    {
        TiXmlElement* child = node->FirstChildElement("Variable");
        while (child)
        {
            wxString name = _U(child->Attribute("name"));
            wxString value = _U(child->Attribute("value"));
            if (!name.IsEmpty())
            	vars.Add(name, value);

            child = child->NextSiblingElement("Variable");
        }
        node = node->NextSiblingElement("Environment");
    }
}

void ProjectLoader::DoUnits(TiXmlElement* parentNode)
{
    Manager::Get()->GetMessageManager()->DebugLog(_U("Loading project files..."));
    TiXmlElement* unit = parentNode->FirstChildElement("Unit");
    while (unit)
    {
        wxString filename = _U(unit->Attribute("filename"));
        if (!filename.IsEmpty())
        {
            ProjectFile* file = m_pProject->AddFile(-1, filename);
            if (!file)
                Manager::Get()->GetMessageManager()->DebugLog(_("Can't load file '%s'"), filename.c_str());
            else
                DoUnitOptions(unit, file);
        }

        unit = unit->NextSiblingElement("Unit");
    }
}

void ProjectLoader::DoUnitOptions(TiXmlElement* parentNode, ProjectFile* file)
{
    int tempval = 0;
    bool foundCompile = false;
    bool foundLink = false;
    bool foundCompilerVar = false;

    TiXmlElement* node = parentNode->FirstChildElement("Option");
    while (node)
    {
        if (node->Attribute("compilerVar"))
        {
            file->compilerVar = _U(node->Attribute("compilerVar"));
            foundCompilerVar = true;
        }
        //
        if (node->QueryIntAttribute("compile", &tempval) == TIXML_SUCCESS)
        {
            file->compile = tempval != 0;
            foundCompile = true;
        }
        //
        if (node->QueryIntAttribute("link", &tempval) == TIXML_SUCCESS)
        {
            file->link = tempval != 0;
            foundLink = true;
        }
        //
        if (node->QueryIntAttribute("weight", &tempval) == TIXML_SUCCESS)
            file->weight = tempval;
        //
        if (node->QueryIntAttribute("useBuildCommand", &tempval) == TIXML_SUCCESS)
            file->useCustomBuildCommand = tempval != 0;
        //
        if (node->Attribute("buildCommand"))
        {
            wxString tmp = _U(node->Attribute("buildCommand"));
            if (!tmp.IsEmpty())
            {
                tmp.Replace(_T("\\n"), _T("\n"));
                file->buildCommand = tmp;
            }
        }
        //
        if (node->QueryIntAttribute("autoDeps", &tempval) == TIXML_SUCCESS)
            file->autoDeps = tempval != 0;
        //
        if (node->Attribute("customDeps"))
        {
            wxString tmp = _U(node->Attribute("customDeps"));
            if (!tmp.IsEmpty())
            {
                tmp.Replace(_T("\\n"), _T("\n"));
                file->customDeps = tmp;
            }
        }
        //
        if (node->Attribute("objectName"))
        {
            wxFileName objName(_U(node->Attribute("objectName")));
            FileType ft = FileTypeOf(file->relativeFilename);
            if (ft != ftResource && ft != ftResourceBin)
            {
                if (objName.GetExt() != CompilerFactory::Compilers[m_pProject->GetCompilerIndex()]->GetSwitches().objectExtension)
                    file->SetObjName(file->relativeFilename);
            }
        }
        //
        if (node->Attribute("target"))
            file->AddBuildTarget(_U(node->Attribute("target")));

        node = node->NextSiblingElement("Option");
    }

    // make sure the "compile" and "link" flags are honored
    if (!foundCompile)
        file->compile = true;
    if (!foundLink)
        file->link = true;
    if (!foundCompilerVar)
        file->compilerVar = _T("CPP");
}

bool ProjectLoader::Save(const wxString& filename)
{
    wxString buffer;
    wxArrayString array;
    CustomVars* vars = 0;

    buffer << _T("<?xml version=\"1.0\"?>") << _T('\n');
    buffer << _T("<!DOCTYPE CodeBlocks_project_file>") << _T('\n');
    buffer << _T("<CodeBlocks_project_file>") << _T('\n');
    buffer << _T('\t') << _T("<FileVersion major=\"") << PROJECT_FILE_VERSION_MAJOR << _T("\" minor=\"") << PROJECT_FILE_VERSION_MINOR << _T("\"/>") << _T('\n');
    buffer << _T('\t') << _T("<Project>") << _T('\n');
    buffer << _T('\t') << _T('\t') << _T("<Option title=\"") << FixEntities(m_pProject->GetTitle()) << _T("\"/>") << _T('\n');
    buffer << _T('\t') << _T('\t') << _T("<Option makefile=\"") << FixEntities(m_pProject->GetMakefile()) << _T("\"/>") << _T('\n');
    buffer << _T('\t') << _T('\t') << _T("<Option makefile_is_custom=\"") << m_pProject->IsMakefileCustom() << _T("\"/>") << _T('\n');
    if (m_pProject->GetDefaultExecuteTargetIndex() != 0)
        buffer << _T('\t') << _T('\t') << _T("<Option default_target=\"") << m_pProject->GetDefaultExecuteTargetIndex() << _T("\"/>") << _T('\n');
    if (m_pProject->GetActiveBuildTarget() != -1)
        buffer << _T('\t') << _T('\t') << _T("<Option active_target=\"") << m_pProject->GetActiveBuildTarget() << _T("\"/>") << _T('\n');
    buffer << _T('\t') << _T('\t') << _T("<Option compiler=\"") << m_pProject->GetCompilerIndex() << _T("\"/>") << _T('\n');

    buffer << _T('\t') << _T('\t') << _T("<Build>") << _T('\n');
    for (int i = 0; i < m_pProject->GetBuildTargetsCount(); ++i)
    {
        ProjectBuildTarget* target = m_pProject->GetBuildTarget(i);
        if (!target)
            break;

        buffer << _T('\t') << _T('\t') << _T('\t') << _T("<Target title=\"") << FixEntities(target->GetTitle()) << _T("\">") << _T('\n');
        if (target->GetTargetType() != ttCommandsOnly)
        {
            buffer << _T('\t') << _T('\t') << _T('\t') << _T('\t') << _T("<Option output=\"") << FixEntities(target->GetOutputFilename()) << _T("\"/>") << _T('\n');
            buffer << _T('\t') << _T('\t') << _T('\t') << _T('\t') << _T("<Option working_dir=\"") << FixEntities(target->GetWorkingDir()) << _T("\"/>") << _T('\n');
            buffer << _T('\t') << _T('\t') << _T('\t') << _T('\t') << _T("<Option object_output=\"") << FixEntities(target->GetObjectOutput()) << _T("\"/>") << _T('\n');
            buffer << _T('\t') << _T('\t') << _T('\t') << _T('\t') << _T("<Option deps_output=\"") << FixEntities(target->GetDepsOutput()) << _T("\"/>") << _T('\n');
        }
        if (!target->GetExternalDeps().IsEmpty())
            buffer << _T('\t') << _T('\t') << _T('\t') << _T('\t') << _T("<Option external_deps=\"") << FixEntities(target->GetExternalDeps()) << _T("\"/>") << _T('\n');
        if (!target->GetAdditionalOutputFiles().IsEmpty())
            buffer << _T('\t') << _T('\t') << _T('\t') << _T('\t') << _T("<Option additional_output=\"") << FixEntities(target->GetAdditionalOutputFiles()) << _T("\"/>") << _T('\n');
        buffer << _T('\t') << _T('\t') << _T('\t') << _T('\t') << _T("<Option type=\"") << target->GetTargetType() << _T("\"/>") << _T('\n');
        buffer << _T('\t') << _T('\t') << _T('\t') << _T('\t') << _T("<Option compiler=\"") << target->GetCompilerIndex() << _T("\"/>") << _T('\n');
        if (target->GetTargetType() == ttConsoleOnly && !target->GetUseConsoleRunner())
            buffer << _T('\t') << _T('\t') << _T('\t') << _T('\t') << _T("<Option use_console_runner=\"0\"/>") << _T('\n');
        if (!target->GetExecutionParameters().IsEmpty())
            buffer << _T('\t') << _T('\t') << _T('\t') << _T('\t') << _T("<Option parameters=\"") << FixEntities(target->GetExecutionParameters()) << _T("\"/>") << _T('\n');
        if (!target->GetHostApplication().IsEmpty())
            buffer << _T('\t') << _T('\t') << _T('\t') << _T('\t') << _T("<Option host_application=\"") << FixEntities(target->GetHostApplication()) << _T("\"/>") << _T('\n');
        if (!target->GetIncludeInTargetAll())
            buffer << _T('\t') << _T('\t') << _T('\t') << _T('\t') << _T("<Option includeInTargetAll=\"0\"/>") << _T('\n');
        if ((target->GetTargetType() == ttStaticLib || target->GetTargetType() == ttDynamicLib) && target->GetCreateDefFile())
            buffer << _T('\t') << _T('\t') << _T('\t') << _T('\t') << _T("<Option createDefFile=\"1\"/>") << _T('\n');
        if (target->GetTargetType() == ttDynamicLib && target->GetCreateStaticLib())
            buffer << _T('\t') << _T('\t') << _T('\t') << _T('\t') << _T("<Option createStaticLib=\"1\"/>") << _T('\n');
        if (target->GetOptionRelation(ortCompilerOptions) != 3) // 3 is the default
            buffer << _T('\t') << _T('\t') << _T('\t') << _T('\t') << _T("<Option projectCompilerOptionsRelation=\"") << target->GetOptionRelation(ortCompilerOptions) << _T("\"/>") << _T('\n');
        if (target->GetOptionRelation(ortLinkerOptions) != 3) // 3 is the default
            buffer << _T('\t') << _T('\t') << _T('\t') << _T('\t') << _T("<Option projectLinkerOptionsRelation=\"") << target->GetOptionRelation(ortLinkerOptions) << _T("\"/>") << _T('\n');
        if (target->GetOptionRelation(ortIncludeDirs) != 3) // 3 is the default
            buffer << _T('\t') << _T('\t') << _T('\t') << _T('\t') << _T("<Option projectIncludeDirsRelation=\"") << target->GetOptionRelation(ortIncludeDirs) << _T("\"/>") << _T('\n');
        if (target->GetOptionRelation(ortResDirs) != 3) // 3 is the default
            buffer << _T('\t') << _T('\t') << _T('\t') << _T('\t') << _T("<Option projectResourceIncludeDirsRelation=\"") << target->GetOptionRelation(ortResDirs) << _T("\"/>") << _T('\n');
        if (target->GetOptionRelation(ortLibDirs) != 3) // 3 is the default
            buffer << _T('\t') << _T('\t') << _T('\t') << _T('\t') << _T("<Option projectLibDirsRelation=\"") << target->GetOptionRelation(ortLibDirs) << _T("\"/>") << _T('\n');
        SaveCompilerOptions(buffer, target, 4);
        SaveResourceCompilerOptions(buffer, target, 4);
        SaveLinkerOptions(buffer, target, 4);
        SaveOptions(buffer, target->GetCommandsBeforeBuild(), _T("ExtraCommands"), 4, _T("before"), target->GetAlwaysRunPreBuildSteps() ? _T("<Mode before=\"always\" />") : _T(""));
        SaveOptions(buffer, target->GetCommandsAfterBuild(), _T("ExtraCommands"), 4, _T("after"), target->GetAlwaysRunPostBuildSteps() ? _T("<Mode after=\"always\" />") : _T(""));

        vars = &target->GetCustomVars();
        SaveEnvironment(buffer, vars, 4);

        buffer << _T('\t') << _T('\t') << _T('\t') << _T("</Target>") << _T('\n');
    }
    vars = &m_pProject->GetCustomVars();
    SaveEnvironment(buffer, vars, 3);
    buffer << _T('\t') << _T('\t') << _T("</Build>") << _T('\n');

    SaveCompilerOptions(buffer, m_pProject, 2);
    SaveResourceCompilerOptions(buffer, m_pProject, 2);
    SaveLinkerOptions(buffer, m_pProject, 2);
    SaveOptions(buffer, m_pProject->GetCommandsBeforeBuild(), wxString(_T("ExtraCommands")), 2, _T("before"), m_pProject->GetAlwaysRunPreBuildSteps() ? _T("<Mode before=\"always\" />") : _T(""));
    SaveOptions(buffer, m_pProject->GetCommandsAfterBuild(), wxString(_T("ExtraCommands")), 2, _T("after"), m_pProject->GetAlwaysRunPostBuildSteps() ? _T("<Mode after=\"always\" />") : _T(""));

    int count = m_pProject->GetFilesCount();
    for (int i = 0; i < count; ++i)
    {
        ProjectFile* f = m_pProject->GetFile(i);
        buffer << _T('\t') << _T('\t') << _T("<Unit filename=\"") << FixEntities(f->relativeFilename) << _T("\">") << _T('\n');
        buffer << _T('\t') << _T('\t') << _T('\t') << _T("<Option compilerVar=\"") << FixEntities(f->compilerVar) << _T("\"/>") << _T('\n');
        if (!f->compile)
            buffer << _T('\t') << _T('\t') << _T('\t') << _T("<Option compile=\"0\"/>") << _T('\n');
        if (!f->link)
            buffer << _T('\t') << _T('\t') << _T('\t') << _T("<Option link=\"0\"/>") << _T('\n');
        if (f->weight != 50)
            buffer << _T('\t') << _T('\t') << _T('\t') << _T("<Option weight=\"") << f->weight << _T("\"/>") << _T('\n');
        if (f->useCustomBuildCommand)
            buffer << _T('\t') << _T('\t') << _T('\t') << _T("<Option useBuildCommand=\"1\"/>") << _T('\n');
        if (!f->buildCommand.IsEmpty())
        {
            f->buildCommand.Replace(_T("\n"), _T("\\n"));
            buffer << _T('\t') << _T('\t') << _T('\t') << _T("<Option buildCommand=\"") << FixEntities(f->buildCommand) << _T("\"/>") << _T('\n');
        }
        if (!f->autoDeps)
            buffer << _T('\t') << _T('\t') << _T('\t') << _T("<Option autoDeps=\"0\"/>") << _T('\n');
        if (!f->customDeps.IsEmpty())
        {
            f->customDeps.Replace(_T("\n"), _T("\\n"));
            buffer << _T('\t') << _T('\t') << _T('\t') << _T("<Option customDeps=\"") << FixEntities(f->customDeps) << _T("\"/>") << _T('\n');
        }
        if (!f->GetObjName().IsEmpty())
        {
            wxFileName tmp(f->GetObjName());
            if (FileTypeOf(f->relativeFilename) != ftHeader &&
                tmp.GetExt() != CompilerFactory::Compilers[m_pProject->GetCompilerIndex()]->GetSwitches().objectExtension)
            {
                buffer << _T('\t') << _T('\t') << _T('\t') << _T("<Option objectName=\"") << FixEntities(f->GetObjName()) << _T("\"/>") << _T('\n');
            }
        }
        for (unsigned int x = 0; x < f->buildTargets.GetCount(); ++x)
            buffer << _T('\t') << _T('\t') << _T('\t') << _T("<Option target=\"") << FixEntities(f->buildTargets[x]) << _T("\"/>") << _T('\n');
        for (unsigned int x = 0; x < f->breakpoints.GetCount(); ++x)
		{
			DebuggerBreakpoint* bp = f->breakpoints[x];
            buffer << _T('\t') << _T('\t') << _T('\t') << _T("<Breakpoint ");
			buffer << _T("line=\"") << bp->line << _T("\" ");
			buffer << _T("enabled=\"") << bp->enabled << _T("\" ");
			buffer << _T("pass=\"") << bp->ignoreCount << _T("\" ");
			buffer << _T("/>") << _T('\n');
		}
		buffer << _T('\t') << _T('\t') << _T("</Unit>") << _T('\n');
    }

    buffer << _T('\t') << _T("</Project>") << _T('\n');
    buffer << _T("</CodeBlocks_project_file>") << _T('\n');

    wxFile file(filename, wxFile::write);
    if (cbWrite(file,buffer))
    {
		m_pProject->SetModified(false);
        return true;
    }
    return false;
}

void ProjectLoader::SaveCompilerOptions(wxString& buffer, CompileOptionsBase* object, int nrOfTabs)
{
    wxString compopts;
    BeginOptionSection(compopts, _T("Compiler"), nrOfTabs);
    bool hasCompOpts = DoOptionSection(compopts, object->GetCompilerOptions(), nrOfTabs + 1, _T("option"));
    bool hasCompDirs = DoOptionSection(compopts, object->GetIncludeDirs(), nrOfTabs + 1, _T("directory"));
    if (hasCompOpts || hasCompDirs)
    {
        EndOptionSection(compopts, _T("Compiler"), nrOfTabs);
        buffer << compopts;
    }
}

void ProjectLoader::SaveResourceCompilerOptions(wxString& buffer, CompileOptionsBase* object, int nrOfTabs)
{
    wxString compopts;
    BeginOptionSection(compopts, _T("ResourceCompiler"), nrOfTabs);
    bool hasCompDirs = DoOptionSection(compopts, object->GetResourceIncludeDirs(), nrOfTabs + 1, _T("directory"));
    if (hasCompDirs)
    {
        EndOptionSection(compopts, _T("ResourceCompiler"), nrOfTabs);
        buffer << compopts;
    }
}

void ProjectLoader::SaveLinkerOptions(wxString& buffer, CompileOptionsBase* object, int nrOfTabs)
{
    wxString linkopts;
    BeginOptionSection(linkopts, _T("Linker"), nrOfTabs);
    bool hasLinkOpts = DoOptionSection(linkopts, object->GetLinkerOptions(), nrOfTabs + 1, _T("option"));
    bool hasLibs = DoOptionSection(linkopts, object->GetLinkLibs(), nrOfTabs + 1, _T("library"));
    bool hasLinkDirs = DoOptionSection(linkopts, object->GetLibDirs(), nrOfTabs + 1, _T("directory"));
    if (hasLinkOpts || hasLibs || hasLinkDirs)
    {
        EndOptionSection(linkopts, _T("Linker"), nrOfTabs);
        buffer << linkopts;
    }
}

void ProjectLoader::SaveEnvironment(wxString& buffer, CustomVars* vars, int nrOfTabs)
{
    if (!vars)
        return;
    const VarsArray& v = vars->GetVars();
    if (v.GetCount() == 0)
        return;
    for (int x = 0; x < nrOfTabs; ++x) buffer << _T('\t');
    buffer << _T("<Environment>") << _T('\n');
    for (unsigned int i = 0; i < v.GetCount(); ++i)
    {
        Var& var = v[i];
        for (int x = 0; x <= nrOfTabs; ++x) buffer << _T('\t');
        buffer << _T("<Variable name=\"") << var.name << _T("\" value=\"") << var.value << _T("\"/>") << _T('\n');
    }
    for (int x = 0; x < nrOfTabs; ++x) buffer << _T('\t');
    buffer << _T("</Environment>") << _T('\n');
}

void ProjectLoader::BeginOptionSection(wxString& buffer, const wxString& sectionName, int nrOfTabs)
{
    wxString local;
    for (int i = 0; i < nrOfTabs; ++i)
        local << _T('\t');
    local << _T("<") << sectionName << _T(">") << _T('\n');
    buffer << local;
}

bool ProjectLoader::DoOptionSection(wxString& buffer, const wxArrayString& array, int nrOfTabs, const wxString& optionName)
{
    if (!array.GetCount())
        return false;

    bool empty = true;
    wxString local;
    for (unsigned int i = 0; i < array.GetCount(); ++i)
    {
        if (array[i].IsEmpty())
            continue;

        empty = false;
        for (int x = 0; x < nrOfTabs; ++x)
            local << _T('\t');
        local << _T("<Add ") << optionName << _T("=\"") << FixEntities(array[i]) << _T("\"/>") << _T('\n');
    }
    buffer << local;
    return !empty;
}

void ProjectLoader::EndOptionSection(wxString& buffer, const wxString& sectionName, int nrOfTabs)
{
    wxString local;
    for (int i = 0; i < nrOfTabs; ++i)
        local << _T('\t');
    local << _T("</") << sectionName << _T(">") << _T('\n');
    buffer << local;
}

void ProjectLoader::SaveOptions(wxString& buffer, const wxArrayString& array, const wxString& sectionName, int nrOfTabs, const wxString& optionName, const wxString& extra)
{
    if (!array.GetCount())
        return;

    wxString local;
    BeginOptionSection(local, sectionName, nrOfTabs);
    if (!extra.IsEmpty())
    {
        for (int i = 0; i < nrOfTabs + 1; ++i)
            local << _T('\t');
        local << extra << _T('\n');
    }
    bool notEmpty = DoOptionSection(local, array, nrOfTabs + 1, optionName);
    if (notEmpty || !extra.IsEmpty())
    {
        EndOptionSection(local, sectionName, nrOfTabs);
        buffer << local;
    }
}

int ProjectLoader::GetValidCompilerIndex(int proposal, const wxString& scope)
{
    if (CompilerFactory::CompilerIndexOK(proposal))
        return proposal;

    m_OpenDirty = true;

    wxArrayString compilers;
    for (unsigned int i = 0; i < CompilerFactory::Compilers.GetCount(); ++i)
    {
        compilers.Add(CompilerFactory::Compilers[i]->GetName());
    }

    wxString msg;
    msg.Printf(_("The specified compiler does not exist.\nPlease select the compiler to use for the %s:"), scope.c_str());
    proposal = wxGetSingleChoiceIndex(msg, _("Select compiler"), compilers);

    if (proposal == -1)
    {
        wxMessageBox(_("Setting to default compiler..."), _("Warning"), wxICON_WARNING);
        return CompilerFactory::GetDefaultCompilerIndex();
    }
    return proposal;
}


syntax highlighted by Code2HTML, v. 0.9.1