/*
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

/*
  <file> UlsLex.cpp </file>
  <brief>
  </brief>
  <author>
    Stanley Hong <link2next@gmail.com>, April 2017.
  </author>
*/
#include "Stdafx.h"
#include "UlsUtil.h"
#include "UlsIStream.h"
#include "UlsLex.h"

#include "uls_lex_macros.h"

using namespace System;
using namespace uls::util;

namespace uls {
namespace polaris {
	//
	// UlsLex
	// 
#define ULS_CLASS_NAME UlsLex
	static ULS_CLASS_NAME::ULS_CLASS_NAME()
	{
		uf = gcnew UlsFactory();

		WANT_EOFTOK = uf->_uls_const_WANT_EOFTOK();
		FILE_UTF8 = uf->_uls_const_FILE_UTF8();

		LINE_NUMBERING = uf->_uls_const_LINE_NUMBERING();
		STREAM_BIN_LE = uf->_uls_const_STREAM_BIN_LE();
		STREAM_BIN_BE = uf->_uls_const_STREAM_BIN_BE();
		NEXTCH_NONE = uf->_uls_const_NEXTCH_NONE();

		objs_list = gcnew UlsObjectListExp();
	}

	UlsFactory ^ULS_CLASS_NAME::getUlsFactory()
	{
		return uf;
	}

	void ULS_CLASS_NAME::dumpSearchPathOfUlc(String ^confname)
	{
		str2utf8bytes_t confname_cppstr(confname);
		uf->ulc_list_searchpath(confname_cppstr.cstr);
	}

	void ULS_CLASS_NAME::listUlcSearchPaths()
	{
		uf->ulc_list_searchpath("simple");
	}

	ULS_CLASS_NAME::ULS_CLASS_NAME(String ^ulc_fpath)
	{
		str2utf8bytes_t ulc_fpath_cstr(ulc_fpath);
		UlsFactory::uls_lex_t ^lex;

		lex = uf->uls_create(ulc_fpath_cstr.cstr);
		if (lex == nullptr) {
			return;
		}
		uls = lex;

		toknum_EOI = uf->_uls_toknum_EOI(uls);
		toknum_EOF = uf->_uls_toknum_EOF(uls);
		toknum_ERR = uf->_uls_toknum_ERR(uls);
		toknum_NONE = uf->_uls_toknum_NONE(uls);
		toknum_ID = uf->_uls_toknum_ID(uls);
		toknum_NUMBER = uf->_uls_toknum_NUMBER(uls);
		toknum_TMPL = uf->_uls_toknum_TMPL(uls);

		Dic4ExtraTokdef = gcnew Dictionary<int, Object^>();

		UlsFactory::uls_tokid_simple_list_t ^toklst = uf->_uls_get_tokid_list_2(uls);
		int n_toks = toklst->n_tokid_list, *int_ary;
		int_ary = toklst->tokid_list;

		if (n_toks > 0) {
			for (int i=0; i<n_toks; i++) {
				int t = int_ary[i];
				Dic4ExtraTokdef[t] = nullptr;
			}
		}

		uf->_uls_put_tokid_list_2(toklst);
		isDisposed = isFinalized = false;
#ifdef _MANAGE_ULS_OBJECTS
		addUlsObject2List(objs_list);
#endif
	}

	void ULS_CLASS_NAME::finalizer()
	{
		if (isFinalized) return;
		uf->uls_destroy(uls);
		isFinalized = true;
	}

	ULS_CLASS_NAME::~ULS_CLASS_NAME()
	{
		if (isDisposed) return;

		finalizer();
#ifdef _MANAGE_ULS_OBJECTS
		delUlsObject2List(objs_list);
#endif
		Dic4ExtraTokdef = nullptr;
		isDisposed = true;
	}

	ULS_CLASS_NAME::!ULS_CLASS_NAME()
	{
		finalizer();
	}

	String ^ULS_CLASS_NAME::get_lexeme_lex()
	{
		String ^str;
		const char *ptr_res;
		int len;

		ptr_res = uf->uls_lexeme(uls);
		len = uf->uls_lexeme_len(uls);

		if (ptr_res == NULL)
		{
			ptr_res = "?";
		}

		str = gcnew String(ptr_res);
		return str;
	}

	String ^ULS_CLASS_NAME::get_tagstr_lex()
	{
		const char *ptr_res;
		int len=0;
			
		ptr_res = uf->uls_get_tag(uls);

		if (ptr_res == NULL) {
			ptr_res = "<none>";
		}
			
		return gcnew String(ptr_res);
	}

	int ULS_CLASS_NAME::TokNum::get()
	{
		return uf->_uls_tok_id(uls);
	}

	int ULS_CLASS_NAME::token::get()
	{
		return TokNum;
	}

	String ^ULS_CLASS_NAME::TokStr::get()
	{
		return get_lexeme_lex();
	}

	String ^ULS_CLASS_NAME::lexeme::get()
	{
		return TokStr;
	}

	String ^ULS_CLASS_NAME::Tag::get()
	{
		return gcnew String(get_tagstr_lex());
	}

	void ULS_CLASS_NAME::Tag::set(String ^value)
	{
		str2utf8bytes_t value_cppstr(value);
		uf->uls_set_tag(uls, value_cppstr.cstr, value_cppstr.len_cstr);
	}

	String ^ULS_CLASS_NAME::FileName::get()
	{
		return Tag;
	}

	void ULS_CLASS_NAME::FileName::set(String ^value)
	{
		Tag = value;
	}

	int ULS_CLASS_NAME::LineNum::get()
	{
		return uf->uls_get_lineno(uls);
	}

	void ULS_CLASS_NAME::LineNum::set(int value)
	{
		uf->_uls_set_lineno(uls, value);
	}

	bool ULS_CLASS_NAME::isEOI::get()
	{
		int t = uf->_uls_tok_id(uls);
		return t == toknum_EOI;
	}

	bool ULS_CLASS_NAME::isEOF::get()
	{
		int t = uf->_uls_tok_id(uls);
		return t == toknum_EOF;
	}
	
	bool ULS_CLASS_NAME::isERR::get()
	{
		int t = uf->_uls_tok_id(uls);
		return t == toknum_ERR;
	}

	String ^ULS_CLASS_NAME::lexemeNumberSuffix::get()
	{
		UlsToolbase::uls_outparam_t parms;
		const char *ptr_res;
		int len;

		ptr_res = uf->_uls_number_suffix(uls, uls_ptr(parms));
		if (ptr_res == NULL) {
			ptr_res = "";
			len = 0;
		} else {
			len = parms.len;
		}

		return gcnew String(ptr_res);
	}

	bool ULS_CLASS_NAME::isLexemeReal::get()
	{
		int rval = uf->uls_is_real(uls);
		return (rval!=0) ? true : false;
	}

	bool ULS_CLASS_NAME::isLexemeInt::get()
	{
		int rval = uf->uls_is_int(uls);
		return (rval != 0) ? true : false;
	}

	bool ULS_CLASS_NAME::isLexemeZero::get()
	{
		int rval = uf->uls_is_zero(uls);
		return (rval != 0) ? true : false;
	}

	Int32 ULS_CLASS_NAME::lexemeAsInt32::get()
	{
		Int32 i32_val;
		i32_val = (Int32) uf->uls_lexeme_int32(uls);
		return i32_val;
	}

	UInt32 ULS_CLASS_NAME::lexemeAsUInt32::get()
	{
		UInt32 u32_val;
		u32_val = (UInt32) uf->uls_lexeme_uint32(uls);
		return u32_val;
	}

	Int64 ULS_CLASS_NAME::lexemeAsInt64::get()
	{
		Int64 i64_val;
		i64_val = (Int64) uf->uls_lexeme_int64(uls);
		return i64_val;
	}

	UInt64 ULS_CLASS_NAME::lexemeAsUInt64::get()
	{
		UInt64 u64_val;
		u64_val = (UInt64) uf->uls_lexeme_uint64(uls);
		return u64_val;
	}

	Double ULS_CLASS_NAME::lexemeAsDouble::get()
	{
		Double d64_val;
		d64_val = (Double) uf->uls_lexeme_double(uls);
		return d64_val;
	}

	UInt32 ULS_CLASS_NAME::skipBlanks()
	{
		uf->uls_skip_blanks(uls);
		return uf->uls_peek_uch(uls, nullptr);
	}

	void ULS_CLASS_NAME::deleteLiteralAnalyzer(String ^pfx)
	{
		str2utf8bytes_t pfx_cppstr(pfx);

		uf->uls_xcontext_delete_litstr_analyzer(uf->_uls_get_xcontext(uls), pfx_cppstr.cstr);
	}

	void ULS_CLASS_NAME::changeLiteralAnalyzer(String ^pfx, UlsFactory::uls_litstr_analyzer_t proc, Object ^data)
	{
		str2utf8bytes_t pfx_cppstr(pfx);

		uf->uls_xcontext_change_litstr_analyzer(uf->_uls_get_xcontext(uls),
			pfx_cppstr.cstr, proc, data);
	}

	UInt32 ULS_CLASS_NAME::peekCh(GetchDetailInfo ^uch_detail)
	{
		UlsFactory::uls_nextch_detail_t detail;
		uls_uch_t uch;

		uch = uf->uls_peek_uch(uls, uls_ptr(detail));

		if (uch_detail != nullptr) {
			uch_detail->uch = uch;
			uch_detail->tok_id = detail.tok;
			uch_detail->is_quote = (detail.qmt != nullptr) ? true : false;
		}

		return uch;
	}

	UInt32 ULS_CLASS_NAME::peekCh()
	{
		return peekCh(nullptr);
	}

	UInt32 ULS_CLASS_NAME::getCh(GetchDetailInfo ^uch_detail)
	{
		UlsFactory::uls_nextch_detail_t detail;
		uls_uch_t uch;

		uch = uf->uls_get_uch(uls, uls_ptr(detail));

		if (uch_detail != nullptr) {
			uch_detail->uch = uch;
			uch_detail->tok_id = detail.tok;
			uch_detail->is_quote = (detail.qmt != nullptr) ? true : false;
		}

		return uch;
	}

	UInt32 ULS_CLASS_NAME::getCh()
	{
		return getCh(nullptr);
	}

	bool ULS_CLASS_NAME::isQuoteTok(int tok_id)
	{
		return uf->uls_is_quote_tok(uls, tok_id) != 0 ? true : false;
	}

	void ULS_CLASS_NAME::ungetCh(UInt32 uch)
	{
		if (uch != NEXTCH_NONE) {
			uf->uls_unget_ch(uls, uch);
		}
	}

	bool ULS_CLASS_NAME::isSpace(UInt32 uch)
	{
		return uf->_uls_is_ch_space(uls, uch) != 0 ? true : false;
	}

	bool ULS_CLASS_NAME::isIdfirst(UInt32 uch)
	{
		return uf->_uls_is_ch_idfirst(uls, uch) != 0 ? true : false;
	}

	bool ULS_CLASS_NAME::isId(UInt32 uch)
	{
		return uf->_uls_is_ch_id(uls, uch) != 0 ? true : false;
	}

	bool ULS_CLASS_NAME::isQuote(UInt32 uch)
	{
		return uf->_uls_is_ch_quote(uls, uch) != 0 ? true : false;
	}

	bool ULS_CLASS_NAME::is1CharTok(UInt32 uch)
	{
		return uf->_uls_is_ch_1ch_token(uls, uch) != 0 ? true : false;
	}

	bool ULS_CLASS_NAME::is2CharTok(UInt32 uch)
	{
		return uf->_uls_is_ch_2ch_token(uls, uch) != 0 ? true : false;
	}

	int ULS_CLASS_NAME::getTok()
	{
		int t = uf->uls_get_tok(uls);
		return t;
	}

	int ULS_CLASS_NAME::getToken()
	{
		return ULS_CLASS_NAME::getTok();
	}

	int ULS_CLASS_NAME::next()
	{
		return ULS_CLASS_NAME::getTok();
	}

	void ULS_CLASS_NAME::expect(int TokExpected)
	{
		uf->uls_expect(uls, TokExpected);
	}

	void ULS_CLASS_NAME::setTok(int t, String ^str)
	{
		str2utf8bytes_t strbuf4cvt(str);

		uf->uls_set_tok(uls, t, strbuf4cvt.cstr, strbuf4cvt.len_cstr);
	}

	void ULS_CLASS_NAME::ungetTok()
	{
		uf->uls_unget_tok(uls);
	}

	void ULS_CLASS_NAME::ungetStr(String ^str)
	{
		str2utf8bytes_t str2_cpp(str);

		uf->uls_unget_str(uls, str2_cpp.cstr);
	}

	void ULS_CLASS_NAME::ungetLexeme(String ^lxm, int tok_id)
	{
		str2utf8bytes_t lxm2_cpp(lxm);

		uf->uls_unget_lexeme(uls, lxm2_cpp.cstr, tok_id);
	}

	void ULS_CLASS_NAME::dumpTok(String ^pfx, String ^suff)
	{
		str2utf8bytes_t pfx2_cpp(pfx);
		str2utf8bytes_t suff2_cpp(suff);

		uf->uls_dump_tok(uls, pfx2_cpp.cstr, suff2_cpp.cstr);
	}

	void ULS_CLASS_NAME::dumpTok()
	{
		uf->uls_dump_tok(uls, "\t", "\n");
	}

	void ULS_CLASS_NAME::dumpTok(String ^pfx, String ^idstr, String ^tstr, String ^suff)
	{
		str2utf8bytes_t pfx2_cpp(pfx);
		str2utf8bytes_t idstr2_cpp(idstr);
		str2utf8bytes_t tstr2_cpp(tstr);
		str2utf8bytes_t suff2_cpp(suff);

		uf->_uls_dump_tok_2(uls, pfx2_cpp.cstr, idstr2_cpp.cstr, tstr2_cpp.cstr, suff2_cpp.cstr);
	}

	String ^ULS_CLASS_NAME::keyword(int t)
	{
		UlsToolbase::uls_outparam_t parms;
		const char *ptr_res;
		int len;

		ptr_res = uf->uls_tok2keyw_2(uls, t, uls_ptr(parms));
		if (ptr_res == NULL) {
			ptr_res = "<unknown>";
			len = (int) strlen(ptr_res);
		} else {
			len = parms.len;
		}

		return gcnew String(ptr_res);
	}

	String ^ULS_CLASS_NAME::keyword()
	{
		return keyword(TokNum);
	}

	String ^ULS_CLASS_NAME::nameOf(int t)
	{
		UlsToolbase::uls_outparam_t parms;
		const char *ptr_res;
		int len;

		ptr_res = uf->uls_tok2name_2(uls, t, uls_ptr(parms));
		if (ptr_res == NULL) {
			ptr_res = "<unknown>";
			len = (int) strlen(ptr_res);
		} else {
			len = parms.len;
		}

        return gcnew String(ptr_res);
	}

	String ^ULS_CLASS_NAME::nameOf()
	{
		return nameOf(TokNum);
	}

	void ULS_CLASS_NAME::setLineNum(int lineno)
	{
		uf->_uls_set_lineno(uls, lineno);
	}

	void ULS_CLASS_NAME::addLineNum(int amount)
	{
		uf->_uls_inc_lineno(uls, amount);
	}

	void ULS_CLASS_NAME::setFileName(String ^fname)
	{
		str2utf8bytes_t fname2_cpp(fname);

		uf->uls_set_tag(uls, fname2_cpp.cstr, 1);
	}

	int ULS_CLASS_NAME::get_uls_flags(InputOpts fl)
	{
		int ret_fl;

		switch (fl) {
		case InputOpts::WantEOF:
			ret_fl = WANT_EOFTOK;
			break;
		case InputOpts::Utf8Encoding:
			ret_fl = FILE_UTF8;
			break;
		default:
			ret_fl = -1;
			break;
		}

		return ret_fl;
	}

	void ULS_CLASS_NAME::setInputOpt(InputOpts fl)
	{
		int uls_fl = get_uls_flags(fl);
		input_flags |=  uls_fl;
	}

	void ULS_CLASS_NAME::clearInputOpt(InputOpts fl)
	{
		int uls_fl = get_uls_flags(fl);
		input_flags &=  ~uls_fl;
	}

	int ULS_CLASS_NAME::getInputOpts()
	{
		return input_flags;
	}

	void ULS_CLASS_NAME::resetInputOpts()
	{
		input_flags = 0;
	}

	bool ULS_CLASS_NAME::pushFile(String ^filepath)
	{
		str2utf8bytes_t filepath2_cpp(filepath);
		int rc, flags = getInputOpts();
		bool rval = true;

		rc = uf->uls_push_file(uls, filepath2_cpp.cstr, flags);
		if (rc < 0) {
			rval = false;
		}

		return rval;
	}

	void ULS_CLASS_NAME::pushFile(String ^filepath, bool want_eof)
	{
		str2utf8bytes_t filepath2_cpp(filepath);
		int rc, flags = 0;
		
		if (want_eof) {
			flags |= WANT_EOFTOK;
		}
		
		try {
			rc = uf->uls_push_file(uls, filepath2_cpp.cstr, flags);
		} catch (IO::IOException ^e) {
			Console::WriteLine("UlsCppNet: {0}", e);
			throw;
		}
	}

	void ULS_CLASS_NAME::setFile(String ^filepath, bool want_eof)
	{
		str2utf8bytes_t filepath2_cpp(filepath);
		int rc, flags = 0;

		if (want_eof) {
			flags |= WANT_EOFTOK;
		}

		try {
			rc = uf->uls_set_file(uls, filepath2_cpp.cstr, flags);
		} catch (IO::IOException ^e) {
			Console::WriteLine("UlsCppNet: {0}", e);
			throw;
		}
	}

	void ULS_CLASS_NAME::pushInput(String ^line)
	{
		str2utf8bytes_t line2_cpp(line);
		int rc, flags = getInputOpts();

		rc = uf->uls_push_line(uls, line2_cpp.cstr, line2_cpp.len_cstr, flags);
		if (rc < 0) {
			Console::WriteLine("Invalid param 'line'!");
		}
	}

	void ULS_CLASS_NAME::pushLine(String ^line, bool want_eof)
	{
		str2utf8bytes_t line2_cpp(line);
		int flags = 0;

		if (want_eof) {
			flags |= WANT_EOFTOK;
		}

		uf->uls_push_line(uls, line2_cpp.cstr, line2_cpp.len_cstr, flags);
	}

	void ULS_CLASS_NAME::setLine(String ^line, bool want_eof)
	{
		str2utf8bytes_t line2_cpp(line);
		int flags = 0;

		if (want_eof) {
			flags |= WANT_EOFTOK;
		}

		uf->uls_set_line(uls, line2_cpp.cstr, line2_cpp.len_cstr, flags);
	}

	bool ULS_CLASS_NAME::pushInput(UlsIStream ^in_str)
	{
		UlsTmplList ^TmplList = in_str->getTmplList();
		UlsFactory::uls_tmpl_list_t ^tmpl_list;
		int flags = getInputOpts();
		bool retval = true;

		tmpl_list = TmplList->exportTmpls();

		if (uf->uls_push_istream(getCore(),
			(UlsFactory::uls_istream_t ^) in_str->getCoreHdr(), tmpl_list, flags) < 0) {
			retval = false;
		}

		uf->uls_destroy_tmpls(tmpl_list);

		return retval;
	}

	void ULS_CLASS_NAME::popInput()
	{
		uf->uls_pop(uls);
	}

	void ULS_CLASS_NAME::popAllInputs()
	{
		uf->uls_pop_all(uls);
	}

	void ULS_CLASS_NAME::setExtraTokdef(int t, Object ^o)
	{
			// overriding the existing one ...
        if (Dic4ExtraTokdef->ContainsKey(t))
        {
            Dic4ExtraTokdef[t] = o;
        }
        else
        {
            Console::WriteLine("No key exists corresponding to {0}!", t);
        }
	}

	Object ^ULS_CLASS_NAME::getExtraTokdef(int t)
	{
		Object ^o = nullptr;

        if (Dic4ExtraTokdef->ContainsKey(t))
        {
            o = Dic4ExtraTokdef[t];
        }
        else
        {
            Console::WriteLine("The user-defined token information of key {0} Not specified!", t);
        }

        return o;
	}

	Object ^ULS_CLASS_NAME::getExtraTokdef()
	{
		return getExtraTokdef(TokNum);
	}

	Object ^ULS_CLASS_NAME::ExtraTokdef::get()
	{
		int t = uf->_uls_tok_id(uls);
        return getExtraTokdef(t);
	}

	UlsFactory::uls_lex_t ^ULS_CLASS_NAME::getCore()
	{
		return uls;
	}

#undef ULS_CLASS_NAME

} // End of polaris
} // End of uls
