/*
 * ============================================================================
 *  Title:    Platform Portability "Library"
 *  Author:   J. Zbiciak, T. Lindner
 * ============================================================================
 *  This module fills in missing features on various platforms.
 * ============================================================================
 *  GETTIMEOFDAY     -- Return current time in seconds/microseconds.
 *  STRDUP           -- Copy a string into freshly malloc'd storage.
 *  STRICMP          -- Case-insensitive string compare.
 *  SNPRINTF         -- Like sprintf(), only with bounds checking.
 *  PLAT_DELAY       -- Sleep w/ millisecond precision.
 *  GET_EXE_DIR      -- Get the directory containing this executable
 * ============================================================================
 */

#include "config.h"
#include "plat/plat_lib_config.h"
#include "/opt/amiga/m68k-amigaos/sys-include/time.h"
#include "/opt/amiga/m68k-amigaos/ndk-include/sys/ioctl.h"
#ifdef USE_TERMIO
# include <termio.h>
//#include "/root/amiga-gcc/projects/newlib-cygwin/newlib/libc/sys/sysvi386/sys/termio.h"

#endif

#ifdef USE_SYS_IOCTL
# include <sys/ioctl.h>
#endif

/* ======================================================================== */
/*  GENERIC PORTABLE VERSIONS...                                            */
/* ======================================================================== */

/* ------------------------------------------------------------------------ */
/*  GET_TIME -- Return current time in seconds as a double.                 */
/* ------------------------------------------------------------------------ */
#if GET_TIME_STRATEGY == GTS_CLOCK_GETTIME
double get_time(void)
{
    struct timespec now;
    double seconds;

    clock_gettime(CLOCK_MONOTONIC, &now);

    seconds = (double)now.tv_sec + (double)now.tv_nsec * 1e-9;
    return seconds;
}
#endif

#if GET_TIME_STRATEGY == GTS_GETTIMEOFDAY
double get_time(void)
{
    struct timeval now;
    double seconds;

    gettimeofday(&now, NULL);

    seconds = (double)now.tv_sec + (double)now.tv_usec * 1e-6;

    return seconds;
}
#endif

#if GET_TIME_STRATEGY == GTS_WIN_PERF_COUNTERS
#  ifndef WIN_HEADERS_INCLUDED
#     define WIN_HEADERS_INCLUDED
#     define WIN32_LEAN_AND_MEAN
#     include <windows.h>
#     include <math.h>
#  endif

LOCAL double perf_rate = -1;

extern double win32_get_time_fallback(void);

double get_time(void)
{
    LARGE_INTEGER li;

    if (perf_rate < 0)
    {
        if (QueryPerformanceFrequency(&li))
        {
            perf_rate = 1.0 / (double)li.QuadPart;
        } else
        {
            perf_rate = 0;
        }
    }

    if (perf_rate > 0)
    {
        double seconds;
        QueryPerformanceCounter(&li);
        seconds = (double)li.QuadPart * perf_rate;
        return seconds;
    }

    return win32_get_time_fallback();
}
#endif

#if GET_TIME_STRATEGY == GTS_WII
double get_time(void)
{
    extern double wii_get_time();

    return wii_get_time();
}
#endif

/* ------------------------------------------------------------------------ */
/*  PLAT_DELAY        -- Sleep w/ millisecond precision.                    */
/*  PLAT_DELAY_NO_SDL -- Sleep w/ millisecond precision.                    */
/* ------------------------------------------------------------------------ */
#if PLAT_DELAY_STRATEGY_NO_SDL == PDS_WIN_WAIT_TIMER
#  ifndef WIN_HEADERS_INCLUDED
#     define WIN_HEADERS_INCLUDED
#     define WIN32_LEAN_AND_MEAN
#     include <windows.h>
#     include <math.h>
#  endif
void plat_delay_no_sdl(unsigned msec)
{
    HANDLE timer; 
    LARGE_INTEGER ft; 

    /* Windows wants 100ns intervals */
    /* Also, -ve to specify _relative_ time */
    ft.QuadPart = -(10000ll*msec); 

    timer = CreateWaitableTimer(NULL, TRUE, NULL); 
    SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); 
    WaitForSingleObject(timer, INFINITE); 
    CloseHandle(timer); 
}
#endif

#if PLAT_DELAY_STRATEGY_NO_SDL == PDS_NANOSLEEP
void plat_delay_no_sdl(unsigned delay)
{
    struct timespec ts;
    ts.tv_sec = delay / 1000;
    ts.tv_nsec = (delay % 1000) * 1000000;
    nanosleep(&ts, NULL);
}
#endif

#if PLAT_DELAY_STRATEGY_NO_SDL == PDS_USLEEP
void plat_delay_no_sdl(unsigned delay)
{
    usleep(delay * 1000);
}
#endif

#if PLAT_DELAY_STRATEGY_NO_SDL == PDS_BUSY_LOOP
void plat_delay_no_sdl(unsigned delay)
{
    double soon = get_time() + ((double)delay / 1000.0);

    /* -------------------------------------------------------------------- */
    /*  BAD BAD BAD: Sit in a busy loop until time expires.                 */
    /* -------------------------------------------------------------------- */
    while (get_time() < soon)
        ;

    return;
}
#endif

#if PLAT_DELAY_STRATEGY != PDS_MACINTOSH \
   && PLAT_DELAY_STRATEGY != PDS_SDL_DELAY
void plat_delay(unsigned delay)
{
    plat_delay_no_sdl(delay);
}
#endif


/* ======================================================================== */
/*  Window size functions                                                   */
/*                                                                          */
/*  GET_DISP_WIDTH   -- Get the width of the text display.                  */
/*  SET_DISP_WIDTH   -- Try to set the width of the text display.           */
/*  INIT_DISP_WIDTH  -- Initialize the display width.                       */
/*                                                                          */
/*  If the display width is unknown, this should return '80', the default   */
/*  width jzIntv historically assumed.  Because 'set_disp_width' can fail,  */
/*  it returns the new width, which may be unchanged.                       */
/*                                                                          */
/*  On platforms which cannot detect display width, "INIT_DISP_WIDTH" can   */
/*  set the display width.  Passing in 0 here just tells it to detect or    */
/*  use the default width.                                                  */
/* ======================================================================== */


/* ------------------------------------------------------------------------ */
/*  Weakest implementation:  Just return 80 unless we're told to return     */
/*  something else.                                                         */
/* ------------------------------------------------------------------------ */
#if !defined(WIN32) && !defined(CAN_TIOCGWINSZ)
LOCAL int disp_width = -1;

int get_disp_width(void)
{
    return disp_width;
}

int set_disp_width(int new_width)
{
    disp_width = new_width > 0 ? new_width : disp_width;
    return disp_width;
}

void init_disp_width(int width)
{
    disp_width = width > 0 ? width : 80;
}
#endif

/* ------------------------------------------------------------------------ */
/*  WIN32 Implementation:  Eventually use "mode con:" to change size.       */
/* ------------------------------------------------------------------------ */
#if defined(WIN32)
LOCAL int disp_width = -1;

LOCAL int set_width_from_mode_con(void)
{
    FILE *f;
    f = popen("mode con:", "r");
    if (f)
    {
        char buf[80];
        int w = -1;

        while (fgets(buf, sizeof(buf), f) != NULL)
        {
            if (sscanf(buf, " Columns: %d", &w) > 0)
                disp_width = w;
        }

        pclose(f);

        if ( w == -1 ) // couldn't find it!
            return -1;
        else
            return 0;  // success
    }
    return 0;
}

int get_disp_width(void)
{
    return disp_width;
}

int set_disp_width(int new_width)
{
    char buf[80];

    if (new_width == disp_width || new_width < 1)
        return disp_width;

    sprintf(buf, "mode con: cols=%d", new_width);

    if (system(buf) == 0)
        if ( set_width_from_mode_con() < 0 ) // asking Windows didn't work?
            disp_width = new_width;          // hack:  assume it worked.

    return disp_width;
}

void init_disp_width(int width)
{

    disp_width = width > 0 ? width : 80;

}

#endif

/* ------------------------------------------------------------------------ */
/*  TIOCGWINSZ w/out SIGWINCH:  Always call the ioctl when asked the size.  */
/* ------------------------------------------------------------------------ */
#if !defined(WIN32) && defined(CAN_TIOCGWINSZ) && !defined(CAN_SIGWINCH)
LOCAL int disp_width = 80;

int get_disp_width(void)
{
    Dims my_win;

    if (ioctl(0, TIOCGWINSZ, &my_win) != 0)
        return disp_width;

    return my_win.ws_col;
}

int set_disp_width(int new_width)
{
    disp_width = new_width > 0 ? new_width : disp_width;
    return get_disp_width();
}

void init_disp_width(int new_width)
{
    disp_width = new_width > 0 ? new_width : disp_width;
}
#endif

/* ------------------------------------------------------------------------ */
/*  TIOCGWINSZ with SIGWINCH:  Only call the ioctl if a SIGWINCH happened.  */
/* ------------------------------------------------------------------------ */
#if !defined(WIN32) && defined(CAN_TIOCGWINSZ) && defined(CAN_SIGWINCH)
#include <signal.h>

LOCAL volatile char need_width_ioctl = 1;
LOCAL int           disp_width       = 80;

LOCAL void sigwinch_handler(int sig)
{
    UNUSED(sig);
    signal(SIGWINCH, sigwinch_handler);
    need_width_ioctl = 1;
}

int get_disp_width(void)
{
    struct winsize my_win;

    if (need_width_ioctl)
    {
        need_width_ioctl = 0;
        if (ioctl(0, TIOCGWINSZ, &my_win) == 0)
            disp_width = my_win.ws_col;
    }

    return disp_width;
}

int set_disp_width(int new_width)
{
    disp_width = new_width > 0 ? new_width : disp_width;
    need_width_ioctl = 1;
    return get_disp_width();
}

void init_disp_width(int new_width)
{
    disp_width = new_width > 0 ? new_width : disp_width;
    need_width_ioctl = 1;

    signal(SIGWINCH, sigwinch_handler);
}
#endif

/* ======================================================================== */
/*  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., 675 Mass Ave, Cambridge, MA 02139, USA.               */
/* ======================================================================== */
/*           Copyright (c) 1998-2010, Joseph Zbiciak, Tim Lindner           */
/* ======================================================================== */

