/*
 * 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> UlsContext.h </file>
  <brief>
  </brief>
  <author>
    Stanley Hong <link2next@gmail.com>, Oct. 2017.
  </author>
*/
#include "Stdafx.h"

#include "UlsUtil.h"
#include "UlsIStream.h"
#include "UlsOStream.h"
#include "UlsLex.h"
#include "UlsContext.h"

using namespace System;
using namespace uls::util;

namespace uls {
namespace polaris {
	//
	// UlsContext
	// 
	void UlsContext::initUlsContext()
	{
		onexit_list = nullptr;
		uf = UlsLex::getUlsFactory();
	}

	UlsContext::UlsContext()
	{
		initUlsContext();
		optfmt = gcnew String("vVh");
	}

	UlsContext::UlsContext(String ^optfmt_str)
	{
		initUlsContext();
		optfmt = optfmt_str;
	}

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

		flushProcsOnExit();

#ifdef _MANAGE_ULS_OBJECTS
		UlsOStream::releaseUlsObjectList();
		UlsIStream::releaseUlsObjectList();
		UlsLex::releaseUlsObjectList();

		UlsFactory::releaseUlsObjectList();
		uf = nullptr;

		UlsToolbase::releaseUlsObjectList();
		UlsLog::releaseUlsObjectList();
#else
		GC::Collect();
		GC::WaitForPendingFinalizers();
		GC::WaitForFullGCComplete();

		delete uf;
#endif
		UlsObject::deInitializeClass();
		isDisposed = true;
	}

	void UlsContext::registerProcOnExit(OnExitProc proc, Object^ data)
	{
		onexit_entry_t ^e = gcnew onexit_entry_t();

		UlsObject::sysLock();

		e->onexit = proc;
		e->data   = data;
		e->next = onexit_list;
		onexit_list = e;

		UlsObject::sysUnlock();
	}

	void UlsContext::flushProcsOnExit()
	{
		onexit_entry_ptr_t e, e_next;
		Object ^data;

		UlsObject::sysLock();

		for (e=onexit_list; e!=nullptr; e = e_next) {
			e_next = e->next;

			if ((data = e->data) == nullptr) {
				data = this;
			}

			e->onexit(data);
		}

		onexit_list = nullptr;

		UlsObject::sysUnlock();
	}

	int UlsContext::getopts_chars(array<String ^> ^args, const char *optfmt_cstr, OptProc proc)
	{
		String ^optarg;
		str2utf8bytes_t optstr_cpp;
		const char *optstr;
		int   opt, rc, i, j, k, l, stat=0;

		for (i=0; i<args->Length && stat == 0; i=k+1) {
			optarg = args[i];
			if (optarg[0] != '-') break;

			optstr = optstr_cpp.set(optarg->Substring(1));

			for (k=i,j=0; (opt = optstr[j]) != '\0'; ) {
				for (l=0; ; l++) {
					if (optfmt_cstr[l] == '\0') {
						Console::WriteLine("getopts: undefined option -{0}",  (Char) opt);
						return -1;
					}

					if (optfmt_cstr[l] == opt) {
						break;
					}
				}

				if (optfmt_cstr[l+1] == ':') { /* the option 'opt' needs a arg-val */
					optarg = gcnew String(optstr + j + 1);
					if (optarg->Length > 0) {
					} else if (k + 1 < args->Length && args[k + 1][0] != '-') {
						optarg = args[++k];
					} else {
						Console::WriteLine("getopts: option -{0} requires an arg.", (Char) opt);
						stat = -2; break;
					}

					j += (int) strlen(optstr + j);
				} else {
					optarg = nullptr;
					j++;
				}

				if ((rc=proc(opt, optarg)) != 0) {
					Console::WriteLine("getopts: semantic error in -{0} {1}.", (Char) opt, optarg);
					stat = rc; break;
				}
			}
		}

		return i;
	}

	int UlsContext::parseOpts(array<String ^> ^args, OptProc proc)
	{
		str2utf8bytes_t optfmt_cpp(optfmt);
		int rc;

		rc = getopts_chars(args, optfmt_cpp.cstr, proc);

		return rc;
	}

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