/* vim:expandtab:ts=2 sw=2:
*/
/*  Grafx2 - The Ultimate 256-color bitmap paint program

	Copyright owned by various GrafX2 authors, see COPYRIGHT.txt for details.

    Grafx2 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; version 2
    of the License.

    Grafx2 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 Grafx2; if not, see <http://www.gnu.org/licenses/>
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#ifdef _MSC_VER
  #define strdup _strdup
  #if _MSC_VER < 1900
    #define snprintf _snprintf
  #endif
#endif
#if defined(__macosx__)
  #import <CoreFoundation/CoreFoundation.h>
  #import <sys/param.h>
#elif defined(__FreeBSD__)
  #include <sys/param.h>
  #include <unistd.h>
#elif defined(__MINT__)
    #include <mint/osbind.h>
    #include <mint/sysbind.h>
#elif defined(__linux__)
  #include <limits.h>
  #include <unistd.h>
  #include <errno.h>
#elif defined(__HAIKU__)
  #include <FindDirectory.h>
  #include "haiku.h"
#endif

#include "struct.h"
#include "io.h"
#include "setup.h"
#include "global.h"
#include "gfx2log.h"

#ifndef PATH_MAX
    // This is a random default value ...
    #define PATH_MAX 32768
#endif

// Determine which directory contains the executable.
// IN: Main's argv[0], some platforms need it, some don't.
// OUT: program_dir. Trailing / or \ is kept.
// Note : in fact this is only used to check for the datafiles and fonts in 
// this same directory.
char * Get_program_directory(const char * argv0)
{
  char * program_dir;
  // MacOSX
  #if defined(__macosx__)
    program_dir = malloc(MAXPATHLEN);
    if (program_dir != NULL)
    {
      CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
      (void)argv0; // unused
      CFURLGetFileSystemRepresentation(url,true,(UInt8*)program_dir,MAXPATHLEN);
      CFRelease(url);
      // Append trailing slash
      strcat(program_dir    ,"/");
    }
  
  // AmigaOS and alike: hard-coded volume name.
  #elif defined(__amigaos4__) || defined(__AROS__) || defined(__MORPHOS__) || defined(__amigaos__)
    (void)argv0; // unused
    program_dir = strdup("PROGDIR:");
  #elif defined(__MINT__)
  static char path[1024]={0};
  char currentDrive;

  (void)argv0; // unused
  currentDrive = 'A' + Dgetdrv();
  Dgetpath(path, 0);
  program_dir = malloc(4 + strlen(path) + 1);
  sprintf(program_dir,"%c:\\%s%s", currentDrive, path, PATH_SEPARATOR);
  #elif defined(__ANDROID__)
  (void)argv0; // unused
  progam_dir = malloc(MAX_PATH_CHARACTERS);
  getcwd(program_dir, MAX_PATH_CHARACTERS);
  strcat(program_dir, "/");
  // Linux: argv[0] unreliable
  #elif defined(__linux__) || defined(__FreeBSD__)
  #if defined(__linux__)
  #define SELF_PATH "/proc/self/exe"
  #elif defined(__FreeBSD__)
  #define SELF_PATH "/proc/curproc/file"
  #endif
  if (argv0[0]!='/')
  {
    ssize_t path_len;
    char path[PATH_MAX];
    path_len = readlink(SELF_PATH, path, sizeof(path));
    if (path_len >= 0)
    {
      path[path_len] = '\0';  // add null terminating char
      GFX2_Log(GFX2_DEBUG, "binary path resolved to : %s\n", path);
      program_dir = Extract_path(path);
    }
    else
    {
      char * current_dir, * tmp;
      size_t len;

      program_dir = NULL;
      GFX2_Log(GFX2_WARNING, "readlink(%s) failed : %s\n", SELF_PATH, strerror(errno));
      current_dir = Get_current_directory(NULL, NULL, 0);
      if (current_dir != NULL)
      {
        len = strlen(current_dir) + strlen(argv0) + 2;
        tmp = malloc(len);
        if (tmp != NULL)
        {
          snprintf(tmp, len, "%s/%s", current_dir, argv0);
          program_dir = Extract_path(tmp);
          free(tmp);
        }
        free(current_dir);
      }
    }
  }
  else
    program_dir = Extract_path(argv0);
  #elif defined(__HAIKU__)
  program_dir = Extract_path(haiku_get_app_path());
  
  // Others: The part of argv[0] before the executable name.    
  // Keep the last \ or /.
  // On Windows, Mingw32 already provides the full path in all cases.
  #else
  program_dir = Extract_path(argv0);
  #endif
  if (program_dir == NULL)
  {
    GFX2_Log(GFX2_WARNING, "Failed to detect program directory, using current directory\n");
    program_dir = strdup("." PATH_SEPARATOR);
  }
  return program_dir;
}

/// Determine which directory contains the read-only data.
/// @param program_dir The directory containing the executable
/// @return the path. Trailing / or \ is kept.
char * Get_data_directory(const char * program_dir)
{
  const char * to_append;
  // On all platforms, data is relative to the executable's directory
  #if defined(__macosx__)
    // On MacOSX,  it is stored in a special folder:
    to_append = "Contents/Resources/";
  #elif defined (__GP2X__) || defined (__gp2x__) || defined (__WIZ__) || defined (__CAANOO__) || defined(GCWZERO) || defined(__AROS__) || defined(__ANDROID__) || defined(__amigaos__) 
    // On GP2X, AROS and Android, executable is not in bin/
    to_append = "data/";
  #elif defined (__MINT__)
    //on tos, the same directory is used for everything
    return strdup(program_dir);
  #elif defined(__SWITCH__)
    //on switch, we store everything in the SD card in /switch/grafx2
    return strdup("/switch/grafx2/");
  #elif defined(__HAIKU__)
    // Haiku provides us with an API to find it.
    char * data_dir = malloc(PATH_MAX);
    if (find_path(Get_data_directory, B_FIND_PATH_DATA_DIRECTORY, "grafx2/", data_dir, PATH_MAX) == B_OK)
    {
      return data_dir;
    }
    else
    {
      // If the program is not installed, find_path will fail. Try from local dir then.
      free(data_dir);
      to_append = "../share/grafx2/";
    }

  #elif defined(WIN32)
    to_append = "..\\share\\grafx2\\";
  #else
    // All other targets, program is in a "bin" subdirectory
    to_append = "../share/grafx2/";
  #endif

  return Filepath_append_to_dir(program_dir, to_append);
}

// Determine which directory should store the user's configuration.
//
// For most Unix and Windows platforms:
// If a config file already exists in program_dir, it will return it in priority
// (Useful for development, and possibly for upgrading from DOS version)
// If the standard directory doesn't exist yet, this function will attempt 
// to create it ($(HOME)/.grafx2, or %APPDATA%\GrafX2)
// If it cannot be created, this function will return the executable's
// own directory.
// IN: The directory containing the executable
// OUT: Write into config_dir. Trailing / or \ is kept.
char * Get_config_directory(const char * program_dir)
{
  // AmigaOS4 provides the PROGDIR: alias to the directory where the executable is.
  #if defined(__amigaos4__) || defined(__AROS__) || defined(__amigaos__)
    return strdup("PROGDIR:");
  // GP2X
  #elif defined(__GP2X__) || defined(__WIZ__) || defined(__CAANOO__)
    // On the GP2X, the program is installed to the sdcard, and we don't want to mess with the system tree which is
    // on an internal flash chip. So, keep these settings locals.
    return strdup(program_dir);
  // For TOS we store everything in the program dir
  #elif defined(__MINT__)
    return strdup(program_dir);
  //on switch, we store everything in the SD card in /switch/grafx2
  #elif defined(__SWITCH__)
    return strdup("/switch/grafx2/");
  // For all other platforms, there is some kind of settings dir to store this.
  #else
    char * config_dir;
    size_t len;
    char * filename;
    #ifdef GCWZERO
      config_dir = strdup("/media/home/.grafx2/");
    #elif defined(__macosx__)
      // On all the remaining targets except OSX, the executable is in ./bin
      config_dir = strdup(program_dir);
    #else
      len = strlen(program_dir) + 2 + strlen(PATH_SEPARATOR) + 1;
      config_dir = malloc(len);
      snprintf(config_dir, len, "%s%s", program_dir, ".." PATH_SEPARATOR);
    #endif

    filename = Filepath_append_to_dir(config_dir, CONFIG_FILENAME);

    if (File_exists(filename))
    {
      // gfx2.cfg found, this is a portable installation
      Portable_Installation_Detected = 1;
    }
    else
    {
      char *config_parent_dir;
      #if defined(__WIN32__) || defined(WIN32)
        // "%APPDATA%\GrafX2"
        const char* Config_SubDir = "GrafX2";
        config_parent_dir = getenv("APPDATA");
      #elif defined(__BEOS__) || defined(__HAIKU__)
        // "`finddir B_USER_SETTINGS_DIRECTORY`/grafx2"
        const char* Config_SubDir = "grafx2";
        {
          static char parent[MAX_PATH_CHARACTERS];
          find_directory(B_USER_SETTINGS_DIRECTORY, 0, false, parent,
            MAX_PATH_CHARACTERS);
          config_parent_dir = parent;
        }
      #elif defined(__macosx__)
        // "~/Library/Preferences/com.googlecode.grafx2"
        const char* Config_SubDir = "Library/Preferences/com.googlecode.grafx2";
        config_parent_dir = getenv("HOME");
      #elif defined(__MINT__)
         const char* Config_SubDir = "";
         printf("GFX2.CFG not found in %s\n",filename);
         config_parent_dir = strdup(config_dir);
      #else
         // ~/.config/grafx2
         const char* Config_SubDir;
         config_parent_dir = getenv("XDG_CONFIG_HOME");
         if (config_parent_dir)
           Config_SubDir = "grafx2";
         else {
           Config_SubDir = ".config/grafx2";
           config_parent_dir = getenv("HOME");
         }
      #endif
      Portable_Installation_Detected = 0;

      if (config_parent_dir && config_parent_dir[0]!='\0')
      {
        size_t size = strlen(config_parent_dir);
        free(config_dir);
        len = size + strlen(Config_SubDir) + strlen(PATH_SEPARATOR) + 1;
        if (config_parent_dir[size-1] != '\\' && config_parent_dir[size-1] != '/')
        {
          len += strlen(PATH_SEPARATOR);
          config_dir = malloc(len);
          snprintf(config_dir, len, "%s%s%s%s",
                   config_parent_dir, PATH_SEPARATOR,
                   Config_SubDir, PATH_SEPARATOR);
        }
        else
        {
          config_dir = malloc(len);
          snprintf(config_dir, len, "%s%s%s",
                   config_parent_dir,
                   Config_SubDir, PATH_SEPARATOR);
        }
        if (!Directory_exists(config_dir))
        {
          // try to create it
          if (Directory_create(config_dir) < 0)
          {
            GFX2_Log(GFX2_WARNING, "Failed to create directory \"%s\"\n", config_dir);
            // Echec: on se rabat sur le repertoire de l'executable.
            Portable_Installation_Detected = 1;
            free(config_dir);
            #if defined(__macosx__)
              len = strlen(program_dir) + 4;
              config_dir = malloc(len);
              snprintf(config_dir, len, "%s%s", program_dir, "../");
            #else
              config_dir = strdup(program_dir);
            #endif
          }
            GFX2_Log(GFX2_INFO, "\"%s\" directory created.\n", config_dir);
        }
      }
    }
    free(filename);
    return config_dir;
  #endif
}
