// K-3D
// Copyright (c) 1995-2004, Timothy M. Shead
//
// Contact: tshead@k-3d.com
//
// 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

/** \file
		\brief Implements objects for wrapping C++ iostreams
		\author Tim Shead (tshead@k-3d.com)
*/

#include <k3dsdk/result.h>
#include <boost/cstdint.hpp>

#include <fstream>
#include <iostream>
#include <vector>

#include <jsapi.h>

namespace
{

std::istream* istream_storage(JSContext* Context, JSObject* Object);

JSBool istream_constructor(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
	if(JSVAL_IS_STRING(argv[0]))
		{
			JS_SetPrivate(Context, Object, new std::ifstream(JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))));
			return JS_TRUE;
		}

	return JS_FALSE;
}

void istream_destructor(JSContext* Context, JSObject* Object)
{
	delete istream_storage(Context, Object);
}

JSBool istream_eof(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
	*Result = BOOLEAN_TO_JSVAL(istream_storage(Context, Object)->eof());
	return JS_TRUE;
}

JSBool istream_get(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
	char c = istream_storage(Context, Object)->get();
	*Result = STRING_TO_JSVAL(JS_NewStringCopyN(Context, &c, 1));
	return JS_TRUE;
}

JSBool istream_getline(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
	std::string line;
	std::getline(*istream_storage(Context, Object), line);
	*Result = STRING_TO_JSVAL(JS_NewStringCopyZ(Context, line.c_str()));
	return JS_TRUE;
}

JSBool istream_read(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
	boost::int32_t count = 0;
	jsint __count;
	return_val_if_fail(JS_TRUE == JS_ValueToInt32(Context, argv[0], &__count), JS_FALSE);
	return_val_if_fail(__count, JS_FALSE);
	count = static_cast<boost::int32_t>(__count);

	std::vector<char> buffer(count+1, '\0');
	istream_storage(Context, Object)->read(&buffer[0], count);
	*Result = STRING_TO_JSVAL(JS_NewStringCopyZ(Context, &buffer[0]));
	return JS_TRUE;
}

JSClass istream_class =
{
	"istream", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, istream_destructor
};

JSPropertySpec istream_properties[] =
{
	{0}
};

JSFunctionSpec istream_methods[] =
{
	{ "eof", istream_eof, 0, 0, 0 },
	{ "get", istream_get, 0, 0, 0 },
	{ "getline", istream_getline, 0, 0, 0 },
	{ "read", istream_read, 1, 0, 0 },
	{0}
};

std::istream* istream_storage(JSContext* Context, JSObject* Object)
{
	return reinterpret_cast<std::istream*>(JS_GetInstancePrivate(Context, Object, &istream_class, 0));
}

// Forward declarations
std::ostream* ostream_storage(JSContext* Context, JSObject* Object);

JSBool ostream_constructor(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
	if(JSVAL_IS_STRING(argv[0]))
		{
			JS_SetPrivate(Context, Object, new std::ofstream(JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))));
			return JS_TRUE;
		}

	return JS_FALSE;
}

void ostream_destructor(JSContext* Context, JSObject* Object)
{
	delete ostream_storage(Context, Object);
}

JSBool ostream_write(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
	const std::string output = JS_GetStringBytes(JS_ValueToString(Context, argv[0]));
	ostream_storage(Context, Object)->write(output.c_str(), output.size());

	return JS_TRUE;
}

JSBool ostream_flush(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
	ostream_storage(Context, Object)->flush();
	return JS_TRUE;
}

JSClass ostream_class =
{
	"ostream", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, ostream_destructor
};

JSPropertySpec ostream_properties[] =
{
	{0}
};

JSFunctionSpec ostream_methods[] =
{
	{ "write", ostream_write, 1, 0, 0 },
	{ "flush", ostream_flush, 0, 0, 0 },
	{0}
};

std::ostream* ostream_storage(JSContext* Context, JSObject* Object)
{
	return reinterpret_cast<std::ostream*>(JS_GetInstancePrivate(Context, Object, &ostream_class, 0));
}

} // namespace

namespace libk3djavascript
{

void initialize_javascript_streams(JSContext* Context, JSObject* Object)
{
	JS_InitClass(Context, Object, 0, &istream_class, istream_constructor, 1, istream_properties, istream_methods, 0, 0);
	JS_InitClass(Context, Object, 0, &ostream_class, ostream_constructor, 1, ostream_properties, ostream_methods, 0, 0);
}

} // namespace libk3djavascript

