// This file is part of the AspectC++ compiler 'ac++'.
// Copyright (C) 1999-2003  The 'ac++' developers (see aspectc.org)
//                                                                
// This program 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 2 of 
// the License, or (at your option) any later version.            
//                                                                
// This program 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 this program; if not, write to the Free     
// Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, 
// MA  02111-1307  USA                                            

#include "ProjectFileManager.h"

#include "Puma/SysCall.h"
#include <fstream>

void ProjectFileManager::addPath(const char *source, const char *destination) {
  _manager.addPath(source, destination);
} 

const char *ProjectFileManager::src(long n) const {
  return _manager.src(n);
}

const char *ProjectFileManager::dest(long n) const {
  return _manager.dest(n);
}

bool ProjectFileManager::isNewer (const char *file) const {
  Puma::FileInfo fileinfo;
  if (! Puma::SysCall::stat (file, fileinfo))
    return false; // File does not exists.
  long last_modified = fileinfo.modi_time ();

  // determine the destination path of the file
  Puma::ProjectFile::MapConstIter iter;
  if (!_manager.isBelow (file, iter)) {
    assert (false); // if we came here, the file should be registered
    return false;
  }

  Puma::ProjectFile &project_file = (Puma::ProjectFile&)iter->second;
  Puma::Filename dest = project_file.dest ();
  if (!dest.is_defined ()) {
    // determine the destination path
    std::ostringstream path;
    if (!getDestinationPath (file, path))
      return false;
    std::string dest_path = path.str ();
    project_file.dest (dest_path.c_str ());
    dest = project_file.dest ();
  }

  bool newer = true;
  if (Puma::SysCall::stat (dest.name (), fileinfo))
    if (last_modified <= fileinfo.modi_time ())
      newer = false;

  return newer;
}

bool ProjectFileManager::make_dir_hierarchy (Puma::Filename path) const {

  // Remember where we are.
  char cwd[PATH_MAX];
  if (Puma::SysCall::getcwd (cwd, PATH_MAX, &_err) == 0)
    return false;

  // Change into the directory and create every missing one
  char *path_copy = Puma::StrCol::dup (path.name ());
  char *p = path_copy;

  // TODO: here is a problem with different drives on Windows!!!
  if (path.is_absolute ()) {
    Puma::SysCall::chdir (path.root (), &_err);
    p += strlen (path.root ());
  }

  char *curr = p;
  while (*p != '\0') {
    if (*p == '/') {
      *p = '\0';
      if (strlen (curr) && ! make_dir (curr)) {
        delete[] path_copy;
        return false;
      }
      curr = p + 1;
    }
    p++;
  }

  delete[] path_copy;

  // Go back
  Puma::SysCall::chdir (cwd, &_err);

  return true;
}

// TODO: get rid of error stream and error message
bool ProjectFileManager::make_dir (const char *directory) const {
  while (!Puma::SysCall::chdir (directory))
    if (! Puma::SysCall::mkdir (directory, &_err)) {
      _err << Puma::sev_error << "unable to create directory \""
             << directory << "\"." << Puma::endMessage;
      return false;
    }
  return true;
}

// save an opened file to its destination
void ProjectFileManager::save (const char *name, bool is_modified, const char *content) const {

  // Do not write files to protected paths or files from outside the
  // source directories.
  if (_manager.isProtected (name))
    return;

  // determine the destination path of the file
  MapConstIter iter;
  if (!isBelow (name, iter)) {
    assert (false); // if we came here, the file should be registered
    return;
  }

  Puma::ProjectFile &project_file = (Puma::ProjectFile&)iter->second;
  Puma::Filename dest = project_file.dest ();
  if (!dest.is_defined ()) {
    // determine the destination path
    std::ostringstream path;
    if (!getDestinationPath (name, path))
      return;
    std::string dest_path = path.str ();
    project_file.dest (dest_path.c_str ());
    dest = project_file.dest ();
  }

  // make sure that the directory for the file exists
  if (!make_dir_hierarchy (dest))
    return;

  // Check whether the file has to be updated.
  if (isNewer(name) || is_modified) {
    // Write the file to disk.
    char *file = (char*)dest.name ();
    std::ofstream out (file, std::ios::out);
    out << content;
  }

}
