/*
 * Copyright (C) 1997-2001 Id Software, Inc.
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 *
 * =======================================================================
 *
 * This file implements some generic functions.
 *
 * =======================================================================
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>        /* for getcwd() on QNX */
#include <limits.h>        /* for PATH_MAX */

#include "../../common/header/shared.h"

/* Global variable holding the path to the executable directory */
char sys_exe_path[PATH_MAX] = {0};

/*
 * SetExecutablePath
 *
 * Sets the executable path for the game.
 * On QNX we use getcwd() as a simple fallback.
 */
void SetExecutablePath(void)
{
    char buf[PATH_MAX];

#if defined(__QNX__)
    if (getcwd(buf, sizeof(buf)) != NULL)
    {
        Q_strlcpy(sys_exe_path, buf, sizeof(sys_exe_path));
        Com_Printf("Executable path: %s\n", sys_exe_path);
    }
    else
    {
        Com_Printf("Warning: Could not determine executable path (getcwd failed)\n");
        Q_strlcpy(sys_exe_path, ".", sizeof(sys_exe_path));
    }
#else
#warning "SetExecutablePath() is unimplemented on this platform"
    Q_strlcpy(sys_exe_path, ".", sizeof(sys_exe_path));
#endif
}

static qboolean
Sys_GetCwd(char *buf, size_t size)
{
#ifdef _WIN32
    WCHAR wpath[PATH_MAX];
    DWORD len;

    if (_wgetcwd(wpath, PATH_MAX) == NULL)
    {
        return false;
    }
    len = WideCharToMultiByte(CP_UTF8, 0, wpath, -1, buf, size, NULL, NULL);
    if (len <= 0 || len == size)
    {
        return false;
    }
#else
    if (getcwd(buf, size) == NULL)
    {
        return false;
    }
#endif
    return Q_strlcat(buf, "/", size) == 1;
}

const char *Sys_GetBinaryDir(void)
{
    static char exeDir[PATH_MAX] = {0};

    if (exeDir[0] != '\0') {
        return exeDir;
    }

    /* Call without argument */
    SetExecutablePath();

    if (exeDir[0] == '\0') {
        if (Sys_GetCwd(exeDir, sizeof(exeDir)) == false) {
            Q_strlcpy(exeDir, "./", sizeof(exeDir));
        }
        Com_Printf("Couldn't determine executable path. Using %s instead.\n", exeDir);
    } else {
        /* cut off executable name */
        char *lastSlash = strrchr(exeDir, '/');
#ifdef _WIN32
        char* lastBackSlash = strrchr(exeDir, '\\');
        if (lastSlash == NULL || lastBackSlash > lastSlash)
            lastSlash = lastBackSlash;
#endif
        if (lastSlash != NULL)
            lastSlash[1] = '\0';   // cut off after last slash
    }

    return exeDir;
}

/* ===================================================================== */

#if defined (__GNUC__) && (__i386 || __x86_64__)
void Sys_SetupFPU(void) {
    // Get current x87 control word
    volatile unsigned short old_cw = 0;
    asm ("fstcw %0" : : "m" (*&old_cw));
    unsigned short new_cw = old_cw;

    new_cw &= ~(1 << 8);
    new_cw |= (1 << 9);

    if (new_cw != old_cw) {
        asm ("fldcw %0" : : "m" (*&new_cw));
    }
}
#else
void Sys_SetupFPU(void) {
#if defined(__arm__) && defined(__ARM_PCS_VFP)
    static const unsigned int bit = 0x04086060;
    static const unsigned int fpscr = 0x03000000;
    int ret;

    asm volatile("fmrx %0, fpscr" : "=r"(ret));
    if (ret != fpscr) {
        asm volatile("fmrx %0, fpscr\n\t"
                     "and  %0, %0, %1\n\t"
                     "orr  %0, %0, %2\n\t"
                     "fmxr fpscr, %0"
                     : "=r"(ret)
                     : "r"(bit), "r"(fpscr));
    }
#endif
}
#endif
