/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
#include "cmUseMangledMesaCommand.h"

#include "cmsys/FStream.hxx"
#include "cmsys/RegularExpression.hxx"

#include "cmSystemTools.h"

class cmExecutionStatus;

bool
cmUseMangledMesaCommand::InitialPass(std::vector<std::string> const& args,
                                     cmExecutionStatus&)
{
    // expected two arguments:
    // argument one: the full path to gl_mangle.h
    // argument two : directory for output of edited headers
    if(args.size() != 2)
    {
        this->SetError("called with incorrect number of arguments");
        return false;
    }
    const char* inputDir = args[0].c_str();
    std::string glh      = inputDir;
    glh += "/";
    glh += "gl.h";
    if(!cmSystemTools::FileExists(glh))
    {
        std::string e = "Bad path to Mesa, could not find: ";
        e += glh;
        e += " ";
        this->SetError(e);
        return false;
    }
    const char*              destDir = args[1].c_str();
    std::vector<std::string> files;
    cmSystemTools::Glob(inputDir, "\\.h$", files);
    if(files.empty())
    {
        cmSystemTools::Error("Could not open Mesa Directory ", inputDir);
        return false;
    }
    cmSystemTools::MakeDirectory(destDir);
    for(std::string const& f : files)
    {
        std::string path = inputDir;
        path += "/";
        path += f;
        this->CopyAndFullPathMesaHeader(path.c_str(), destDir);
    }

    return true;
}

void
cmUseMangledMesaCommand::CopyAndFullPathMesaHeader(const char* source,
                                                   const char* outdir)
{
    std::string dir, file;
    cmSystemTools::SplitProgramPath(source, dir, file);
    std::string outFile = outdir;
    outFile += "/";
    outFile += file;
    std::string tempOutputFile = outFile;
    tempOutputFile += ".tmp";
    cmsys::ofstream fout(tempOutputFile.c_str());
    if(!fout)
    {
        cmSystemTools::Error(
            "Could not open file for write in copy operation: ",
            tempOutputFile.c_str(), outdir);
        cmSystemTools::ReportLastSystemError("");
        return;
    }
    cmsys::ifstream fin(source);
    if(!fin)
    {
        cmSystemTools::Error("Could not open file for read in copy operation",
                             source);
        return;
    }
    // now copy input to output and expand variables in the
    // input file at the same time
    std::string inLine;
    // regular expression for any #include line
    cmsys::RegularExpression includeLine(
        "^[ \t]*#[ \t]*include[ \t]*[<\"]([^\">]+)[\">]");
    // regular expression for gl/ or GL/ in a file (match(1) of above)
    cmsys::RegularExpression glDirLine("(gl|GL)(/|\\\\)([^<\"]+)");
    // regular expression for gl GL or xmesa in a file (match(1) of above)
    cmsys::RegularExpression glLine("(gl|GL|xmesa)");
    while(cmSystemTools::GetLineFromStream(fin, inLine))
    {
        if(includeLine.find(inLine))
        {
            std::string includeFile = includeLine.match(1);
            if(glDirLine.find(includeFile))
            {
                std::string gfile = glDirLine.match(3);
                fout << "#include \"" << outdir << "/" << gfile << "\"\n";
            } else if(glLine.find(includeFile))
            {
                fout << "#include \"" << outdir << "/" << includeLine.match(1)
                     << "\"\n";
            } else
            {
                fout << inLine << "\n";
            }
        } else
        {
            fout << inLine << "\n";
        }
    }
    // close the files before attempting to copy
    fin.close();
    fout.close();
    cmSystemTools::CopyFileIfDifferent(tempOutputFile.c_str(), outFile.c_str());
    cmSystemTools::RemoveFile(tempOutputFile);
}
