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

using namespace System;
using namespace System::Text;

namespace uls {
namespace util {
	//
	// str2utf8bytes_t
	// 
	str2utf8bytes_t::str2utf8bytes_t(String ^str)
	{
		isDisposed = false;
		set(str);
	}

	str2utf8bytes_t::str2utf8bytes_t()
	{
		isDisposed = false;
		cstr = NULL;
		len_cstr = siz_cstr = 0;
	}

	void str2utf8bytes_t::finalizer()
	{
		deinit();
	}

	str2utf8bytes_t::~str2utf8bytes_t()
	{
		if (isDisposed) return;
		finalizer();
		isDisposed = true;
	}

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

	void str2utf8bytes_t::reset(int siz)
	{
		resize_str2buf(siz);
	}

	void str2utf8bytes_t::deinit()
	{
		reset(0);
	}

	void str2utf8bytes_t::resize_str2buf(int siz)
	{
		if (siz <= 0) {
			if (siz_cstr > 0) {
				free(cstr); siz_cstr = 0;
			}
			cstr = NULL;
			len_cstr = 0;
		} else {
			if (siz > siz_cstr) {
				if ((siz = (siz + 128) >> 7) <= 0) siz = 1;
				siz <<= 7;
				if (cstr == NULL) {
					cstr = (char *) malloc(siz);
				} else {
					cstr = (char *) realloc(cstr, siz);
				}
				siz_cstr = siz;
			}
		}
	}

	char *str2utf8bytes_t::set(String ^str)
	{
		array<Byte> ^utf8_bytes;
		char *cptr;
		int i;

		utf8_bytes = Text::Encoding::UTF8->GetBytes(str);
		resize_str2buf(utf8_bytes->Length + 1);

		for (cptr=cstr, i=0; i < utf8_bytes->Length; i++) {
			*cptr++ = (char) utf8_bytes[i];
		}
		*cptr = '\0';

		len_cstr = utf8_bytes->Length;
		return cstr;
	}

	String ^str2utf8bytes_t::cvt_utf8bytes(const char *cstr, int len)
	{
		array<Byte> ^utf8_bytes;
		String ^str;
		int i;

		if (len < 0) {
			for (len = 0; cstr[len] != '\0'; len++) /* NOTHING */;
		}

		if (len == 0) {
			return gcnew String("");
		}

		utf8_bytes = gcnew array<Byte>(len);

		for (i=0; i < len; i++) {
			utf8_bytes[i] = (Byte) cstr[i];
		}

		str = Text::Encoding::UTF8->GetString(utf8_bytes);

		return str;
	}
	
	String ^str2utf8bytes_t::cvt_utf8bytes(const char *cstr)
	{
		return cvt_utf8bytes(cstr, -1);
	}

	//
	// UlsTools
	// 
	UlsTools::UlsTools()
	{
	}

	UlsTools::~UlsTools()
	{

	}

	int UlsTools::getopts_chars(array<String ^> ^args, const char *optfmt_cstr, uls_getopts_proc_t 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 UlsTools::getopts(array<String ^> ^args, String ^optfmt, uls_getopts_proc_t proc)
	{
		str2utf8bytes_t optfmt_cpp(optfmt);
		int rc;

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

		return rc;
	}

	String ^UlsTools::uls_filename(String ^filepath)
	{
		String ^fname;
		int i;

		if ((i = filepath->LastIndexOf('\\')) < 0) {
			fname = filepath;
		} else {
			fname = filepath->Substring(i+1);
		}

		return fname;
	}

}
}
