/*  This file is part of mhmake.
 *
 *  Copyright (C) 2001-2010 marha@sourceforge.net
 *
 *  Mhmake is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Mhmake is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Mhmake.  If not, see <http://www.gnu.org/licenses/>.
*/

/* $Rev$ */

#ifndef __UTIL_H__
#define __UTIL_H__

#include "fileinfo.h"

// List of pre-defined variables
#define WC_REVISION  "WC_REVISION"
#define WC_URL       "WC_URL"
#define AUTODEPFILE  "AUTODEPFILE"
#define OBJEXTVAR    "OBJEXT"
#define EXEEXTVAR    "EXEEXT"
#define SKIPHEADERS  "SKIPHEADERS"
#define MAKE         "MAKE"
#define MHMAKECONF   "MHMAKECONF"
#define BASEAUTOMAK  "BASEAUTOMAK"
#define CURDIR       "CURDIR"
#define USED_ENVVARS "USED_ENVVARS"
#define PATH         "PATH"
#ifdef WIN32
#define COMSPEC      "COMSPEC"
#define PYTHONEXE    "python.exe"
#define EXEEXT       ".exe"
#define OBJEXT       ".obj"
#define PLATFORM     "win32"
#else
#define COMSPEC      "SHELL"
#define PYTHONEXE    "python"
#define EXEEXT       ""
#define OBJEXT       ".o"
#define PLATFORM     "linux"
#endif

#define MHMAKEVER    "3.0.4"

class makecommand
{
  string m_BuildCommand;
public:
  makecommand();
  operator string()
  {
    return m_BuildCommand;
  }
};

extern makecommand g_MakeCommand;

///////////////////////////////////////////////////////////////////////////////
inline const char *NextItem(const char *pTmp,string &Output, const char *pToks=" \t")
{
  const char *pStart;
  const char *pStop;
  while (strchr(pToks,*pTmp)&&*pTmp) pTmp++;
  pStart=pTmp;
  while (1)
  {
    if (*pTmp=='"')
    {
      pTmp++;
      while (*pTmp && *pTmp!='"') pTmp++;
      if (*pTmp) pTmp++;
      pStop=pTmp;
      if (!*pTmp || strchr(pToks,*pTmp))
        break;
    }
    else if (*pTmp=='\'')
    {
      pTmp++;
      while (*pTmp && *pTmp!='\'') pTmp++;
      if (*pTmp) pTmp++;
      pStop=pTmp;
      if (!*pTmp || strchr(pToks,*pTmp))
        break;
    }
    else if (!*pTmp)
    {
      pStop=pTmp;
      break;
    }
    else
    {
      pTmp++;
      #if OSPATHSEP=='/'
      while (*pTmp)
      {
        if (!strchr(pToks,*pTmp) || (*(pTmp-1)=='\\'))
          pTmp++;
        else
          break;
      }
      #else
      while (!strchr(pToks,*pTmp)) pTmp++;
      #endif
      pStop=pTmp;
      break;
    }
  }
  Output=string(pStart,pStop);
  // skip trailing space
  while (strchr(pToks,*pTmp)&&*pTmp) pTmp++;
  return pTmp;
}

///////////////////////////////////////////////////////////////////////////////
inline const char *NextCharItem(const char *pTmp,string &Output,char Char)
{
  const char *pStart=pTmp;
  while (*pTmp && *pTmp!=Char) pTmp++;
  const char *pStop=pTmp;
  if (*pTmp) pTmp++;

  while (pStart<pStop && (*pStart==' ' || *pStart == '\t')) pStart++;
  pStop--;
  while (pStart<=pStop && (*pStop==' ' || *pStop == '\t')) pStop--;
  pStop++;

  Output=string(pStart,pStop);
  return pTmp;
}

///////////////////////////////////////////////////////////////////////////////
inline const char *SkipMakeExpr(const char *pMacro)
{
#ifdef _DEBUG
  const char *pMacroIn=pMacro;
#endif
  char Char=*pMacro++;
  char EndChar;
  if (Char=='(')
    EndChar=')';
  else if (Char=='{')
    EndChar='}';
  else
    return pMacro;
  Char=*pMacro++;
  while (Char!=EndChar)
  {
    if (Char=='$')
    {
      pMacro=SkipMakeExpr(pMacro);
    } else if (Char=='(')
    {
      pMacro=SkipMakeExpr(pMacro-1);
    }
#ifdef _DEBUG
    if (!*pMacro)
      throw(string(1,EndChar)+" not found in "+pMacroIn);
#endif
    Char=*pMacro++;
  }
  return pMacro;
}

///////////////////////////////////////////////////////////////////////////////
inline size_t SkipMakeExpr(const string &Expr,size_t i)
{
  const char *pTmp=Expr.c_str();
  return SkipMakeExpr(pTmp+i)-pTmp;
}

///////////////////////////////////////////////////////////////////////////////

string Substitute(const string &ToSubst,const string &SrcStr,const string &ToStr);

struct matchres
{
  string m_First;
  string m_Stem;
  string m_Last;
};

bool PercentMatch(const string &String,const string &Expr,matchres *pRes=NULL,const char Char='%');
bool PercentMatchNoCase(const string &String,const string &Expr,matchres *pRes=NULL,const char Char='%');
bool PercentMatchList(const string &String,const string &ExprList,matchres *pRes=NULL);
string ReplaceWithStem(const string &String,const string &Stem);

struct loadedmakefile : public refbase
{
  struct loadedmakefile_statics
  {
    map<string,string>   m_GlobalCommandLineVars;
    fileinfo            *m_MhMakeConf;

    loadedmakefile_statics();
  };
  static loadedmakefile_statics sm_Statics;

  fileinfo            *m_Makefile;
  const fileinfo      *m_MakeDir;
  map<string,string>   m_CommandLineVars;

  vector<string>       m_CommandLineTargets;
  refptr<mhmakefileparser> m_pMakefileParser;

  loadedmakefile(const fileinfo *pDir, vector<string> &Args,const string &Makefile=g_EmptyString);

  void LoadMakefile();
  void AddCommandLineVarsToEnvironment()
  {
    map<string,string>::const_iterator It=m_CommandLineVars.begin();
    map<string,string>::const_iterator ItEnd=m_CommandLineVars.end();
    while (It!=ItEnd)
    {
      sm_Statics.m_GlobalCommandLineVars.insert(*It++);
    }
  }

  int operator==(const loadedmakefile &Other)
  {
    if (m_Makefile!=Other.m_Makefile)
      return 0;
    if (m_MakeDir!=Other.m_MakeDir)
      return 0;
    if (m_CommandLineTargets.size()!=Other.m_CommandLineTargets.size())
      return 0;
    if (m_CommandLineVars.size()!=Other.m_CommandLineVars.size())
      return 0;
    map<string,string>::iterator VarIt=m_CommandLineVars.begin();
    while (VarIt!=m_CommandLineVars.end())
    {
      map<string,string>::const_iterator pFound=Other.m_CommandLineVars.find(VarIt->first);
      if (pFound==Other.m_CommandLineVars.end())
        return 0;
      if (pFound->second!=VarIt->second)
        return 0;
      VarIt++;
    }
    vector<string>::iterator TarIt=m_CommandLineTargets.begin();
    while (TarIt!=m_CommandLineTargets.end())
    {
      vector<string>::const_iterator OtherIt=Other.m_CommandLineTargets.begin();
      while (OtherIt!=Other.m_CommandLineTargets.begin())
      {
        if (*TarIt==*OtherIt)
          break;
        OtherIt++;
      }
      if (OtherIt==Other.m_CommandLineTargets.end())
        return 0;
      TarIt++;
    }
    return 1;
  }
};

class LOADEDMAKEFILES : public vector<refptr<loadedmakefile> >
{
public:
  refptr<loadedmakefile> find(const loadedmakefile &pToSearch);
  typedef vector<refptr<loadedmakefile> >::iterator iterator;
};

extern LOADEDMAKEFILES g_LoadedMakefiles;

bool MakeDirs(fileinfo *pDir); // Creates a directory tree
void DumpVarsAndRules();

#endif