/*
 *  R : A Computer Language for Statistical Data Analysis
 *  Copyright (C) 2000-2021 The R Core Team
 *
 *  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, a copy is available at
 *  https://www.R-project.org/Licenses/
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <Defn.h> /* for checkArity */
#include <Internal.h>

#undef _
#ifdef ENABLE_NLS
#include <libintl.h>
#define _(String) dgettext ("utils", String)
#else
#define _(String) (String)
#endif


#ifdef Win32
# include "Startup.h"
# include "getline/getline.h"     /* for gl_load/savehistory */
# include "getline/wc_history.h"  /* for wgl_load/savehistory */
SEXP savehistory(SEXP call, SEXP op, SEXP args, SEXP env)
{
    SEXP sfile;

    args = CDR(args);
    sfile = CAR(args);
    if (!isString(sfile) || LENGTH(sfile) < 1)
	errorcall(call, _("invalid '%s' argument"), "file");
    if (CharacterMode == RGui) {
	R_setupHistory(); /* re-read the history size */
	wgl_savehistoryW(filenameToWchar(STRING_ELT(sfile, 0), 0), 
			 R_HistorySize);
    } else if (R_Interactive && CharacterMode == RTerm) {
	R_setupHistory(); /* re-read the history size */
	gl_savehistory(translateChar(STRING_ELT(sfile, 0)), R_HistorySize);
    } else
	errorcall(call, _("'savehistory' can only be used in Rgui and Rterm"));
    return R_NilValue;
}

SEXP loadhistory(SEXP call, SEXP op, SEXP args, SEXP env)
{
    SEXP sfile;

    args = CDR(args);
    sfile = CAR(args);
    if (!isString(sfile) || LENGTH(sfile) < 1)
	errorcall(call, _("invalid '%s' argument"), "file");
    if (CharacterMode == RGui)
	wgl_loadhistoryW(filenameToWchar(STRING_ELT(sfile, 0), 0));
    else if (R_Interactive && CharacterMode == RTerm)
	gl_loadhistory(translateChar(STRING_ELT(sfile, 0)));
    else
	errorcall(call, _("'loadhistory' can only be used in Rgui and Rterm"));
    return R_NilValue;
}

SEXP addhistory(SEXP call, SEXP op, SEXP args, SEXP env)
{
    SEXP stamp;
    const void *vmax = vmaxget();

    args = CDR(args);
    stamp = CAR(args);
    if (!isString(stamp))
	errorcall(call, _("invalid timestamp"));
    if (CharacterMode == RGui) {   
	for (int i = 0; i < LENGTH(stamp); i++) 
	    wgl_histadd(wtransChar(STRING_ELT(stamp, i)));
    } else if (R_Interactive && CharacterMode == RTerm) {
    	for (int i = 0; i < LENGTH(stamp); i++)
	    gl_histadd(translateChar(STRING_ELT(stamp, i)));
    }
    vmaxset(vmax);
    return R_NilValue;
}

SEXP Win_dataentry(SEXP args);
SEXP Win_dataviewer(SEXP args);

SEXP dataentry(SEXP call, SEXP op, SEXP args, SEXP rho)
{
    return Win_dataentry(CDR(args));
}

SEXP dataviewer(SEXP call, SEXP op, SEXP args, SEXP rho)
{
    return Win_dataviewer(CDR(args));
}

SEXP Win_selectlist(SEXP args);

SEXP selectlist(SEXP call, SEXP op, SEXP args, SEXP rho)
{
    return Win_selectlist(CDR(args));
}

#else

#define R_INTERFACE_PTRS 1
#include <Rinterface.h>

SEXP loadhistory(SEXP call, SEXP op, SEXP args, SEXP rho)
{
    ptr_R_loadhistory(call, op, CDR(args), rho);
    return R_NilValue;
}

SEXP savehistory(SEXP call, SEXP op, SEXP args, SEXP rho)
{
    ptr_R_savehistory(call, op, CDR(args), rho);
    return R_NilValue;
}

SEXP addhistory(SEXP call, SEXP op, SEXP args, SEXP rho)
{
    if(ptr_R_addhistory) ptr_R_addhistory(call, op, CDR(args), rho);
    return R_NilValue;
}

#ifdef HAVE_X11

#include <Rdynpriv.h>
#include <Rmodules/RX11.h>   /* typedefs for the module routine types */
static R_deRoutines de_routines, *de_ptr = &de_routines;

static void R_de_Init(void)
{
    static int de_init = 0;

    if(de_init > 0) return;
    if(de_init < 0) error(_("X11 dataentry cannot be loaded"));

    de_init = -1;
    if(strcmp(R_GUIType, "none") == 0) {
	warning(_("X11 is not available"));
	return;
    }
    int res = R_moduleCdynload("R_de", 1, 1);
    if(!res) error(_("X11 dataentry cannot be loaded"));
    de_ptr->de = (R_X11DataEntryRoutine) 
	R_FindSymbol("in_RX11_dataentry", "R_de", NULL);
    de_ptr->dv = (R_X11DataViewer) 
	R_FindSymbol("in_R_X11_dataviewer", "R_de", NULL);
    de_init = 1;
    return;
}

static SEXP X11_do_dataentry(SEXP call, SEXP op, SEXP args, SEXP rho)
{
    R_de_Init();
    return (*de_ptr->de)(call, op, args, rho);
}

static SEXP X11_do_dataviewer(SEXP call, SEXP op, SEXP args, SEXP rho)
{
    R_de_Init();
    return (*de_ptr->dv)(call, op, args, rho);
}

#else /* no X11 */

static SEXP X11_do_dataentry(SEXP call, SEXP op, SEXP args, SEXP rho)
{
    error(_("X11 is not available"));
    return R_NilValue;
}

static SEXP X11_do_dataviewer(SEXP call, SEXP op, SEXP args, SEXP rho)
{
    error(_("X11 is not available"));
    return R_NilValue;
}
#endif

SEXP dataentry(SEXP call, SEXP op, SEXP args, SEXP env)
{
    args = CDR(args);
    if(ptr_do_dataentry) return ptr_do_dataentry(call, op, args, env);
    else return X11_do_dataentry(call, op, args, env);
}

SEXP dataviewer(SEXP call, SEXP op, SEXP args, SEXP env)
{
    args = CDR(args);
    if(ptr_do_dataviewer) return ptr_do_dataviewer(call, op, args, env);
    else return X11_do_dataviewer(call, op, args, env);
}

SEXP selectlist(SEXP call, SEXP op, SEXP args, SEXP env)
{
    if(ptr_do_selectlist) return ptr_do_selectlist(call, op, CDR(args), env);
    return R_NilValue;
}
#endif

SEXP edit(SEXP call, SEXP op, SEXP args, SEXP rho)
{
    return do_edit(call, op, CDR(args), rho);
}

SEXP flushconsole(void)
{
    R_FlushConsole();
    return R_NilValue;
}

SEXP processevents(void)
{
    R_ProcessEvents();
    return R_NilValue;
}

// formerly in src/main/platform.c
SEXP fileedit(SEXP call, SEXP op, SEXP args, SEXP rho)
{
    SEXP fn, ti, ed;
    const char **f, **title, *editor;
    int i, n;
    const void *vmax = vmaxget();

    args = CDR(args);
    fn = CAR(args); args = CDR(args);
    ti = CAR(args); args = CDR(args);
    ed = CAR(args);

    n = length(fn);
    if (!isString(ed) || length(ed) != 1)
	error(_("invalid '%s' specification"), "editor");
    if (n > 0) {
	if (!isString(fn))
	    error(_("invalid '%s' specification"), "filename");
	for (i = 0; i < n; i++)
	    if (STRING_ELT(fn, i) == NA_STRING)
		error(_("'%s' contains missing values"), "filename");
	f = (const char**) R_alloc(n, sizeof(char*));
	title = (const char**) R_alloc(n, sizeof(char*));
	/* FIXME convert to UTF-8 on Windows */
	for (i = 0; i < n; i++) {
	    SEXP el = STRING_ELT(fn, 0);
	    if (!isNull(el))
#ifdef Win32
		f[i] = acopy_string(reEnc(CHAR(el), getCharCE(el), CE_UTF8, 1));
#else
		f[i] = acopy_string(translateChar(el));
#endif
	    else
		f[i] = "";
	    if (!isNull(STRING_ELT(ti, i)))
		title[i] = acopy_string(translateChar(STRING_ELT(ti, i)));
	    else
		title[i] = "";
	}
    }
    else {  /* open a new file for editing */
	n = 1;
	f = (const char**) R_alloc(1, sizeof(char*));
	f[0] = "";
	title = (const char**) R_alloc(1, sizeof(char*));
	title[0] = "";
    }
    SEXP ed0 = STRING_ELT(ed, 0);
#ifdef Win32
    editor = acopy_string(reEnc(CHAR(ed0), getCharCE(ed0), CE_UTF8, 1));
#else
    editor = acopy_string(translateChar(ed0));
#endif
    R_EditFiles(n, f, title, editor);
    vmaxset(vmax);
    return R_NilValue;
}

#ifdef Win32
SEXP in_loadRconsole(SEXP);
SEXP in_shortpath(SEXP);

SEXP loadRconsole(SEXP file)
{
    return in_loadRconsole(file);
}

SEXP shortpath(SEXP paths)
{
    return in_shortpath(paths);
}

#endif

/* called from tar() */
SEXP octsize(SEXP size)
{
    double s = asReal(size);
    SEXP ans = allocVector(RAWSXP, 11);
    Rbyte *ra = RAW(ans);
    if (!R_FINITE(s) && s >= 0) error("size must be finite and >= 0");
    /* We have to be able to do this on a 32-bit system */
    for (int i = 0; i < 11; i++) {
	double s2 = floor(s/8.);
	double t = s - 8.*s2;
	s = s2;
	ra[10-i] = (Rbyte) (48 + t); // as ASCII
    }
    return ans;
}
