// ---------------------------------------------------------------------------
// - System.cpp                                                              -
// - standard object library - system function class implementation          -
// ---------------------------------------------------------------------------
// - This program is free software;  you can redistribute it  and/or  modify -
// - it provided that this copyright notice is kept intact.                  -
// -                                                                         -
// - 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.  In no event shall -
// - the copyright holder be liable for any  direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software.     -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2008 amaury darsch                                   -
// ---------------------------------------------------------------------------

#include "System.hpp"
#include "Unicode.hpp"
#include "Integer.hpp"
#include "Pathname.hpp"
#include "Exception.hpp"
#include "cloc.hpp"
#include "csio.hpp"
#include "csys.hpp"
#include "cdir.hpp"
#include "cclk.hpp"
#include "cstr.hpp"
#include "ccnv.hpp"
#include "cgen.hpp"
#include "cloc.hpp"

namespace afnix {

  // -------------------------------------------------------------------------
  // - system encoding and transcoding modes                                 -
  // -------------------------------------------------------------------------

  // get the system encoding mode

  System::t_emod System::getsem (void) {
    // get the system codeset
    t_codeset codeset = c_getcset ();
    // map the code set
    switch (codeset) {
    case LOC_ASC_00:
    case LOC_ISO_01:
    case LOC_ISO_02:
    case LOC_ISO_03:
    case LOC_ISO_04:
    case LOC_ISO_05:
    case LOC_ISO_06:
    case LOC_ISO_07:
    case LOC_ISO_08:
    case LOC_ISO_09:
    case LOC_ISO_10:
    case LOC_ISO_11:
    case LOC_ISO_13:
    case LOC_ISO_14:
    case LOC_ISO_15:
    case LOC_ISO_16:
      return BYTE;
      break;
    case LOC_UTF_08:
      return UTF8;
      break;
    default:
      break;
    }
    return BYTE;
  }

  // get the encoding mode by name

  System::t_emod System::toemod (const String& mode) {
    System::t_emod emod = System::BYTE;
    if ((mode == "UTF-8") || 
	(mode == "UTF_8")) {
      emod = System::UTF8;
    }
    return emod;
  }

  // get the system transcoding mode

  System::t_tmod System::getstm (void) {
    // get the system codeset
    t_codeset codeset = c_getcset ();
    // map the code set
    switch (codeset) {
    case LOC_ASC_00:
      return DEFAULT;
      break;
    case LOC_ISO_01:
      return I8859_01;
      break;
    case LOC_ISO_02:
      return I8859_02;
      break;
    case LOC_ISO_03:
      return I8859_03;
      break;
    case LOC_ISO_04:
      return I8859_04;
      break;
    case LOC_ISO_05:
      return I8859_05;
      break;
    case LOC_ISO_06:
      return I8859_06;
      break;
    case LOC_ISO_07:
      return I8859_07;
      break;
    case LOC_ISO_08:
      return I8859_08;
      break;
    case LOC_ISO_09:
      return I8859_09;
      break;
    case LOC_ISO_10:
      return I8859_10;
      break;
    case LOC_ISO_11:
      return I8859_11;
      break;
    case LOC_ISO_13:
      return I8859_13;
      break;
    case LOC_ISO_14:
      return I8859_14;
      break;
    case LOC_ISO_15:
      return I8859_15;
      break;
    case LOC_ISO_16:
      return I8859_16;
      break;
    default:
      return DEFAULT;
      break;
    }
    return DEFAULT;
  }

  // get the transcoding mode by name

  System::t_tmod System::totmod (const String& mode) {
    // map to upper case
    String umod = mode.toupper ();
    System::t_tmod tmod = System::DEFAULT;
    if ((umod == "DEFAULT") ||
	(umod == "ASCII")) {
      tmod = System::DEFAULT;
    } else if ((umod == "ISO-8859-1") ||
	       (umod == "ISO_8859_1")) {
      tmod = System::I8859_01;
    } else if ((umod == "ISO-8859-2") ||
	       (umod == "ISO_8859_2")) {
      tmod = System::I8859_02;
    } else if ((umod == "ISO-8859-3") ||
	       (umod == "ISO_8859_3")) {
      tmod = System::I8859_03;
    } else if ((umod == "ISO-8859-4") ||
	       (umod == "ISO_8859_4")) {
      tmod = System::I8859_04;
    } else if ((umod == "ISO-8859-5") ||
	       (umod == "ISO_8859_5")) {
      tmod = System::I8859_05;
    } else if ((umod == "ISO-8859-6") ||
	       (umod == "ISO_8859_6")) {
      tmod = System::I8859_06;
    } else if ((umod == "ISO-8859-7") ||
	       (umod == "ISO_8859_7")) {
      tmod = System::I8859_07;
    } else if ((umod == "ISO-8859-8") ||
	       (umod == "ISO_8859_8")) {
      tmod = System::I8859_08;
    } else if ((umod == "ISO-8859-9") ||
	       (umod == "ISO_8859_9")) {
      tmod = System::I8859_09;
    } else if ((umod == "ISO-8859-10") ||
	       (umod == "ISO_8859_10")) {
      tmod = System::I8859_10;
    } else if ((umod == "ISO-8859-11") ||
	       (umod == "ISO_8859_11")) {
      tmod = System::I8859_11;
    } else if ((umod == "ISO-8859-13") ||
	       (umod == "ISO_8859_13")) {
      tmod = System::I8859_13;
    } else if ((umod == "ISO-8859-14") ||
	       (umod == "ISO_8859_14")) {
      tmod = System::I8859_14;
    } else if ((umod == "ISO-8859-15") ||
	       (umod == "ISO_8859_15")) {
      tmod = System::I8859_15;
    } else if ((umod == "ISO-8859-16") ||
	       (umod == "ISO_8859_16")) {
      tmod = System::I8859_16;
    } else if ((umod == "UTF-8") ||
	       (umod == "UTF_8")) {
      tmod = System::DEFAULT;
    } else {
      throw Exception ("mode-error", "invalid stream mode", mode);
    }
    return tmod;
  }

  // -------------------------------------------------------------------------
  // - platform management                                                   -
  // -------------------------------------------------------------------------

  // initialize the system

  void System::initialize (const char* name) {
    // check the initial flag
    static bool iflg = false;
    if (iflg == true) return;
    // main system initialization
    if (iflg == false) {
      // set localization
      c_iloc ();
      // set the program name
      c_setpgm (name);
      // mark initialized
      iflg = true;
    }
  }

  // register the program name

  void System::setpgm (const String& name) {
    char* data = name.tochar ();
    c_setpgm (data);
    delete [] data;
  }

  // get the program name

  String System::getpgm (void) {
    char* name = c_getpgm ();
    String result = name;
    delete [] name;
    return result;
  }

  // return the host name
  
  String System::hostname (void) {
    char* name = c_hostname ();
    String result = name;
    delete [] name;
    return result;
  }

  // return the user name
  
  String System::username (void) {
    char* name = c_username ();
    String result = name;
    delete [] name;
    return result;
  }

  // return the afnix url
  
  String System::geturi (void) {
    return "http://www.afnix.org";
  }

  // return the afnix major version
  
  String System::major (void) {
    Integer val (AFNIX_VERSION_MAJOR);
    return val.tostring ();
  }

  // return the afnix minor version
  
  String System::minor (void) {
    Integer val (AFNIX_VERSION_MINOR);
    return val.tostring ();
  }

  // return the afnix patch version
  
  String System::patch (void) {
    Integer val (AFNIX_VERSION_PATCH);
    return val.tostring ();
  }

  // return the os name
  
  String System::osname (void) {
    return c_osname ();
  }

  // return the os type

  String System::ostype (void) {
    return c_ostype ();
  }

  // return the version of the afnix system
  
  String System::version (void) {
    return AFNIX_VERSION_TOTAL;
  }

  // return true if the system is 32 bit

  bool System::is32 (void) {
    return (sizeof (long) == 4);
  }

  // return true if the system is 64 bit

  bool System::is64 (void) {
    return (sizeof (long) == 8);
  }

  // return the machine size

  String System::machs (void) {
    if (sizeof (long) == 4) return "32";
    if (sizeof (long) == 8) return "64";
    throw Exception ("internal-error", "unknown machine size");
  }

  // return the option separator

  t_quad System::optsep (void) {
    return Unicode::toquad (c_optsep ());
  }

  // return a formated option

  String System::getopt (const char opte) {
    return System::getopt (Unicode::toquad (opte));
  }

  // return a formated options

  String System::getopt (const t_quad opte) {
    String result = System::optsep ();
    result += opte;
    return result;
  }

  // return an environment variable value

  String System::getenv (const String& name) {
    const char* buf = name.tochar ();
    String result = c_getenv (buf);
    delete [] buf;
    return result;
  }

  // return a system uniq id

  t_long System::uniqid (void) {
    return c_uniqid ();
  }

  // exit unconditionally with a status code
  
  void System::exit (int status) {
    c_exit (status);
  }

  // return the process id

  long System::getpid (void) {
    return c_getpid ();
  }

  // pause for a certain time

  void System::sleep (const long time) {
    c_sleep (time);
  }

  // swap a word between host and network order

  t_word System::wswap (const t_word value) {
    return c_wswap (value);
  }

  // swap a quad between host and network order

  t_quad System::qswap (const t_quad value) {
    return c_qswap (value);
  }

  // swap a word between host and network order

  t_octa System::oswap (const t_octa value) {
    return c_oswap (value);
  }
  
  // return the system root directory

  String System::rootdir (void) {
    String result = c_rootdir ();
    return result;
  }

  // return the current working directory

  String System::getcwd (void) {
    String result = c_getcwd ();
    return result;
  }

  // return the directory separator

  String System::dirsep (void) {
    String result = c_dirsep ();
    return result;
  }

  // return the system temporary directory

  String System::tempdir (void) {
    String result = c_tempdir ();
    return result;
  }

  // return the system temporary name

  String System::tempnam (void) {
    String result = c_tempnam ("afnix");
    return result;
  }

  // return the system temporary name

  String System::tempnam (const String& prefix) {
    char* pf = prefix.tochar ();
    String result = c_tempnam (pf);
    delete [] pf;
    return result;
  }

  // return a system temporary path

  String System::temppth (void) {
    String dir = System::tempdir ();
    String nam = System::tempnam ();
    String result = System::join (dir, nam);
    return result;
  }

  // return a system temporary path

  String System::temppth (const String& prefix) {
    String dir = System::tempdir ();
    String nam = System::tempnam (prefix);
    String result = System::join (dir, nam);
    return result;
  }

  // remove the file name extension
  
  String System::rmext (const String& name) {
    char* fname = name.tochar ();
    char* data  = c_rmext (fname);
    String result = data;
    delete [] fname;
    delete [] data;
    return result;
  }
    
  // join one name to form an absolute path
  
  String System::join (const String& path) {
    return System::join (System::rootdir (), path);
  }

  // join two names to form a new file name
  
  String System::join (const String& dir, const String& name) {
    String result = dir;
    t_quad dirsep =  Unicode::toquad (c_dirsep ());
    if (result.last () != dirsep) result += dirsep;
    result += name;
    return result;
  }

  // extract a directory name from a path

  String System::xdir (const String& path) {
    char* data = path.tochar ();
    char* name = c_xdir (data);
    String result = name;
    delete [] data;
    delete [] name;
    return result;
  }

  // extract a file name from a path

  String System::xname (const String& path) {
    char* data = path.tochar ();
    char* name = c_xname (data);
    String result = name;
    delete [] data;
    delete [] name;
    return result;
  }

  // extract a file name extension

  String System::xext (const String& name) {
    char* data = name.tochar ();
    char* fext = c_xext (data);
    String result = fext;
    delete [] data;
    delete [] fext;
    return result;
  }

  // return true if the name corresponds to a regular file name
  
  bool System::isfile (const String& name) {
    char* fname = name.tochar ();
    bool status = c_isfile (fname);
    delete [] fname;
    return status;
  }
  
  // return true if the name is a regular file in the directory

  bool System::isfile (const String& dir, const String& name) {
    String path = System::join (dir, name);
    return isfile (path);
  }

  // return true if the name corresponds to a directory name
  
  bool System::isdir (const String& name) {
    char* dname = name.tochar ();
    bool status = c_isdir (dname);
    delete [] dname;
    return status;
  }

  // return true if the name is a sub-directory name

  bool System::isdir (const String& dir, const String& name) {
    String path = System::join (dir, name);
    return isdir (path);
  }

  // remove a file by name

  bool System::rmfile (const String& name) {
    char* fname = name.tochar ();
    bool status = c_rm (fname);
    delete [] fname;
    return status;
  }

  // create a directory by name

  bool System::mkdir (const String& name) {
    char* dname = name.tochar ();
    bool status = c_mkdir (dname);
    delete [] dname;
    return status;
  }

  // create a directory hierarchicaly by name

  bool System::mhdir (const String& name) {
    // create a path name by name
    Pathname path;
    path.setdnam (name);
    // get the path length
    long len = path.length ();
    if (len == 0) return false;
    // create the first component
    String pnam = path.getpath (0);
    if (System::mkdir (pnam) == false) return false;
    if (len == 1) return true;
    // loop in the other components
    for (long i = 1; i < len; i++) {
      pnam = System::join (pnam, path.getpath (i));
      if (System::mkdir (pnam) == false) return false;
    }
    // all done
    return true;
  }

  // remove a directory by name

  bool System::rmdir (const String& name) {
    char* dname = name.tochar ();
    bool status = c_rmdir (dname);
    delete [] dname;
    return status;
  }
}
