/*
 * Copyright (C) 2002,2003,2004 Daniel Heck
 * Copyright (C) 2006,2007,2008,2009 Ronald Lamprecht
 *
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */
#include "ecl_system.hh"
#include "ecl_util.hh"

#include <cstdlib>
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <locale.h>
#include <unistd.h>
#include <cassert>

#include <config.h>

#ifdef __MINGW32__
#include <windows.h>
#include <shellapi.h>
#elif MACOSX
#include <Carbon/Carbon.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>
#endif

using namespace ecl;
using std::string;

#ifdef __MINGW32__
const char *ecl::PathSeparator = "\\";  // for path assembly
const char *ecl::PathsSeparator = ";";  // for listing paths in a string
#else
const char *ecl::PathSeparator = "/";   // for path assembly
const char *ecl::PathsSeparator = ":";  // for listing paths in a string
#endif
const char *ecl::PathSeparators = "/\\";  // for path splits

std::string ecl::ExpandPath(const string &pth) {
    std::string path = pth;
    std::string::size_type p = path.find("~");
    if (p != string::npos) {
        std::string home;
        if (char *h = getenv("HOME"))
            home = h;
        path.replace(p, 1, home);
    }
    return path;
}

std::string ecl::BeautifyPath(const std::string path) {
    std::string foreignSeparators = ecl::PathSeparators;
    foreignSeparators.erase(foreignSeparators.find(ecl::PathSeparator));
    std::string result = path;
    for (std::string::size_type pos = result.find_first_of(foreignSeparators);
         pos != std::string::npos; pos = result.find_first_of(foreignSeparators)) {
        result.replace(pos, 1, ecl::PathSeparator);
    }
    return result;
}

bool ecl::FileExists(const std::string &fname) {
    struct stat s;
    return (stat(fname.c_str(), &s) == 0 && S_ISREG(s.st_mode));
}

time_t ecl::FileModTime(const std::string &fname) {
    struct stat s;
    if (stat(fname.c_str(), &s) == 0) {
        return s.st_mtime;
    }

    return 0;  // beginning of time
}

bool ecl::FolderExists(const std::string &fname) {
    struct stat s;
    return (stat(fname.c_str(), &s) == 0 && S_ISDIR(s.st_mode));
}

bool ecl::FolderCreate(const std::string &fname) {
    string parent_folder;
    string sub_folder;
    bool ok = true;

    assert(!FolderExists(fname));

    if (split_path(fname, &parent_folder, &sub_folder)) {
        if (!FolderExists(parent_folder)) {
            ok = FolderCreate(parent_folder);
        }
    }

    if (ok) {
#ifdef __MINGW32__
        ok = mkdir(fname.c_str()) == 0;
#else
        ok = mkdir(fname.c_str(), 0777) == 0;
#endif
    }
    return ok;
}

bool ecl::BrowseUrl(const std::string url) {
    bool result = true;
#ifdef __MINGW32__
    result == ((int)ShellExecute(NULL, "open", url.c_str(), NULL, NULL, SW_SHOWNORMAL) >= 32);
#elif MACOSX
    CFStringRef cfurlStr = CFStringCreateWithCString(NULL, url.c_str(), kCFStringEncodingASCII);

    // Create a URL object:
    CFURLRef cfurl = CFURLCreateWithString(NULL, cfurlStr, NULL);

    // Open the URL:
    LSOpenCFURLRef(cfurl, NULL);

    // Release the created resources:
    CFRelease(cfurl);
    CFRelease(cfurlStr);
#else
    result = (system(("xdg-open " + url + " &").c_str()) == 0);
#endif
    return result;
}

bool ecl::ExploreFolder(const std::string path) {
    bool result = true;
#ifdef __MINGW32__
    result == ((int)ShellExecute(NULL, "explore", path.c_str(), NULL, NULL, SW_SHOWNORMAL) >= 32);
#elif MACOSX
    FSRef fref;
    FSPathMakeRef((UInt8 *)path.c_str(), &fref, NULL);
    LSOpenFSRef(&fref, NULL);
#else
    result = (system(("xdg-open " + path + " &").c_str()) == 0);
#endif
    return result;
}

#ifdef __MINGW32__
// should be ecl_system_windows.cc ?
std::string ecl::ApplicationDataPath() {
    typedef HRESULT(WINAPI * SHGETFOLDERPATH)(HWND, int, HANDLE, DWORD, LPTSTR);
#define CSIDL_FLAG_CREATE 0x8000
#define CSIDL_APPDATA 0x1A
#define SHGFP_TYPE_CURRENT 0

    HINSTANCE shfolder_dll;
    SHGETFOLDERPATH SHGetFolderPath;

    static bool didRun = false;
    static std::string result = "";  // remember exec results

    if (!didRun) {
        didRun = true;
        /* load the shfolder.dll to retreive SHGetFolderPath */
        if ((shfolder_dll = LoadLibrary("shfolder.dll")) != NULL) {
            SHGetFolderPath = (SHGETFOLDERPATH)GetProcAddress(shfolder_dll, "SHGetFolderPathA");
            if (SHGetFolderPath != NULL) {
                TCHAR szPath[MAX_PATH] = "";

                /* get the "Application Data" folder for the current user */
                if (S_OK == SHGetFolderPath(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL,
                                            SHGFP_TYPE_CURRENT, szPath)) {
                    result = szPath;
                }
            }
            FreeLibrary(shfolder_dll);
        }
    }
    return result;
}

void ecl::ToLowerCase(std::string &filename) {
    for (int i = 0; i < filename.length(); i++) {
        char c = filename[i];
        if (c >= 'A' && c <= 'Z')
            filename.replace(i, 1, 1, c - 'A' + 'a');
    }
}

std::set<std::string> ecl::UniqueFilenameSet(std::set<std::string> inSet) {
    std::set<std::string>::iterator it = inSet.begin();
    std::set<std::string> outSet;
    std::set<std::string> lowerSet;
    for (; it != inSet.end(); it++) {
        std::string name = *it;
        ecl::ToLowerCase(name);
        if (lowerSet.find(name) == lowerSet.end()) {
            outSet.insert(*it);
            lowerSet.insert(name);
        }
    }
    return outSet;
}

#endif

std::string ecl::GetLanguageCode(const std::string &ln) {
    if (ln == "C" || ln == "POSIX" || ln.size() < 2)
        return "en";

    return ln.substr(0, 2);
}
