

#include "tsystem.h"

#include <time.h>
#include <sys/timeb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <set>

#undef PLATFORM

#ifdef WIN32
#define PLATFORM WIN32
#include <process.h>
#include <io.h>
#include <stdlib.h>
#include <direct.h>
#include "winsock2.h"
#include "lmcons.h"
#include <sys/utime.h>
#endif

#ifdef LINUX
#define PLATFORM LINUX
#include <grp.h>
#include <utime.h>
#include <sys/param.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/dir.h>
#include <sys/sysinfo.h>
#include <sys/swap.h>
#include <sys/statfs.h>
#include <pwd.h>
#include <mntent.h>
#include <dlfcn.h>

extern "C" {
int gethostname(char *, size_t);
// int L_cuserid;
char *cuserid(char *);
char *tempnam(const char *, const char *);
}

#endif

#ifdef __sgi
#define PLATFORM SGI
#include <sys/param.h>
#include <unistd.h>
#include <grp.h>
#include <sys/dir.h>  // dirent.h
#include <sys/utime.h>
#include <sys/swap.h>
#include <sys/statfs.h>
#include <pwd.h>
#include <mntent.h>

#include <dlfcn.h>

#endif

#ifndef PLATFORM
PLATFORM_NOT_SUPPORTED
#endif

using namespace std;

#ifdef WIN32
const int TSystem::MaxPathLen     = _MAX_PATH;
const int TSystem::MaxFNameLen    = _MAX_FNAME;
const int TSystem::MaxHostNameLen = 64;

const unsigned short TFileStatus::UserReadable   = _S_IREAD;
const unsigned short TFileStatus::UserWritable   = _S_IWRITE;
const unsigned short TFileStatus::UserExecutable = _S_IEXEC;
const unsigned short TFileStatus::OwnerReadWriteExec =
    TFileStatus::UserReadable | TFileStatus::UserWritable |
    TFileStatus::UserExecutable;
const unsigned short TFileStatus::OwnerReadable   = TFileStatus::UserReadable;
const unsigned short TFileStatus::OwnerWritable   = TFileStatus::UserWritable;
const unsigned short TFileStatus::OwnerExecutable = TFileStatus::UserExecutable;
const unsigned short TFileStatus::GroupReadWriteExec =
    TFileStatus::OwnerReadWriteExec;
const unsigned short TFileStatus::GroupReadable   = TFileStatus::UserReadable;
const unsigned short TFileStatus::GroupWritable   = TFileStatus::UserWritable;
const unsigned short TFileStatus::GroupExecutable = TFileStatus::UserExecutable;
const unsigned short TFileStatus::OtherReadWriteExec =
    TFileStatus::UserReadable | TFileStatus::UserWritable |
    TFileStatus::UserExecutable;
;
const unsigned short TFileStatus::OtherReadable   = TFileStatus::UserReadable;
const unsigned short TFileStatus::OtherWritable   = TFileStatus::UserWritable;
const unsigned short TFileStatus::OtherExecutable = TFileStatus::UserReadable;

const unsigned short TFileStatus::IfDir = _S_IFDIR;

#define MKDIR _mkdir
#define RMDIR _rmdir
#define TEMPNAM _tempnam
#define ACCESS _access
#define STAT _stat
#define UTIME _utime
#define FTIME _ftime
using std::ifstream;
using std::ofstream;
using std::filebuf;
#else  // these are common for IRIX & LINUX
const int TSystem::MaxPathLen     = MAXPATHLEN;
const int TSystem::MaxHostNameLen = MAXHOSTNAMELEN;

const unsigned short TFileStatus::OwnerReadWriteExec = S_IRWXU;
const unsigned short TFileStatus::OwnerReadable      = S_IRUSR;
const unsigned short TFileStatus::OwnerWritable      = S_IWUSR;
const unsigned short TFileStatus::OwnerExecutable    = S_IXUSR;
const unsigned short TFileStatus::GroupReadWriteExec = S_IRWXG;
const unsigned short TFileStatus::GroupReadable      = S_IRGRP;
const unsigned short TFileStatus::GroupWritable      = S_IWGRP;
const unsigned short TFileStatus::GroupExecutable    = S_IXGRP;
const unsigned short TFileStatus::OtherReadWriteExec = S_IRWXO;
const unsigned short TFileStatus::OtherReadable      = S_IROTH;
const unsigned short TFileStatus::OtherWritable      = S_IWOTH;
const unsigned short TFileStatus::OtherExecutable    = S_IXOTH;

#define MKDIR mkdir
#define RMDIR rmdir
#define TEMPNAM tempnam
#define ACCESS access
#define STAT stat
#define UTIME utime
#define FTIME ftime

#ifdef __sgi
const int TSystem::MaxFNameLen                       = MAXNAMELEN;
const unsigned short TFileStatus::UserReadable       = S_IREAD;
const unsigned short TFileStatus::UserWritable       = S_IWRITE;
const unsigned short TFileStatus::UserExecutable     = S_IEXEC;
const unsigned short TFileStatus::IfDir              = S_IFDIR;
#else
const int TSystem::MaxFNameLen                   = 1024;
const unsigned short TFileStatus::UserReadable   = __S_IREAD;
const unsigned short TFileStatus::UserWritable   = __S_IWRITE;
const unsigned short TFileStatus::UserExecutable = __S_IEXEC;
const unsigned short TFileStatus::IfDir          = __S_IFDIR;
#endif

#endif

//-----------------------------------------------------------------------------------

ostream &operator<<(ostream &out, const TSystemException &e) {
  return out << "TSystem Exception : " << e.getMessage() << endl;
}

//-----------------------------------------------------------------------------------
// conversion functions
namespace {

//---------------------------------------------------------

void makeTm(const TTime &t, tm *p) {
  ::memset(p, 0, sizeof(tm));
  p->tm_year  = t.getYear() - 1900;
  p->tm_mon   = t.getMonth() - 1;  //[0,11]
  p->tm_mday  = t.getDay();
  p->tm_hour  = t.getHour();
  p->tm_min   = t.getMinutes();
  p->tm_sec   = t.getSeconds();
  p->tm_isdst = -1;
}

//---------------------------------------------------------

TTime makeTTime(const tm &p, int msec = 0) {
  return TTime(p.tm_year + 1900, p.tm_mon + 1, p.tm_mday, p.tm_hour, p.tm_min,
               p.tm_sec, msec);
}

//---------------------------------------------------------

time_t makeTimeT(const TTime &t) {
  tm x;
  makeTm(t, &x);
  time_t tt = mktime(&x);
  return tt;
}

}  // namespace

//-----------------------------------------------------------------------------------

TDeltaTime::TDeltaTime(long sec, long msec) {
  // assert((sec && msec>=0) ||(sec==0 ));
  assert(!((sec < 0 && msec > 0) || (sec > 0 && msec < 0)));

  /*
0   0   ok
0   1   ok
1   0   ok
1   1   ok
0  -1   ok
-1   0   ok
-1  -1   ok
-1   1   NO
1  -1   NO
*/

  m_dsec  = sec + msec / 1000;
  m_dmsec = msec % 1000;
}

//------------------------------------------------------------

TDeltaTime TDeltaTime::operator+(const TDeltaTime &tdt) {
  long sumSec  = m_dsec + tdt.m_dsec;
  long sumMsec = m_dmsec + tdt.m_dmsec;

  if (sumSec > 0 && sumMsec < 0) return TDeltaTime(sumSec - 1, sumMsec + 1000);

  if (sumSec < 0 && sumMsec > 0) return TDeltaTime(sumSec + 1, sumMsec - 1000);

  return TDeltaTime(sumSec, sumMsec);
}

//------------------------------------------------------------

TDeltaTime TDeltaTime::operator-(const TDeltaTime &tdt) {
  long diffSec  = m_dsec - tdt.m_dsec;
  long diffMsec = m_dmsec - tdt.m_dmsec;

  if (diffSec < 0 && diffMsec > 0)
    return TDeltaTime(diffSec + 1, diffMsec - 1000);

  if (diffSec > 0 && diffMsec < 0)
    return TDeltaTime(diffSec - 1, diffMsec + 1000);

  return TDeltaTime(diffSec, diffMsec);
}

//------------------------------------------------------------

ostream &operator<<(ostream &out, const TDeltaTime &dt) {
  return out << dt.getSeconds() << "." << dt.getMilliSeconds();
}

//-----------------------------------------------------------------------------------

// correggere gli assert => invalid (esiste il metodo bool isValid()const;
// operazioni con
// un TTIme invalid danno un risultato invalid
//
//
TTime::TTime(int yyyy, int mm, int dd, int hh, int min, int sec, int msec) {
  if (yyyy < 10)
    yyyy += 2000;
  else if (yyyy < 99)
    yyyy += 1900;
  assert(1970 <= yyyy && yyyy <= 2038);
  m_y = yyyy;
  assert(1 <= mm && mm <= 12);
  m_m = mm;

  int table[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  int ddlimit = mm == 2 ? (28 + (isLeap() ? 1 : 0)) : table[mm - 1];
  assert(1 <= dd && dd <= ddlimit);
  m_d = dd;

  assert(0 <= hh && hh <= 23);
  m_h = hh;

  assert(0 <= min && min <= 59);
  m_min = min;

  assert(0 <= sec && sec <= 59);
  m_sec = sec;

  assert(0 <= msec && msec <= 999);
  m_msec = msec;
}

//-----------------------------------------------------------------------------------

TTime::TTime(const TTime &t)
    : m_y(t.m_y)
    , m_m(t.m_m)
    , m_d(t.m_d)
    , m_h(t.m_h)
    , m_min(t.m_min)
    , m_sec(t.m_sec)
    , m_msec(t.m_msec) {}

//-----------------------------------------------------------------------------------

bool TTime::operator==(const TTime &time) const {
  return (m_y == time.m_y) && (m_m == time.m_m) && (m_d == time.m_d) &&
         (m_h == time.m_h) && (m_min == time.m_min) && (m_sec == time.m_sec) &&
         (m_msec == time.m_msec);
}

//-----------------------------------------------------------------------------------

bool TTime::operator>(const TTime &time) const {
  return (m_y > time.m_y) || (m_y == time.m_y && m_m > time.m_m) ||
         (m_y == time.m_y && m_m == time.m_m && m_d > time.m_d) ||
         (m_y == time.m_y && m_m == time.m_m && m_d == time.m_d &&
          m_h > time.m_h) ||
         (m_y == time.m_y && m_m == time.m_m && m_d == time.m_d &&
          m_h == time.m_h && m_min > time.m_min) ||
         (m_y == time.m_y && m_m == time.m_m && m_d == time.m_d &&
          m_h == time.m_h && m_min == time.m_min && m_sec > time.m_sec) ||
         (m_y == time.m_y && m_m == time.m_m && m_d == time.m_d &&
          m_h == time.m_h && m_min == time.m_min && m_sec == time.m_sec &&
          m_msec > time.m_msec);
}

//-----------------------------------------------------------------------------------

TDeltaTime TTime::operator-(const TTime &t) const {
  time_t t2 = makeTimeT(*this);
  assert(t2 != -1);
  time_t t1 = makeTimeT(t);
  assert(t1 != -1);
  double d = difftime(t2, t1);
  if (m_msec >= t.m_msec) {
    if (d <= -1)
      return TDeltaTime((long)(d + 1), -(long)(1000 - (m_msec - t.m_msec)));
    else
      return TDeltaTime((long)d, (long)(m_msec - t.m_msec));
  } else {
    if (d <= 0)
      return TDeltaTime((long)(d), (long)(m_msec - t.m_msec));
    else
      return TDeltaTime((long)(d - 1), (long)(1000 + m_msec - t.m_msec));
  }
}

//-----------------------------------------------------------------------------------

TTime TTime::operator+(const TDeltaTime &tdt) const {
  tm x;
  makeTm(*this, &x);
  int ms = m_msec + tdt.getMilliSeconds();
  if (ms < 0) {
    ms += 1000;
    x.tm_sec += tdt.getSeconds() - 1;
  } else
    x.tm_sec += tdt.getSeconds() + ms / 1000;

  mktime(&x);
  return makeTTime(x, ms % 1000);
}

//-----------------------------------------------------------------------------------

TTime TTime::operator-(const TDeltaTime &tdt) const {
  tm x;
  makeTm(*this, &x);
  int ms = m_msec - tdt.getMilliSeconds();
  if (ms < 0) {
    ms += 1000;
    x.tm_sec -= tdt.getSeconds() + 1;
  } else
    x.tm_sec -= tdt.getSeconds() + ms / 1000;

  mktime(&x);
  return makeTTime(x, ms % 1000);
}
//-----------------------------------------------------------------------------------

string TTime::getFormattedString(char *fmt) const {
  tm x;
  makeTm(*this, &x);
  char strDest[256];
  if (!fmt)
    // fmt="%b %d %Y %X";
    return getDate() + " " + getTime();
  strftime(strDest, 256, fmt, &x);
  return string(strDest);
}

//-----------------------------------------------------------------------------------

string TTime::getDate() const { return getFormattedString("%b %d %Y"); }

//-----------------------------------------------------------------------------------

string TTime::getTime() const {  // hh:mm:ss
  stringstream buff_s;
  buff_s << "." << m_msec << '\0';
  return getFormattedString("%X") + buff_s.str();
}

//-----------------------------------------------------------------------------------

TTime TTime::floor() const {
  return TTime(m_y, m_m, m_d, m_h, m_min, m_sec, 0);
}

//-----------------------------------------------------------------------------------

TTime TTime::ceil() const {
  if (m_msec == 0)
    return *this;
  else
    return *this + TDeltaTime(0, 1000 - m_msec);
}

//===================================================================================

TFileStatus::TFileStatus(const TFilePath &path) {
  int acc = ACCESS(path.getFullPath().c_str(), 00);  // 00 == solo esistenza
  m_exist = acc != -1;
  // gestire eccezioni controllando il valore di ritorno di access
  // controllare il valore di errno
  // int ret=
  STAT(path.getFullPath().c_str(),
       &m_fStatus);  // returns 0 if the file-status information is obtained
  /* if(ret!=0) ::memset(&m_fStatus,0,sizeof(m_fStatus)):*/
}

//-----------------------------------------------------------------------------------

string TFileStatus::getGroup() const {
#ifndef WIN32
  struct group *grp = getgrgid(m_fStatus.st_gid);
  if (grp) return string(grp->gr_name);
#endif
  stringstream buff;
  buff << m_fStatus.st_gid;
  return buff.str();
}

//-----------------------------------------------------------------------------------

string TFileStatus::getUser() const {
#ifndef WIN32
  struct passwd *pw = getpwuid(m_fStatus.st_uid);
  if (pw) return string(pw->pw_name);
#endif
  stringstream buff;
  buff << m_fStatus.st_uid;
  return buff.str();
}

//-----------------------------------------------------------------------------------

long TFileStatus::getSize() const { return (long)m_fStatus.st_size; }

//-----------------------------------------------------------------------------------

TTime TFileStatus::getLastAccessTime() const {
  struct tm *t = localtime(&(m_fStatus.st_atime));
  return TTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour,
               t->tm_min, t->tm_sec);
}

//-----------------------------------------------------------------------------------

TTime TFileStatus::getLastModificationTime() const {
  struct tm *t = localtime(&(m_fStatus.st_mtime));
  return TTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour,
               t->tm_min, t->tm_sec);
}

//-----------------------------------------------------------------------------------

TTime TFileStatus::getCreationTime() const {
  struct tm *t = localtime(&(m_fStatus.st_ctime));
  return TTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour,
               t->tm_min, t->tm_sec);
}

//-----------------------------------------------------------------------------------

unsigned long TFileStatus::getPermissions() const { return m_fStatus.st_mode; }

//-----------------------------------------------------------------------------------

bool TFileStatus::isDirectory() const {
  return doesExist() && (getPermissions() & TFileStatus::IfDir) != 0;
}

//-----------------------------------------------------------------------------------

string TSystem::getHostName() {
  char hostName[MaxHostNameLen];
#ifdef WIN32
  WSADATA wdata;
  int err;
  err = WSAStartup(MAKEWORD(1, 1), &wdata);
  if (!err) strcpy(hostName, "localhost");  // la rete non e' partita  ?!?
#endif
  gethostname((char *)&hostName, MaxHostNameLen);
#ifdef WIN32
  if (err) WSACleanup();
#endif

  return hostName;
}

//------------------------------------------------------------

string TSystem::getUserName() {
#ifdef WIN32
#ifndef UNLEN
#define UNLEN (256)
#endif
  const long bufSize = UNLEN;
#else
  const long bufSize                                 = L_cuserid;
#endif

  char userName[bufSize + 1];

#ifdef WIN32
  GetUserName((char *)&userName, (unsigned long *)(&bufSize));
#else
  cuserid(userName);
  if (userName[0] == 0x00) strcpy(userName, "User Unknown");
#endif
  return string(userName);
}

//------------------------------------------------------------

string TSystem::getGroupName() {
#ifdef WIN32

  return string();

#else
  struct group *currentGroup;
  currentGroup = getgrgid(getgid());
  if (currentGroup)
    return string(currentGroup->gr_name);
  else
    return string();
#endif
}

//------------------------------------------------------------

int TSystem::getProcessId() { return getpid(); }

//------------------------------------------------------------

TFilePath TSystem::getHomeDirectory() {
#ifndef WIN32
  struct passwd *pwd = getpwnam(getUserName().c_str());
  if (!pwd) {
    return TFilePath();
  }
  return TFilePath(pwd->pw_dir);
#else
  char *s = getenv("USERPROFILE");
  if (!s)
    return TFilePath();
  else
    return TFilePath(s);
#endif
}

//------------------------------------------------------------

TFilePath TSystem::getTempDir() {
#ifdef WIN32
  // gestire eccezioni se dw==0
  DWORD dw   = GetTempPath(0, 0);  // non include il terminatore
  char *path = new char[dw + 1];
  GetTempPath(dw, path);
  TFilePath tempDir(path);
  delete[] path;
  return tempDir;
#else
  return TFilePath(tmpnam(0)).getParentDir();
#endif
}

//------------------------------------------------------------

TFilePath TSystem::getTestDir() {
#ifdef WIN32
  return TFilePath("\\\\sirio\\toonz5.0\\TNZCORE_TEST");
#else
  return TFilePath("/ULTRA/toonz5.0/TNZCORE_TEST");
#endif
}

//------------------------------------------------------------

string TSystem::getSystemValue(const TFilePath &name) {
#ifdef WIN32

  string keyName = name.getParentDir().getFullPath();
  string varName = name.getName();

  HKEY hkey;
  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName.c_str(), 0, KEY_READ, &hkey) !=
      ERROR_SUCCESS) {
    /*key not defined*/
    return "";
  }
  unsigned char buffer[1024];
  unsigned long bufferSize = sizeof(buffer);

  string value;
  if (RegQueryValueEx(hkey, varName.c_str(), NULL, NULL, buffer, &bufferSize) ==
      ERROR_SUCCESS)
    value = string((char *)buffer);

  RegCloseKey(hkey);

  return value;

#else
  char *s = getenv(name.getFullPath().c_str());
  return string(s ? s : "");
#endif
}

//------------------------------------------------------------

TFilePath TSystem::getBinDir() {
#ifdef WIN32
  char buf[MaxPathLen];
  GetModuleFileName(0, buf, MaxPathLen);
  return TFilePath(buf).getParentDir();
#else
  string binroot = TSystem::getSystemValue("BINROOT");
  if (binroot == "") {
    assert(!"BINROOT variable undefined");
  }
  return TFilePath(binroot) + "bin";
#endif
}

//------------------------------------------------------------

TFilePath TSystem::getUniqueFile(string field) {
  char *tempName = TEMPNAM(getTempDir().getFullPath().c_str(), field.c_str());
  if (tempName == 0)
    throw TSystemException(TFilePath(getTempDir().getFullPath() + field), 1);
  TFilePath outPath(tempName);
  free(tempName);
  return outPath;
}

//------------------------------------------------------------

const TDeltaTime TDeltaTime::second = TDeltaTime(1, 0);
const TDeltaTime TDeltaTime::minute = 60 * TDeltaTime::second;
const TDeltaTime TDeltaTime::hour   = 60 * TDeltaTime::minute;
const TDeltaTime TDeltaTime::day    = 24 * TDeltaTime::hour;

//------------------------------------------------------------

TTime TSystem::getCurrentTime() {
#ifdef WIN32
  struct _timeb curTime;
#else
  struct timeb curTime;
#endif
  FTIME(&curTime);
  struct tm *localTime = localtime(&(curTime.time));

  return TTime(localTime->tm_year + 1900, localTime->tm_mon + 1,
               localTime->tm_mday, localTime->tm_hour, localTime->tm_min,
               localTime->tm_sec, curTime.millitm);
}

//------------------------------------------------------------
// gestire exception
void TSystem::mkDir(const TFilePath &path) {
  if (path == "") {
    throw TSystemException("mkdir: Empty argument");
  }

  if (TFileStatus(path).doesExist() || path.isRoot()) return;
  while (!TFileStatus(path.getParentDir()).doesExist() &&
         !path.getParentDir().isRoot()) {
    mkDir(path.getParentDir());
  }
#ifdef WIN32
  int ret = MKDIR(path.getFullPath().c_str());  // ret e' EEXIST o ENOENT
#else
  umask(0);
  mode_t attr = TFileStatus::UserReadable | TFileStatus::UserWritable |
                TFileStatus::UserExecutable | TFileStatus::OwnerReadWriteExec |
                TFileStatus::GroupReadWriteExec |
                TFileStatus::OtherReadWriteExec;
  int ret = MKDIR(path.getFullPath().c_str(), attr);  // ret e` EEXIST o ENOENT
#endif
  if (ret == -1) throw TSystemException(path, errno);
}

//------------------------------------------------------------
// gestire exception
void TSystem::rmDir(const TFilePath &path) {
  //  if (!TFileStatus(path).doesExist())
  //	return;
  int ret = RMDIR(path.getFullPath().c_str());
  if (ret == -1)  // ret e' ENOTEMPTY o ENOENT
    throw TSystemException(path, errno);
}

//------------------------------------------------------------

void TSystem::rmDirTree(const TFilePath &path) {
  TFilePathSet pathSet = readDirectory(path);
  for (TFilePathSet::iterator it = pathSet.begin(); it != pathSet.end(); it++) {
    TFilePath path = *it;
    if (TFileStatus(path).isDirectory())
      rmDirTree(path);
    else
      deleteFile(path);
  }
  rmDir(path);
}

//------------------------------------------------------------

void TSystem::copyDir(const TFilePath &dst, const TFilePath &src) {
  if (!TFileStatus(dst).doesExist()) mkDir(dst);

  TFilePathSet pathSet = readDirectory(src);
  for (TFilePathSet::iterator it = pathSet.begin(); it != pathSet.end(); it++) {
    TFilePath path = *it;
    if (TFileStatus(path).isDirectory())
      copyDir(path.withParentDir(dst), path);
    else
      copyFile(path.withParentDir(dst), path);
  }
}

//------------------------------------------------------------

void TSystem::touchFile(const TFilePath &path) {
  string filename = path.getFullPath();
  if (TFileStatus(path).doesExist()) {
    if (0 != UTIME(filename.c_str(), 0)) throw TSystemException(path, errno);
  } else {
    ofstream file(filename.c_str());
    if (!file) {
      throw TSystemException(path, errno);
    }
    file.close();  // altrimenti il compilatore da' un warning:
                   // variabile non utilizzata
  }
}

//------------------------------------------------------------

#ifdef WIN32

namespace {

//------------------------------------------------------------

string getFormattedMessage(DWORD lastError) {
  LPVOID lpMsgBuf;
  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
                    FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL, lastError,
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),  // Default language
                (LPTSTR)&lpMsgBuf, 0, NULL);
  return (char *)lpMsgBuf;
}

//------------------------------------------------------------

}  // namespace
#endif
//------------------------------------------------------------

void TSystem::copyFile(const TFilePath &dst, const TFilePath &src) {
  assert(dst != src);
#ifdef WIN32
  // o e' meglio usare CopyFileEx??
  BOOL res = CopyFile(
      src.getFullPath().c_str(),  // pointer to name of an existing file
      dst.getFullPath().c_str(),  // pointer to filename to copy to
      TRUE);                      // flag for operation if file exists
  if (res == 0) {
    throw TSystemException(src, getFormattedMessage(GetLastError()));
  }
#else
  ifstream fpin(src.getFullPath().c_str(), ios::in);
  ofstream fpout(dst.getFullPath().c_str(), ios::out);
  if (!fpin || !fpout) throw TSystemException(src, "unable to copy file");
  int c = fpin.get();
  while (!fpin.eof()) {
    fpout.put(c);
    c = fpin.get();
  }
#endif
}

//------------------------------------------------------------

void TSystem::renameFile(const TFilePath &dst, const TFilePath &src) {
  int ret = rename(src.getFullPath().c_str(), dst.getFullPath().c_str());
  if (ret != 0) throw TSystemException(dst, errno);
}

//------------------------------------------------------------

// gestire gli errori con GetLastError?
void TSystem::deleteFile(const TFilePath &dst) {
#ifdef WIN32
  BOOL res = DeleteFile(
      dst.getFullPath().c_str());  // pointer to name of file to delete
  // se fallisce perche' un file e' aperto chiudiamo prima il file?
  // To close an open file, use the CloseHandle function.
  if (res == 0)
    throw TSystemException(dst, getFormattedMessage(GetLastError()));
#else
  int ret = remove(dst.getFullPath().c_str());
  if (ret != 0) throw TSystemException(dst, errno);
#endif
}

//------------------------------------------------------------

void TSystem::moveFileToRecycleBin(const TFilePath &fp) {
#ifdef WIN32
  //
  // from http://msdn.microsoft.com/msdnmag/issues/01/04/c/default.aspx
  //

  // Copy pathname to double-NULL-terminated string.
  //
  char buf[_MAX_PATH + 1];                // allow one more character
  strcpy(buf, fp.getFullPath().c_str());  // copy caller's path name
  buf[strlen(buf) + 1] = 0;               // need two NULLs at end

  SHFILEOPSTRUCT data;
  memset(&data, 0, sizeof(SHFILEOPSTRUCT));
  data.fFlags |= FOF_SILENT;          // don't report progress
  data.fFlags |= FOF_NOERRORUI;       // don't report errors
  data.fFlags |= FOF_NOCONFIRMATION;  // don't confirm delete

  data.wFunc = FO_DELETE;        // REQUIRED: delete operation
  data.pFrom = buf;              // REQUIRED: which file(s)
  data.pTo   = NULL;             // MUST be NULL
  data.fFlags |= FOF_ALLOWUNDO;  // ..send to Recycle Bin
  SHFileOperation(&data);        // do it!
#else
  assert(!"Not implemented yet");
#endif
}

//------------------------------------------------------------

void TSystem::readDirectory(TFilePathSet &dst, const TFilePath &path) {
  if (!TFileStatus(path).isDirectory()) {
    throw TSystemException(path, " is not a directory");
  }
#ifdef WIN32
  WIN32_FIND_DATA findFileData;
  string search = (path + "*").getFullPath();
  HANDLE h      = FindFirstFile(search.c_str(), &findFileData);
  if (h == INVALID_HANDLE_VALUE)
    throw TSystemException(path, getFormattedMessage(GetLastError()));
  do {
    string filename(findFileData.cFileName);
    if (filename == "." || filename == "..") continue;
    TFilePath son = path + filename;
    dst.push_back(son);
  } while (FindNextFile(h, &findFileData));
  FindClose(h);
#else
  DIR *dirp;
  struct direct *directp;
  dirp = opendir(path.getFullPath().c_str());
  if (dirp == 0) throw TSystemException(path, errno);
  while (directp = readdir(dirp)) {
    string filename(directp->d_name);
    if (filename == "." || filename == "..") continue;
    TFilePath son = path + filename;
    dst.push_back(son);
  }
  closedir(dirp);
#endif
}

//------------------------------------------------------------

void TSystem::readDirectory(TFilePathSet &dst, const TFilePathSet &pathSet) {
  for (TFilePathSet::const_iterator it = pathSet.begin(); it != pathSet.end();
       it++)
    readDirectory(dst, *it);
}

//------------------------------------------------------------

TFilePathSet TSystem::readDirectory(const TFilePath &path) {
  TFilePathSet filePathSet;
  readDirectory(filePathSet, path);
  return filePathSet;
}

//------------------------------------------------------------

TFilePathSet TSystem::readDirectory(const TFilePathSet &pathSet) {
  TFilePathSet dst;
  readDirectory(dst, pathSet);
  return dst;
}

//------------------------------------------------------------

void TSystem::readDirectoryTree(TFilePathSet &dst, const TFilePath &path) {
  if (!TFileStatus(path).isDirectory()) {
    throw TSystemException(path, " is not a directory");
  }
#ifdef WIN32
  WIN32_FIND_DATA findFileData;
  string search = (path + "*").getFullPath();
  HANDLE h      = FindFirstFile(search.c_str(), &findFileData);
  if (h == INVALID_HANDLE_VALUE) return;
  do {
    string filename(findFileData.cFileName);
    if (filename == "." || filename == "..") continue;
    TFilePath son = path + findFileData.cFileName;
    if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
      readDirectoryTree(dst, son);
    else
      dst.push_back(son);
  } while (FindNextFile(h, &findFileData));
  FindClose(h);
#else
  DIR *dirp;
  struct direct *directp;
  dirp = opendir(path.getFullPath().c_str());
  if (dirp == 0) throw TSystemException(path, errno);
  while (directp = readdir(dirp)) {
    string filename(directp->d_name);
    if (filename == "." || filename == "..") continue;
    TFilePath son = path + filename;
    if (TFileStatus(son).isDirectory())
      readDirectoryTree(dst, son);
    else
      dst.push_back(son);
  }
  closedir(dirp);
#endif
}

//------------------------------------------------------------

void TSystem::readDirectoryTree(TFilePathSet &dst,
                                const TFilePathSet &pathSet) {
  for (TFilePathSet::const_iterator it = pathSet.begin(); it != pathSet.end();
       it++)
    readDirectoryTree(dst, *it);
}

//------------------------------------------------------------

TFilePathSet TSystem::readDirectoryTree(const TFilePath &path) {
  TFilePathSet dst;
  readDirectoryTree(dst, path);
  return dst;
}

//------------------------------------------------------------

TFilePathSet TSystem::readDirectoryTree(const TFilePathSet &pathSet) {
  TFilePathSet dst;
  readDirectoryTree(dst, pathSet);
  return dst;
}

//------------------------------------------------------------

TFilePathSet TSystem::packLevelNames(const TFilePathSet &fps) {
  std::set<TFilePath> tmpSet;
  TFilePathSet::const_iterator cit;
  for (cit = fps.begin(); cit != fps.end(); ++cit)
    tmpSet.insert(cit->getParentDir() + cit->getLevelName());

  TFilePathSet fps2;
  for (std::set<TFilePath>::const_iterator c_sit = tmpSet.begin();
       c_sit != tmpSet.end(); ++c_sit) {
    fps2.push_back(*c_sit);
  }
  return fps2;
}

//------------------------------------------------------------

TFilePathSet TSystem::getDisks() {
  TFilePathSet filePathSet;

#ifdef WIN32
  DWORD size   = GetLogicalDriveStrings(0, NULL);
  char *buffer = new char[size + 1];
  char *ptr    = buffer;
  DWORD rc     = GetLogicalDriveStrings(size, buffer);
  if (rc == 0) throw TSystemException("", getFormattedMessage(GetLastError()));

  while (*ptr) {
    filePathSet.push_back(ptr);
    while (*ptr) ptr++;
    ptr++;
  }

  delete[] buffer;
  return filePathSet;
#else
  FILE *f = setmntent("/etc/fstab", "r");
  if (f) {
    while (struct mntent *m = getmntent(f)) {
      // cout << "machine "<< m->mnt_fsname << " dir " <<m->mnt_dir << " type "
      // <<  m->mnt_type << endl;
      filePathSet.push_back(m->mnt_dir);
    }
    endmntent(f);
  }

  return filePathSet;
#endif
}

//------------------------------------------------------------

ULONG TSystem::getDiskSize(const TFilePath &diskName) {
  ULONG size = 0;
  if (!diskName.isAbsolute()) {
    assert(0);
    return 0;
  }
#ifndef WIN32
  struct statfs buf;
#ifdef __sgi
  statfs(diskName.getFullPath().c_str(), &buf, sizeof(struct statfs), 0);
#else
  statfs(diskName.getFullPath().c_str(), &buf);
#endif
  size = (ULONG)((buf.f_blocks * buf.f_bsize) >> 10);
#else
  DWORD sectorsPerCluster;     // sectors per cluster
  DWORD bytesPerSector;        // bytes per sector
  DWORD numberOfFreeClusters;  // free clusters
  DWORD totalNumberOfClusters;

  BOOL rc = GetDiskFreeSpace(diskName.getFullPath().c_str(),  // root path
                             &sectorsPerCluster,     // sectors per cluster
                             &bytesPerSector,        // bytes per sector
                             &numberOfFreeClusters,  // free clusters
                             &totalNumberOfClusters  // total clusters
                             );

  if (!rc)
    throw TSystemException(diskName, getFormattedMessage(GetLastError()));
  else
    size = (totalNumberOfClusters * sectorsPerCluster * bytesPerSector) >> 10;
#endif
  return size;
}

//------------------------------------------------------------

ULONG TSystem::getFreeDiskSize(const TFilePath &diskName) {
  ULONG size = 0;
  if (!diskName.isAbsolute()) {
    assert(0);
    return 0;
  }
#ifndef WIN32
  struct statfs buf;
#ifdef __sgi
  statfs(diskName.getFullPath().c_str(), &buf, sizeof(struct statfs), 0);
#else
  statfs(diskName.getFullPath().c_str(), &buf);
#endif
  size = (ULONG)(buf.f_bfree * buf.f_bsize) >> 10;
#else
  DWORD sectorsPerCluster;     // sectors per cluster
  DWORD bytesPerSector;        // bytes per sector
  DWORD numberOfFreeClusters;  // free clusters
  DWORD totalNumberOfClusters;

  BOOL rc = GetDiskFreeSpace(diskName.getFullPath().c_str(),  // root path
                             &sectorsPerCluster,     // sectors per cluster
                             &bytesPerSector,        // bytes per sector
                             &numberOfFreeClusters,  // free clusters
                             &totalNumberOfClusters  // total clusters
                             );

  if (!rc)  // eccezione... getLastError etc...
    throw TSystemException(diskName, getFormattedMessage(GetLastError()));
  else
    size = (numberOfFreeClusters * sectorsPerCluster * bytesPerSector) >> 10;
#endif
  return size;
}

//------------------------------------------------------------

ULONG TSystem::getFreeMemorySize() {
  ULONG totalFree = 0;

#ifdef WIN32
  ULONG virtualFree  = 0;
  ULONG physicalFree = 0;
  MEMORYSTATUS buff;
  GlobalMemoryStatus(&buff);
  physicalFree = buff.dwAvailPhys;
  virtualFree  = buff.dwAvailVirtual;
  totalFree    = (physicalFree + virtualFree) >> 10;
#else
#ifdef __sgi
  // check for virtual memory
  int numberOfResources =
      swapctl(SC_GETNSWP, 0); /* get number of swapping resources configued */

  if (numberOfResources == 0) return 0;

  // avrei voluto fare: struct swaptable *table = new struct swaptable[...]
  struct swaptable *table = (struct swaptable *)calloc(
      1, sizeof(struct swapent) * numberOfResources + sizeof(int));

  table->swt_n = numberOfResources;
  swapctl(SC_LIST, table); /* list all the swapping resources */

  ULONG virtualFree  = 0;
  ULONG physicalFree = 0;
  for (int i = 0; i < table->swt_n; i++) {
    virtualFree += table->swt_ent[i].ste_free;
  }

  free(table);
  totalFree = virtualFree << 4 + physicalFree;
#else
#ifdef LINUX

  struct sysinfo *sysInfo = (struct sysinfo *)calloc(1, sizeof(struct sysinfo));

  if (!sysinfo(sysInfo)) {
    totalFree = sysInfo->freeram + sysInfo->freeswap;
  } else {
    assert(!"sysinfo function failed");
  }
  free(sysInfo);

#else
  @ @ @ERROR : PLATFORM NOT SUPPORTED
#endif
#endif  //__sgi

#endif  // WIN32

#ifndef WIN32
#else
#endif

  return totalFree;
}

//------------------------------------------------------------

ostream &operator<<(ostream &out, const TTime &t) {
  return out << t.getDate() << " " << t.getTime();
}

//------------------------------------------------------------

#ifdef __sgi
extern "C" long sginap(long ticks);
#else
#ifdef LINUX
extern "C" int usleep(unsigned int);
#endif
#endif

void TSystem::sleep(const TDeltaTime &delay) {
  int ms = delay.getSeconds() * 1000;
  assert(ms >= delay.getSeconds());
  ms += delay.getMilliSeconds();
#ifdef WIN32
  Sleep(ms);
#else
#ifdef __sgi
  sginap(ms * CLK_TCK / 1000);
#else
#ifdef LINUX
  ms *= 1000;
  usleep(ms);
#endif
#endif
#endif
}

//--------------------------------------------------------------

TSystemException::TSystemException(const TFilePath &fname, int err)
    : m_fname(fname.getFullPath())
    , m_err(err)
    , m_msg("")

{}

//--------------------------------------------------------------

TSystemException::TSystemException(const TFilePath &fname, const string &msg)
    : m_fname(fname.getFullPath()), m_err(-1), m_msg(msg) {}

//--------------------------------------------------------------

TSystemException::TSystemException(const string &msg)
    : m_fname(""), m_err(-1), m_msg(msg) {}

//--------------------------------------------------------------

string TSystemException::getMessage() const {
  string msg = m_fname;
  switch (m_err) {
  case -1:
    // nothing
    msg += ": ";
    msg += m_msg;
    CASE EEXIST : msg +=
                  ": Directory was not created because filename is the name of "
                  "an existing file, directory, or device";
    CASE ENOENT : msg +=
                  ": Path was not found, or the named file does not exist or "
                  "is a null pathname.";
    CASE ENOTEMPTY : msg +=
                     ": Given path is not a directory; directory is not empty; "
                     "or directory is either current working directory or root "
                     "directory";
    CASE EACCES : msg +=
                  ": Search permission is denied by a component of the path "
                  "prefix, or write permission on the file named by path is "
                  "denied, or times is NULL, and write access is denied";
    CASE EFAULT : msg +=
                  ": Times is not NULL and, or points outside the process's "
                  "allocated address space.";
    CASE EINTR : msg += ": A signal was caught during the utime system call.";
    CASE ENAMETOOLONG : msg +=
                        ": The length of the path argument exceeds {PATH_MAX}, "
                        "or the length of a path component exceeds {NAME_MAX} "
                        "while _POSIX_NO_TRUNC is in effect.";
    CASE ENOTDIR : msg +=
                   ": A component of the path prefix is not a directory.";
    CASE EPERM : msg +=
                 ": The calling process does not have the super-user "
                 "privilege, the effective user ID is not the owner of the "
                 "file, and times is not NULL, or the file system containing "
                 "the file is mounted read-only";
    CASE EROFS : msg +=
                 ": The current file system level range does not envelop the "
                 "level of the file named by path, and the calling process "
                 "does not have the super-user privilege.";
    CASE ENOSYS : msg +=
                  ": When the named file cannot have its time reset.  The file "
                  "is on a file system that doesn't have this operation.";
    CASE EMFILE
        : msg += ": The maximum number of file descriptors are currently open.";
    CASE ENFILE : msg += ": The system file table is full.";
    CASE EBADF : msg +=
                 ": The file descriptor determined by the DIR stream is no "
                 "longer valid.  This result occurs if the DIR stream has been "
                 "closed.";
    CASE EINVAL
        : msg +=
          ": 64-bit and non-64-bit calls were mixed in a sequence of calls.";
  DEFAULT:
    msg += ": Unknown error";

#ifndef WIN32
    CASE ELOOP
        : msg +=
          ": Too many symbolic links were encountered in translating path.";
    CASE EMULTIHOP : msg +=
                     ": Components of path require hopping to multiple remote "
                     "machines and the file system does not allow it.";
    CASE ENOLINK : msg +=
                   ": Path points to a remote machine and the link to that "
                   "machine is no longer active.";
#ifndef LINUX
    CASE EDIRCORRUPTED : msg += ": The directory is corrupted on disk.";
#endif
    CASE EOVERFLOW : msg +=
                     ": One of the inode number values or offset values did "
                     "not fit in 32 bits, and the 64-bit interfaces were not "
                     "used.";
#endif
  }
  return msg;
}

//--------------------------------------------------------------

static const char *TnzLibMainProcName = "TLibMain";
static std::map<string, const TPluginInfo *> PluginTable;
static std::set<TnzLibMainProcType *> PluginMainTable;

//--------------------------------------------------------------

namespace {

#ifdef WIN32
static std::vector<HINSTANCE> PluginInstanceTable;
#else
static std::vector<void *> PluginInstanceTable;
#endif

extern "C" void unloadPlugins() {
#ifdef WIN32
  for (std::vector<HINSTANCE>::iterator it = PluginInstanceTable.begin();
       it != PluginInstanceTable.end(); ++it)
    FreeLibrary(*it);
#else
  for (std::vector<void *>::iterator it = PluginInstanceTable.begin();
       it != PluginInstanceTable.end(); ++it)
    dlclose(*it);
#endif
  PluginInstanceTable.clear();
}
}

//--------------------------------------------------------------

void TSystem::unLoadPlugins() { unloadPlugins(); }

//--------------------------------------------------------------

void TSystem::loadPlugins(const TFilePath &dir) {
  static bool cbReg = false;
  if (!cbReg) {
    cbReg = true;
    atexit(unloadPlugins);
  }

#ifdef WIN32
  string extension = "dll";
#else
  string extension = "so";
#endif

  TFilePathSet dirContent = TSystem::readDirectory(dir);
  if (dirContent.empty()) return;

  for (TFilePathSet::iterator it = dirContent.begin(); it != dirContent.end();
       it++) {
    TFilePath fp = *it;
    if (fp.getType() != extension) continue;

// cout << "Loading..." << fp << endl;
#ifdef WIN32
    HINSTANCE handle = LoadLibrary(fp.getFullPath().c_str());
#else
    void *handle   = dlopen(fp.getFullPath().c_str(), RTLD_LAZY);
#endif

    if (!handle) {
// non riesce a caricare la libreria;
#ifdef WIN32
      DWORD err = GetLastError();
      string s;
      s = "*Error* unable to load " + fp.getFullPath() + ": " +
          getFormattedMessage(err) + '\0';
      TSystem::outputDebug(s);
#else
      cout << "*ERROR* couldn't load " << fp << ":";
      cout << dlerror() << endl;
#endif
    } else {
      PluginInstanceTable.push_back(handle);
// cout << "loaded" << endl;
#ifdef WIN32
      TnzLibMainProcType *tnzLibMain =
          (TnzLibMainProcType *)GetProcAddress(handle, TnzLibMainProcName);
#else
      TnzLibMainProcType *tnzLibMain =
          (TnzLibMainProcType *)dlsym(handle, TnzLibMainProcName);
#endif
      if (!tnzLibMain) {
        // La libreria non esporta TLibMain;
        // per ora niente messaggi di errore
        // cout<< "Unable to load TLibMain" << endl;
        /*
#ifdef WIN32
FreeLibrary(handle);
#else
dlclose(handle);
#endif
*/
      } else {
        std::set<TnzLibMainProcType *>::iterator it;
        it = PluginMainTable.find(tnzLibMain);
        if (it == PluginMainTable.end()) {
          PluginMainTable.insert(tnzLibMain);
          const TPluginInfo *info = tnzLibMain();
          if (info) {
            PluginTable[info->getName()] = info;
          }
        }
      }  // if(tnzLibMain)
    }    // if(handle)
  }      // for
}
//--------------------------------------------------------------

void TSystem::loadStandardPlugins() {
  static bool alreadyDone = false;
  if (alreadyDone) return;
  alreadyDone          = true;
  TFilePath pluginsDir = TSystem::getBinDir() + "plugins";

  // cout << "loading standard plugins ... " << pluginsDir << endl;

  try {
    TSystem::loadPlugins(pluginsDir + "io");
  } catch (const TException &e) {
#ifdef WIN32
    MessageBox(0, e.getMessage().c_str(), "Error loading plugin", MB_OK);
#else
    cout << e.getMessage() << endl;
#endif
  }

  try {
    TSystem::loadPlugins(pluginsDir + "fx");
  } catch (const TException &e) {
#ifdef WIN32
    MessageBox(0, e.getMessage().c_str(), "Error loading plugin", MB_OK);
#else
    cout << e.getMessage() << endl;
#endif
  }

  // cout << "done ... " << endl;
}

//--------------------------------------------------------------

const TPluginInfo *TSystem::getLoadedPluginInfo(string name) {
  std::map<string, const TPluginInfo *>::iterator it;
  it = PluginTable.find(name);
  if (it == PluginTable.end())
    return 0;
  else
    return it->second;
}

//--------------------------------------------------------------

void TSystem::showDocument(const TFilePath &path) {
#ifdef WIN32
  int ret = (int)ShellExecute(0, "open", path.getFullPath().c_str(), 0, 0,
                              SW_SHOWNORMAL);
  if (ret <= 32) {
    throw TException(path.getFullPath() + " : can't open");
  }
#else
  string cmd = "mediaplayer ";
  cmd        = cmd + path.getFullPath();
  system(cmd.c_str());
#endif
}

//--------------------------------------------------------------

int TSystem::getProcessorCount() {
#ifdef WIN32
  SYSTEM_INFO sysInfo;
  GetSystemInfo(&sysInfo);
  return sysInfo.dwNumberOfProcessors;
#else
#ifdef __sgi
  return sysconf(_SC_NPROC_CONF);
#else
  return sysconf(_SC_NPROCESSORS_CONF);
#endif
#endif
}

//--------------------------------------------------------------

void TSystem::outputDebug(string s) {
#ifdef WIN32
  OutputDebugString(s.c_str());
#else
  cerr << s << endl;
#endif
}
