/*
  +----------------------------------------------------------------------+
  | PHP Version 5                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 1997-2006 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 3.01 of the PHP license,      |
  | that is bundled with this package in the file LICENSE, and is        |
  | available through the world-wide-web at the following url:           |
  | http://www.php.net/license/3_01.txt                                  |
  | If you did not receive a copy of the PHP license and are unable to   |
  | obtain it through the world-wide-web, please send a note to          |
  | license@php.net so we can mail you a copy immediately.               |
  +----------------------------------------------------------------------+
  | Author: Andrey Hristov                                               |
  +----------------------------------------------------------------------+
*/

/* $Id: header,v 1.17 2006/01/01 13:09:48 sniper Exp $ */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "ext/standard/url.h"
#include "ext/standard/info.h"
#include "php_streamex.h"

#define BUFSIZE 1024

#define PHP_STREAMEX_WRAPPER_NAME "stx"
#define PHP_STREAMEX_STREAMTYPE   "streamex"

int le_php_mysql_conn;

typedef struct _php_streamex_data {
	off_t	position;
	char	*buffer;
	size_t	bufsize;
} php_streamex_data;

static void php_streamex_compute_count_n_overflow(php_streamex_data *data,
											      size_t *count, size_t *overflow)
{
	if (data->position + *count > data->bufsize) {
		*overflow = data->position + *count - data->bufsize;
		*count -= *overflow;
		if (*overflow > data->bufsize) {
			*overflow = data->bufsize;
		}
	} else {
		*overflow = 0;
	}
}


static size_t php_streamex_write(php_stream *stream, const char *buf,
								size_t count TSRMLS_DC)
{
	php_streamex_data *data= stream->abstract;
	size_t overflow = 0;

	php_streamex_compute_count_n_overflow(data, &count, &overflow);

	memcpy(data->buffer + data->position, buf, count);
	/* Copy the overflow */
	if (overflow) {
		memcpy(data->buffer, buf + count, overflow);
		data->position= overflow;
		count += overflow;
	}
	data->position = (data->position + count) % data->bufsize;

	return count;
}


static size_t php_streamex_read(php_stream *stream, char *buf,
								size_t count TSRMLS_DC)
{
	php_streamex_data *data= stream->abstract;
	size_t overflow = 0;

	php_streamex_compute_count_n_overflow(data, &count, &overflow);
	memcpy(buf, data->buffer + data->position, count);
	if (overflow) {
		memcpy(buf, data->buffer, overflow);
		data->position = overflow;
		count += overflow;
	}
	data->position = (data->position + count) % data->bufsize;

	return count;
}


static int php_streamex_close(php_stream *stream,
							   int close_handler TSRMLS_DC)
{
	php_streamex_data *data= stream->abstract;

	efree(data->buffer);
	efree(data);

	return 0;
}


static int php_streamex_flush(php_stream *stream TSRMLS_DC)
{
	php_streamex_data *data= stream->abstract;

	data->position= 0;
	
	return 0;
}


static int php_streamex_seek(php_stream *stream, off_t offset,
							 int whence, off_t *newoffset TSRMLS_DC)
{
	php_streamex_data *data= stream->abstract;

	switch (whence) {
		case SEEK_SET:
			data->position = offset % data->bufsize;
			break;
		case SEEK_CUR:
			data->position = (data->position + offset) % data->bufsize;
			break;
		case SEEK_END:
			data->position = (data->bufsize + offset) % data->bufsize;
			break;
	}
	php_printf("data->position=%d\n", data->position);
	return 0;
}

static php_stream_ops php_streamex_ops = {
	php_streamex_write,		/* write */
	php_streamex_read,		/* read */
	php_streamex_close,		/* clos */
	php_streamex_flush,		/* flush */
	PHP_STREAMEX_STREAMTYPE,
	php_streamex_seek,		/* seek */
	NULL,	/* cast */
	NULL,	/* stat */
	NULL	/* set_option */
};


static php_stream *php_streamex_ctor(php_stream_wrapper *wrapper,
		char *filename, char *mode, int options, char **opened_path,
		php_stream_context *context STREAMS_DC TSRMLS_DC)
{
	php_streamex_data *data;
	php_stream *ret;
	php_url *url;

	if (options & STREAM_OPEN_PERSISTENT) {
		php_stream_wrapper_log_error(wrapper, options TSRMLS_CC,
									 "Unable to open %s persistently", filename);
		return NULL;
	}

	if (!(url = php_url_parse(filename))) {
		php_stream_wrapper_log_error(wrapper, options TSRMLS_CC,
									 "Unexpected error while parsing URL");
		return NULL;
	}

	if (!url->host || (url->host[0] == 0) ||
		strcasecmp(PHP_STREAMEX_WRAPPER_NAME, url->scheme) != 0) {

		php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Invalid URL");
		php_url_free(url);
		return NULL;
	}

	data= emalloc(sizeof(php_streamex_data));
	data->position = 0;
	data->buffer = ecalloc(data->bufsize = BUFSIZE, sizeof(char));
	php_url_free(url);

	ret = php_stream_alloc(&php_streamex_ops, data, 0, mode);
	ret->chunk_size = data->bufsize;
	return ret;
}


static php_stream_wrapper_ops php_streamex_wrapper_ops = {
	php_streamex_ctor,
	NULL,	/* stream_close */
	NULL,	/* stream_stat */
	NULL,	/* url_stat */
	NULL,	/* dir_opener */
	PHP_STREAMEX_WRAPPER_NAME,
	NULL,	/* unlink */
#if PHP_MAJOR_VERSION >= 5
	NULL,	/* rename */
	NULL,	/* mkdir */
	NULL,	/* rmdir */
#endif
};


php_stream_wrapper php_streamex_wrapper = {
	&php_streamex_wrapper_ops,
	NULL,	/* delimiter */
	0		/* is_url */
};


PHP_MINIT_FUNCTION(streamex)
{
	le_php_mysql_conn =
		zend_register_list_destructors_ex(php_mysql_stream_rsrc_dtor, NULL, PHP_MYSQLND_RES_NAME,
											  module_number);

	return php_register_url_stream_wrapper(PHP_STREAMEX_WRAPPER_NAME, &php_streamex_wrapper TSRMLS_CC);
}


PHP_MSHUTDOWN_FUNCTION(streamex)
{
	return php_unregister_url_stream_wrapper(PHP_STREAMEX_WRAPPER_NAME TSRMLS_CC);
}


PHP_MINFO_FUNCTION(streamex)
{
	php_info_print_table_start();
	php_info_print_table_header(2, "streamex support", "enabled");
	php_info_print_table_end();
}

extern zend_function_entry streamex_functions[];

/* {{{ streamex_module_entry
 */
zend_module_entry streamex_module_entry = {
	STANDARD_MODULE_HEADER,
	PHP_STREAMEX_EXTNAME,
	streamex_functions,	/* functions */
	PHP_MINIT(streamex),
	PHP_MSHUTDOWN(streamex),
	NULL,	/* RINIT     */
	NULL,	/* RSHUTDOWN */
	PHP_MINFO(streamex),
	PHP_STREAMEX_EXTVER, 
	STANDARD_MODULE_PROPERTIES
};
/* }}} */


#ifdef COMPILE_DL_STREAMEX
ZEND_GET_MODULE(streamex);
#endif
