/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2009-2010 JRuby Team (www.jruby.org).
 * Copyright 1997-2008 Sun Microsystems, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common
 * Development and Distribution License("CDDL") (collectively, the
 * "License"). You may not use this file except in compliance with the
 * License. You can obtain a copy of the License at
 * http://www.netbeans.org/cddl-gplv2.html
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 * specific language governing permissions and limitations under the
 * License.  When distributing the software, include this License Header
 * Notice in each file and include the License file at
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the GPL Version 2 section of the License file that
 * accompanied this code. If applicable, add the following below the
 * License Header, with the fields enclosed by brackets [] replaced by
 * your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Contributor(s):
 *
 * The Original Software is NetBeans. The Initial Developer of the Original
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2008 Sun
 * Microsystems, Inc. All Rights Reserved.
 *
 * If you wish your version of this file to be governed by only the CDDL
 * or only the GPL Version 2, indicate your decision by adding
 * "[Contributor] elects to include this software in this distribution
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 * single choice of license, a recipient has the option to distribute
 * your version of this file under either the CDDL, the GPL Version 2 or
 * to extend the choice of license to its licensees as provided above.
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 * Version 2 license, then the option applies only if the new code is
 * made subject to such option by the copyright holder.
 *
 * Author: Tomas Holy
 */

#include <stdio.h>
#include <stdarg.h>
#include <cstring>
#include <cstdlib>
#include <memory>
#include <list>
#include <algorithm>
#include <iterator>
#include <iostream>
#include <unistd.h>
#include "utilsfuncs.h"
#include "argnames.h"
#include <limits.h>

#ifndef WIN32
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#endif

#ifdef __SUNOS__
#include <sys/varargs.h>
#endif

using namespace std;

bool checkExists(const char* path, unsigned int flags) {
#ifdef WIN32
    WIN32_FIND_DATA fd = {0};
    HANDLE hFind = 0;
    hFind = FindFirstFile(path, &fd);
    if (hFind == INVALID_HANDLE_VALUE) {
        return false;
    }
    FindClose(hFind);
    if (flags == 0) {
	return true;
    }
    return (fd.dwFileAttributes & flags) != 0;
#else
    struct stat dir;
    if (stat(path, &dir) != 0) {
	return false;
    }
    if (flags == 0) {
	return true;
    }
    return dir.st_mode & flags;
#endif
}

bool checkDirectory(const char* path) {
#ifdef WIN32
    unsigned int flags = FILE_ATTRIBUTE_DIRECTORY;
#else
    unsigned int flags = S_IFDIR;
#endif
    return checkExists(path, flags);
}

bool dirExists(const char *path) {
    if (!checkDirectory(path)) {
        logMsg("Dir \"%s\" does not exist", path);
        return false;
    }
    logMsg("Dir \"%s\" exists", path);
    return true;
}

bool fileExists(const char *path) {
    if (!checkExists(path, 0)) {
        logMsg("File \"%s\" does not exist", path);
        return false;
    }
    logMsg("File \"%s\" exists", path);
    return true;
}

string findOnPath(const char* name) {
    string path(getenv("PATH"));
    size_t start = 0;
    size_t sep;

    while (start < path.length()) {
	sep = path.find(PATH_SEP, start);
	if (sep == string::npos) {
	    sep = path.length();
	}

	string elem(path.substr(start, sep - start));
	if (elem[elem.length() - 1] != FILE_SEP) {
	    elem += FILE_SEP;
	}
	elem += name;

	if (checkExists(elem.c_str(), 0)) {
            string found_string(elem);
	    return found_string;
	}

	start = sep + 1;
    }
    return "";
}

string resolveSymlinks(string path) {
#ifndef WIN32
    struct stat st;
    char tmp[PATH_MAX + 1];

    if (lstat(path.c_str(), &st) == 0 && (st.st_mode & S_IFMT) == S_IFLNK) {
        realpath(path.c_str(), tmp);
        path = tmp;
    }
#endif
    return path;
}

const char* getSysError(char *str, int strSize) {
#ifdef WIN32
    int err = GetLastError();
    LPTSTR lpMsgBuf;
    FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM |
            FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL,
            err,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (LPTSTR) & lpMsgBuf,
            0,
            NULL
            );
    LPTSTR tmp = strchr(lpMsgBuf, '\r');
    if (tmp != NULL) {
        *tmp = '\0';
    }

    _snprintf(str, strSize, " %s (%u)", lpMsgBuf, err);
    LocalFree(lpMsgBuf);
#else
    const char* error = strerror(errno);
    snprintf(str, strSize, " %s (%u)", error, errno);
#endif
    return str;
}

string gLogFileName;

void logV(bool appendSysError, bool showMsgBox, const char *format, va_list args) {
    char msg[4096] = "";
    vsnprintf(msg, 4096, format, args);

    if (appendSysError) {
        char sysErr[512] = "";
        getSysError(sysErr, 512);
        strncat(msg, sysErr, 4096 - strlen(msg));
    }

    if (!gLogFileName.empty() && gLogFileName != "''") {
        FILE *file = fopen(gLogFileName.c_str(), "a");
        if (file) {
            fprintf(file, "%s\n", msg);
            fclose(file);
        }
    }

    if (showMsgBox) {
#ifdef WIN32
        // Pop-up the message box only if there is no console
        if (!isConsoleAttached()) {
            ::MessageBox(NULL, msg, "JRuby Error", MB_OK | MB_ICONSTOP);
        }
#endif
        fprintf(stdout, "%s\n", msg);
    }
}

void logErr(bool appendSysError, bool showMsgBox, const char *format, ...) {
    va_list args;
    va_start(args, format);
    logV(appendSysError, showMsgBox, format, args);
}

void logMsg(const char *format, ...) {
    va_list args;
    va_start(args, format);
    logV(false, false, format, args);
}

bool checkLoggingArg(int argc, char *argv[], bool delFile) {
    for (int i = 0; i < argc; i++) {
        if (strcmp("--", argv[i]) == 0) {
            break;
        }
        if (strcmp(ARG_NAME_LAUNCHER_LOG, argv[i]) == 0) {
            if (i+1 == argc || *argv[i+1] == '-') {
                return false;
            }
            gLogFileName = argv[++i];
            if (delFile) {
#ifdef WIN32
                DeleteFile(gLogFileName.c_str());
#else
		unlink(gLogFileName.c_str());
#endif
            }
            break;
        }
    }
    return true;
}

bool printToConsole(const char *msg) {
    fprintf(stdout, "%s", msg);
    return false;
}

char** convertToArgvArray(list<string> args) {
    char ** argv = new char*[args.size()+1];
    int i = 0;
    for (list<string>::iterator it = args.begin(); it != args.end(); ++it, ++i) {
        argv[i] = strdup((*it).c_str());
    }
    argv[args.size()] = NULL;
    return argv;
}

void addToArgList(list<string> & args, int argc, char ** argv) {
    for (int i = 0; i < argc; i++) {
        std::string str(argv[i]);
	args.push_back(str);
    }
}

void printListToConsole(list<string> values) {
    for (list<string>::iterator it = values.begin(); it != values.end(); ++it) {
        std::cout << *it << std::endl;
    }
}

string trimTrailingBackslashes(string orig) {
    while (orig.size() > 0 &&
        orig.at(orig.size() - 1) == '\\') {
        orig.erase(orig.size() - 1);
    }
    return orig;
}