/*
	Copyright (C) 2022-2024 Mark Tyler

	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 3 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 in the file COPYING.
*/

#include "nush.h"



int main (
	int			const	argc,
	char	const * const * const	argv
	)
{
	Core	core;

	if ( core.command_line ( argc, argv ) )
	{
		return core.exit.value();
	}

// Future expansion

	return core.exit.value();
}



int Core::evaluate_line ( char const * const text )
{
	if ( numeval.evaluate ( text ) )
	{
		std::cerr << numeval.error() << "\n";
		return 0;
	}

	if ( m_verbose )
	{
		std::cout << text << "\n";
	}

	std::cout << numeval.get_result();

	return 0;		// Continue parsing next item
}

int Core::command_line (
	int			const	argc,
	char	const * const *	const	argv
	)
{
	mtKit::Arg args ( [this]( char const * const text )
		{
			return evaluate_line ( text );
		} );

	auto print_version = []()
	{
		printf ( BIN_NAME " - Part of %s\n\n", VERSION );
		return 1;		// Stop parsing
	};

	auto print_help = [print_version]()
	{
		print_version ();
		printf ("For further information consult the man page "
			"%s(1) or the mtCellEdit Handbook.\n"
			"\n"
			, BIN_NAME );
		return 1;		// Stop parsing
	};

	int stop = 0, inum;

	args.add ( "-help",	stop, 1, print_help );
	args.add ( "-version",	stop, 1, print_version );
	args.add ( "b",		inum, [&inum, this]()
		{
			numeval.set_number_size ( inum );

			if ( m_verbose )
			{
				std::cout << "New number bits = "
					<< numeval.num_bits()
					<< " significant digits = "
					<< numeval.get_float_digit_total()
					<< "\n";
			}

			return 0;
		} );
	args.add ( "cli",	[this]() { return cli_mode (); } );
	args.add ( "funcs",	[this]() { return numeval.print_funcs (); } );
	args.add ( "i",		m_arg_i, [this]() { return argcb_i(); } );
	args.add ( "integer",	[this]()
		{
			numeval.set_mode ( NumEval::MODE_INTEGER );
			return 0;
		});
	args.add ( "double",	[this]()
		{
			numeval.set_mode ( NumEval::MODE_DOUBLE );
			return 0;
		});
	args.add ( "float",	[this]()
		{
			numeval.set_mode ( NumEval::MODE_FLOAT );
			return 0;
		});
	args.add ( "rational",	[this]()
		{
			numeval.set_mode ( NumEval::MODE_RATIONAL );
			return 0;
		});
	args.add ( "v",		m_verbose, 1 );
	args.add ( "vars",	[this]() { return numeval.print_vars (); } );

	if ( args.parse ( argc, argv ) || stop )
	{
		return 1;		// Quit program
	}

	return 0;			// Continue program
}

int Core::cli_mode ()
{
	while ( false == exit.aborted () )
	{
		std::string const & line = m_clishell.read_line( BIN_NAME" > ");

		if (	m_clishell.finished () ||
			(line[0] == '.' && line[1] == 0)
			)
		{
			break;
		}
		else if ( line[0] == '#' )
		{
			// Comment
		}
		else if ( line[0] )
		{
			// String isn't empty so do something

			m_clishell.add_history ();

			if ( evaluate_line ( line.c_str() ) )
			{
				break;
			}
		}
		else
		{
			// Quietly ignore empty lines
		}
	}

	return 0;		// Continue parsing
}



/// ----------------------------------------------------------------------------



class LoadLine
{
public:
	LoadLine () {}
	~LoadLine () { set_line ( nullptr ); }

	void set_line ( char * txt )	{ free ( m_line ); m_line = txt; }

	void set_fp_stdin ()		{ m_fp = stdin; }
	int set_fp_filename ( char const * filename );

	char * load_line ();
		// nullptr => EOF or error

private:
	char		* m_line = nullptr;	// Owned
	FILE		* m_fp = nullptr;	// Not owned

	mtKit::ByteFileRead	m_file;

	MTKIT_RULE_OF_FIVE( LoadLine )
};



int LoadLine::set_fp_filename ( char const * const filename )
{
	if ( m_file.open ( filename, 0 ) )
	{
		return 1;
	}

	m_fp = m_file.get_fp();

	return 0;
}

char * LoadLine::load_line ()
{
	char * txt = mtkit_file_readline ( m_fp, nullptr, nullptr );

	set_line ( txt );

	return txt;
}



/// ----------------------------------------------------------------------------



int Core::argcb_i ()
{
	if ( ! m_arg_i )
	{
		return 1;
	}

	LoadLine ll;

	if ( m_arg_i[0] == '-' && m_arg_i[1] == 0 )
	{
		ll.set_fp_stdin ();
	}
	else
	{
		if ( ll.set_fp_filename ( m_arg_i ) )
		{
			std::cerr << "Unable to open file '" << m_arg_i
				<< "'\n";
			return 1;
		}
	}

	while ( char * txt = ll.load_line() )
	{
		evaluate_line ( txt );
	}

	return 0;
}

