/*
 *  Copyright (C) 2002-2021  The DOSBox 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, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */


#ifndef DOSBOX_SHELL_H
#define DOSBOX_SHELL_H

#include "programs.h"

#include <SDL.h>
#if SDL_VERSION_ATLEAST(2, 0, 0)
#define SDL_STRING "SDL2"
#else
#define SDL_STRING "SDL1"
#endif

#define CMD_MAXLINE 4096
#define CMD_MAXCMDS 20
#define CMD_OLDSIZE 4096

#define CONFIG_SIZE 4096
#define AUTOEXEC_SIZE 4096

extern Bitu call_shellstop;
class DOS_Shell;

/* first_shell is used to add and delete stuff from the shell env 
 * by "external" programs. (config) */
extern DOS_Shell * first_shell;

const std::map<int, std::string> langcp_map {
	{437, "en_US"},
	//{850, "de_DE"},
    {852, "hu_HU"},
    {857, "tr_TR"},
	{858, "es_ES"},
	{859, "fr_FR"},
	{860, "pt_BR"},
	{866, "ru_RU"},
	{932, "ja_JP"},
	{936, "zh_CN"},
	{949, "ko_KR"},
	{950, "zh_TW"},
	{951, "zh_TW"},
};

class BatchFile {
public:
	BatchFile(DOS_Shell * host,char const* const resolved_name,char const* const entered_name, char const * const cmd_line);
	virtual ~BatchFile();
	virtual bool ReadLine(char * line);
	bool Goto(const char * where);
	void Shift(void);
	uint16_t file_handle;
	uint32_t location;
	bool echo;
	DOS_Shell * shell;
	BatchFile * prev;
	CommandLine * cmd;
	std::string filename;
};

class AutoexecEditor;

/*! \brief          DOS shell program object
 *
 *  \description    This is the DOS shell, including built-in commands
 */
class DOS_Shell : public Program {
private:
	friend class AutoexecEditor;
	std::list<std::string> l_history, l_completion;
    template<class _Type>
    struct less_ignore_case {
        typedef _Type first_argument_type;
        typedef _Type second_argument_type;
        typedef bool result_type;

        constexpr bool operator()(const _Type& _Left, const _Type& _Right) const {
            return strcasecmp(_Left.c_str(), _Right.c_str()) < 0;
        }
    };
    typedef std::map<std::string, std::string, less_ignore_case<std::string> > cmd_alias_map_t, cmd_assoc_map_t;
    cmd_alias_map_t cmd_alias;
    cmd_assoc_map_t cmd_assoc;
	uint16_t completion_index;
	
private:
	void ProcessCmdLineEnvVarStitution(char * line);
	std::string hasAssociation(const char* name);
	static bool hasExecutableExtension(const char* name);

public:

	bool free_your_own_psp = false; /* set to true if created directly, will not terminate through INT 21h, and therefore normal PSP and memory free will not happen */

	DOS_Shell();
	virtual ~DOS_Shell();

#if defined(OSFREE)
	bool OSFreeOperatingSystemNotFound(void);
#endif

	void Prepare(void);
    /*! \brief      Program entry point, when the command is run
     */
	void Run(void) override;

    /*! \brief      Alternate execution if /C switch is given
     */
	void RunInternal(void); //for command /C

/* A load of subfunctions */

    /*! \brief      Line parsing function
     */
	void ParseLine(char * line);

    /*! \brief      Redirection handling
     */
	Bitu GetRedirection(char *s, char **ifn, char **ofn, char **toc,bool * append);

    /*! \brief      Build Tab completion
     */
	bool BuildCompletions(char * line, uint16_t str_len);

    /*! \brief      Command line input and keyboard handling
     */
	void InputCommand(char * line);

    /*! \brief      Render and output command prompt
     */
	void ShowPrompt();

    /*! \brief      Process and execute command (internal or external)
     */
	void DoCommand(char * line);

    /*! \brief      Execute a command
     */
    bool Execute(char* name, const char* args);

	/*! \brief      Checks if it matches a hardware-property */
	bool CheckConfig(char* cmd_in,char*line);

    /*! \brief      Given a command, look up the path using default paths and the PATH variable
     */
	char * Which(char * name);

    /*! \brief      Command history list
     */
	void CMD_HISTORY(char * args);

    /*! \brief      Online HELP for the shell
     */
	void CMD_HELP(char * args);

    /*! \brief      Extended Ctrl+C switch
     */
	void CMD_BREAK(char * args);

    /*! \brief      Clear screen (CLS)
     */
	void CMD_CLS(char * args);

    /*! \brief      File copy command
     */
	void CMD_COPY(char * args);

    /*! \brief      Command to set date (DATE)
     */
	void CMD_DATE(char * args);

    /*! \brief      Command to set time (TIME)
     */
	void CMD_TIME(char * args);

    /*! \brief      Directory listing (DIR)
     */
	void CMD_DIR(char * args);

    /*! \brief      Deletion command (DEL)
     */
	void CMD_DELETE(char * args);

    /*! \brief      Delete directory tree (DELTREE)
     */
	void CMD_DELTREE(char * args);

    /*! \brief      Echo command (ECHO)
     */
	void CMD_ECHO(char * args);

    /*! \brief      Exit command (EXIT)
     */
	void CMD_EXIT(char * args);

    /*! \brief      Directory creation (MKDIR)
     */
	void CMD_MKDIR(char * args);

    /*! \brief      Change current directory (CD)
     */
	void CMD_CHDIR(char * args);

    /*! \brief      Directory deletion (RMDIR)
     */
	void CMD_RMDIR(char * args);

    /*! \brief      Environment variable setting/management (SET)
     */
	void CMD_SET(char * args);

    /*! \brief      Conditional execution (IF)
     */
	void CMD_IF(char * args);

    /*! \brief      Batch file branching (GOTO)
     */
	void CMD_GOTO(char * args);

    /*! \brief      Print file to console (TYPE)
     */
	void CMD_TYPE(char * args);

    /*! \brief      Human readable comment (REM)
     */
	void CMD_REM(char * args);

    /*! \brief      File rename (REN)
     */
	void CMD_RENAME(char * args);

    /*! \brief      Execute batch file as sub-program (CALL)
     */
	void CMD_CALL(char * args);

    /*! \brief      Print generic Syntax Error message to console
     */
	void SyntaxError(void);

    /*! \brief      Pause and wait for user to hit Enter (PAUSE)
     */
	void CMD_PAUSE(char * args);

    /*! \brief      Map drive letter to folder (SUBST)
     */
	void CMD_SUBST(char* args);

    /*! \brief      Load a program into high memory if possible
     */
	void CMD_LOADHIGH(char* args);

    /*! \brief      Prompt for a choice (CHOICE)
     */
	void CMD_CHOICE(char * args);

    /*! \brief      Set file attributes (ATTRIB)
     */
	void CMD_ATTRIB(char * args);

    /*! \brief      Set PATH variable (PATH)
     */
	void CMD_PATH(char * args);

    /*! \brief      Consume one command line argument (SHIFT)
     */
	void CMD_SHIFT(char * args);

    /*! \brief      List directory tree (TREE)
     */
	void CMD_TREE(char * args);

    /*! \brief      File verification switch
     */
	void CMD_VERIFY(char * args);

    /*! \brief      Print DOS version (VER)
     */
	void CMD_VER(char * args);

    /*! \brief      TODO?
     */
	void CMD_ADDKEY(char * args);

    /*! \brief      TODO?
     */
	void CMD_VOL(char * args);

    /*! \brief      Change DOS prompt pattern (PROMPT)
     */
	void CMD_PROMPT(char * args);

    /*! \brief      Text pager (MORE)
     */
	void CMD_MORE(char * args);

    /*! \brief      Change TTY (console) device (CTTY)
     */
	void CMD_CTTY(char * args);

    /*! \brief      Change country code
     */
	void CMD_CHCP(char * args);
	void CMD_COUNTRY(char * args);
	void CMD_PUSHD(char * args);
	void CMD_POPD(char * args);
    void CMD_TRUENAME(char * args);
    void CMD_DXCAPTURE(char * args);

    /*! \brief      Looping execution (FOR)
     */
	void CMD_FOR(char * args);

    /*! \brief      LFN switch for FOR
     */
	void CMD_LFNFOR(char * args);

    /*! \brief      ALIAS
    */
	void CMD_ALIAS(char* args);

    /*! \brief      ALIAS
    */
	void CMD_ASSOC(char* args);

    /*! \brief      VTEXT
    */
	void CMD_VTEXT(char *args);

    /*! \brief      LS
    */
	void CMD_LS(char *args);

#if C_DEBUG
    /*! \brief      Execute command within debugger (break at entry point)
     */
	void CMD_DEBUGBOX(char * args);

    /*! \brief      INT 2Fh debugging tool
     */
	void CMD_INT2FDBG(char * args);
#endif

	virtual bool execute_shell_cmd(char *name, char *arguments);

	/* The shell's variables */
	uint16_t input_handle;
	BatchFile * bf;                     //! Batch file to execute
	bool echo;
	bool exit;
	bool call;
    bool exec;
    bool perm;
	bool lfnfor;
    /* Status */
    bool input_eof;                     //! STDIN has hit EOF
    bool config_shell = false;
};

#if !defined(OSFREE)
class DOS_ConfigShell : public DOS_Shell {
public:
	DOS_ConfigShell();
	virtual ~DOS_ConfigShell();

    /*! \brief      Program entry point, when the command is run
     */
	void Run(void) override;
};
#endif

struct SHELL_Cmd {
	const char * name;								/* Command name*/
	uint32_t flags;									/* Flags about the command */
	void (DOS_Shell::*handler)(char * args);		/* Handler for this command */
	const char * help;								/* String with command help */
};

/* Object to manage lines in the autoexec.bat The lines get removed from
 * the file if the object gets destroyed. The environment is updated
 * as well if the line set a variable */
class AutoexecObject{
private:
	bool installed = false;
	std::string buf;
public:
	AutoexecObject() {};
	void Install(std::string const &in);
	void InstallBefore(std::string const &in);
	void Uninstall();
	~AutoexecObject();
private:
	void CreateAutoexec(void);
};

size_t GetPauseCount();

#endif
