/* Copyright (C) 1996,1997 Robert H”hne, see COPYING.RH for details */
/* This file is part of RHIDE. */
#define Uses_ifpstream
#define Uses_ofpstream
#define Uses_MsgBox
#define Uses_TProject
#define Uses_TDepCollection
#define Uses_TOptions
#define Uses_TDirList
#define Uses_TParamList
#define Uses_TStringCollection

#include <libide.h>

#define Uses_TSCollection
#define Uses_tvutilFunctions
#include <libtvuti.h>

#include <rhide.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
#include <unistd.h>

TProject *project;
char *project_directory = NULL;
char *project_name = NULL;
int recursive_make = 0;

Boolean ReadProject(const char *prjname)
{
  ifpstream *ifile;
  char *magic;
  ifile = new ifpstream(prjname);
  if (ifile->good())
  {
    magic = ifile->readString();
    if (!ifile->good() || 
        (strcmp(magic,PROJECT_IDENT) && strcmp(magic,OLD_PROJECT_IDENT)) )
    {
      messageBox(mfError | mfOKButton,_("Invalid project-file %s(%s)"),
                 prjname,magic);
      delete(ifile);
      return False;
    }
    delete magic;
    *ifile >> project;
    if (!CReservedWords) DefaultReservedWords();
    if (!GPCReservedWords) DefaultGPCReservedWords();
    if (!RHIDEUserWords) DefaultUserWords();
    delete(ifile);
    if (project_directory) string_free(project_directory);
    project_directory = getcwd(NULL,512);
    ClearFindCache();
    return True;
  }
  delete(ifile);
  return False;
}

static TSCollection *targets;
static int depcount;

static Boolean AddTarget(const char * name)
{
  int i;
  char * tmp;
  for (i=0;i<targets->getCount();i++)
  {
    if (!strcmp(name,(char *)targets->at(i))) return False;
  }
  string_dup(tmp,name);
  targets->insert(tmp);
  return True;
}

char *GetCompilerSpec(TDependency *dep,Boolean & is_user);

static void WriteTarget(FILE *f,TDependency *dep,int depth)
{
  int i;
  if (AddTarget(FName(dep->dest_name)) == False) return;
  if (dep->compile_id == COMPILE_NONE) return;
  if (!dep->dest_name)
  {
    if (!dep->dependencies || dep->dependencies->getCount() == 0) return;
  }
  fprintf(f,"DEPS_%d=",depcount);
  depcount++;
  if (dep->source_name)
  {
    if (dep->compile_id != COMPILE_PROJECT)
    {
      fprintf(f,"%s",FName(dep->source_name));
    }
  }
  if ((AllDeps || depth<2) && dep->dependencies &&
      dep->dependencies->getCount()>0)
  {
    for (i=0;i<dep->dependencies->getCount();i++)
    {
      TDependency *_dep = (TDependency *)dep->dependencies->at(i);
      if (_dep->compile_id == COMPILE_PROJECT)
      {
        char *dir,*name,*ext;
        split_fname(FName(_dep->dest_name),dir,name,ext);
        fprintf(f,"\\\n\t\t$(%s%s.dir)/%s%s",name,ext,name,ext);
        string_free(dir);
        string_free(name);
        string_free(ext);
      }
      else
      {
#if 0
        fprintf(f,"\\\n\t\t%s",FName(_dep->dest_name));
#else
        char *dname;
        FindFile(FName(_dep->dest_name),dname);
        fprintf(f,"\\\n\t\t%s",dname);
#endif
      }
    }
  }
  fprintf(f,"\n");
  if (depcount == 1)
  {
    fprintf(f,"NO_LINK=");
    for (i=0;i<dep->dependencies->getCount();i++)
    {
      TDependency *_dep = (TDependency *)dep->dependencies->at(i);
      if (!_dep->exclude_from_link) continue;
      if (_dep->compile_id == COMPILE_PROJECT)
      {
        char *dir,*name,*ext;
        split_fname(FName(_dep->dest_name),dir,name,ext);
        fprintf(f,"\\\n\t\t$(%s%s.dir)/%s%s",name,ext,name,ext);
        string_free(dir);
        string_free(name);
        string_free(ext);
      }
      else
      {
        fprintf(f,"\\\n\t\t%s",FName(_dep->dest_name));
      }
    }
    fprintf(f,"\n");
    fprintf(f,"LINK_FILES=$(filter-out $(NO_LINK),$(DEPS_%d))\n",depcount-1);
  }
  if (dep->compile_id == COMPILE_PROJECT)
  {
    char *dir,*name,*ext;
    split_fname(FName(dep->dest_name),dir,name,ext);
    if (*dir)
      dir[strlen(dir)-1] = 0;
    fprintf(f,"$(%s%s.dir)/%s%s: $(DEPS_%d) force\n",name,ext,name,ext,
            depcount-1);
    string_free(dir);
    string_free(name);
    string_free(ext);
  }
  else
  {
    char *dname;
    FindFile(FName(dep->dest_name),dname);
    AbsToRelPath(project_directory,dname);
    fprintf(f,"%s: $(DEPS_%d)\n",dname,depcount-1);
    string_free(dname);
  }
  if (dep->compile_id != COMPILE_PROJECT)
  {
    Boolean is_user;
    char *spec = GetCompilerSpec(dep,is_user);
    if (spec) fprintf(f,"\t%s\n",spec);
    string_free(spec);
    if ((AllDeps || depth<2) && dep->dependencies &&
        dep->dependencies->getCount()>0)
    {
      for (i=0;i<dep->dependencies->getCount();i++)
      {
        WriteTarget(f,(TDependency *)dep->dependencies->at(i),depth+1);
      }
    }
  }
  if (dep->compile_id == COMPILE_PROJECT)
  {
    char *dir,*name,*ext;
    split_fname(FName(dep->dest_name),dir,name,ext);
    if (*dir)
      dir[strlen(dir)-1] = 0;
    string_free(name);
    string_free(ext);
    BaseName(FName(dep->source_name),name,False);
    fprintf(f,"\t$(MAKE)%s%s -f %s.mak\n",*dir?" -C ":"",*dir?dir:"",name);
    if (recursive_make && PushProject(dir,FName(dep->source_name)) == True)
    {
      int _depcount = depcount;
      TSCollection *_targets = targets;
      WriteMake();
      PopProject();
      depcount = _depcount;
      targets = _targets;
    }
    string_free(dir);
    string_free(name);
  }
  if (dep->local_options->getCount()>0)
  {
    fprintf(f,"LOCAL_%d=",(depcount-1));
    for (i=0;i<dep->local_options->getCount();i++)
    {
      char buffer[1000],*_buffer=(char *)(dep->local_options->at(i));
      char *tmp = buffer;
      int has_spaces = 0;
      if (strchr(_buffer,' ') != NULL) has_spaces = 1;
      strcpy(tmp,FName(dep->source_name));
      strcat(tmp,"___");
      while (*tmp) tmp++;
      if (has_spaces)
      {
        *tmp = '"';
        tmp++;
      }
      while (*_buffer)
      {
        if (*_buffer == ' ')
        {
          *tmp = 0;
          strcat(tmp,"___~~~___");
          while (*tmp) tmp++;
        }
        else
        {
          *tmp = *_buffer;
          tmp++;
        }
        _buffer++;
      }
      if (has_spaces)
      {
        *tmp = '"';
        tmp++;
      }
      *tmp = 0;
      fprintf(f,"\\\n\t%s",buffer);
    }
    fprintf(f,"\nLOCAL_OPTIONS += $(LOCAL_%d)\n",(depcount-1));
  }
}

static void WriteTargets(FILE *f)
{
  depcount = 0;
  targets = new TSCollection();
  WriteTarget(f,project,1);
  destroy(targets);
}

static void check_vars(TStringCollection *vars,TDirList *dirs)
{
  int i;
  ccIndex index;
  char *tmp,*temp,*temp_end,c;
  for (i=0;i<dirs->getCount();i++)
  {
    tmp = (char *)dirs->at(i);
    while ((temp = strchr(tmp,'$')) != NULL)
    {
      temp+=2;
      tmp = temp;
      temp_end = strchr(temp,')');
      if (!temp_end) continue;
      c = *temp_end;
      *temp_end = 0;
      if (vars->search(temp,index) == False)
      {
        vars->insert(strdup(temp));
      }
      *temp_end = c;
      tmp = temp_end+1;
    }
  }
}

void WriteMake(char *outname,int argc,char *argv[])
{
  char *dir,*fname,*ext,*name;
  FILE *f;
  TStringCollection *vars = new TStringCollection(10,10);
  int i;
  if (!outname)
  {
    split_fname(project_name,dir,fname,ext);
    string_dup(name,dir);
    string_cat(name,fname);
    string_cat(name,".mak");
  }
  else
  {
    string_dup(name,outname);
  }
  if (strcmp(name,"-") == 0) f = stdout;
  else f = fopen(name,"w+t");
  fprintf(f,"# This file is automaticaly generated by %s\n",IDEVersion);
  if (argc > 0)
  {
    int i;
    fprintf(f,"# created with the command:\n#");
    for (i=0;i<argc;i++)
    {
      fprintf(f," %s",argv[i]);
    }
    fprintf(f,"\n");
  }
  else
  {
    fprintf(f,"# created from within RHIDE\n");
  }
  check_vars(vars,Options.SrcDirs);
  check_vars(vars,Options.ObjDirs);
  check_vars(vars,Options.include_path);
  check_vars(vars,Options.library_path);
  for (i=0;i<vars->getCount();i++)
  {
    char *var,*env;
    var = (char *)vars->at(i);
    env = getenv(var);
    fprintf(f,"ifeq ($(strip $(%s)),)\n",var);
    fprintf(f,"%s=",var);
    if (env) fprintf(f,"%s",env);
    fprintf(f,"\nendif\n");
  }
  destroy(vars);
  if (Project.dependencies)
  {
    for (i=0;i<Project.dependencies->getCount();i++)
    {
      TDependency *_dep = (TDependency *)Project.dependencies->at(i);
      if (_dep->compile_id == COMPILE_PROJECT)
      {
        char *dir,*name,*ext;
        split_fname(FName(_dep->dest_name),dir,name,ext);
        if (*dir)
          dir[strlen(dir)-1] = 0;
        else
          dir = string_dup(".");
        fprintf(f,"%s%s.dir=%s\n",name,ext,dir);
        string_free(dir);
        string_free(name);
        string_free(ext);
      }
    }
  }
  if (Options.SrcDirs->getCount() > 0)
  {
    fprintf(f,"vpath_src=");
    for (i=0;i<Options.SrcDirs->getCount();i++)
    {
      if (i>0) fprintf(f," ");
      fprintf(f,"%s",((char *)Options.SrcDirs->at(i)));
    }
    fprintf(f,"\n");
#define VS(suffix) fprintf(f,"vpath %c"#suffix" $(vpath_src)\n",'%')
    VS(.c);
    VS(.cc);
    VS(.cpp);
    VS(.C);
    VS(.cxx);
    VS(.s);
    VS(.S);
    VS(.p);
    VS(.pas);
    VS(.f);
    VS(.for);
    VS(.F);
    VS(.fpp);
    VS(.i);
    VS(.ii);
    VS(.m);
    VS(.asm);
    VS(.nsm);
#undef VS
  }
  if (Options.ObjDirs->getCount() > 0)
  {
    fprintf(f,"vpath_obj=");
    for (i=0;i<Options.ObjDirs->getCount();i++)
    {
      if (i>0) fprintf(f," ");
      fprintf(f,"%s",((char *)Options.ObjDirs->at(i)));
    }
    fprintf(f,"\n");
    fprintf(f,"vpath %c.o $(vpath_obj)\n",'%');
  }
  WriteSpecData(f);
  fprintf(f,"all: %s\n",FName(Project.dest_name));
  WriteTargets(f);
  fprintf(f,"\n");
  fprintf(f,"force:\n\n");
  fclose(f);
  string_free(dir);
  string_free(fname);
  string_free(ext);
  string_free(name);
}

