#include "apr_strings.h"
#include "apr_lib.h"            /* for apr_is* */

#define APR_WANT_STRFUNC
#include "apr_want.h"

#include "httpd.h"
#define CORE_PRIVATE
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_main.h"
#include "http_protocol.h"
#include "http_request.h"
#include "http_connection.h"
#include "util_script.h"

#include "dav_repos.h"
#include "dbms.h"
#include "share_common.h"
#include "share_account.h"
#include "share_statistic.h"
#include "transfer.h"
#include "util.h"
#include "out_filter.h"
#include "inout_filter.h"

extern module AP_MODULE_DECLARE_DATA dav_repos_module;

// reference : mod_dumpio
int dav_input_filter(ap_filter_t *f, apr_bucket_brigade *bb,
	ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes)
{
	apr_status_t ret;
	request_rec *r = f->r;

	ret = ap_get_brigade(f->next, bb, mode, block, readbytes);

	if (ret == APR_SUCCESS) {
		if (r && r->method_number == M_PUT)
		{
			apr_off_t length;
			apr_brigade_length(bb, 0, &length);
			//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
			//"mod_dumpio: PUT %" APR_OFF_T_FMT , length);
			dav_shared_ts_transfer_data(r, INB, length, 0);
		}
	}
	else {
		return ret;
	}

	return APR_SUCCESS;
}

typedef struct mod_cband_brigade_ctx {
	apr_bucket_brigade *bb;
} mod_cband_brigade_ctx;

// reference : mod-cband-0.9.6.1
int dav_bc_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
{
	apr_bucket_alloc_t *alloc = apr_bucket_alloc_create(f->r->pool);
	apr_bucket *b = APR_BRIGADE_FIRST(bb);
	request_rec *r = f->r;
	mod_cband_brigade_ctx *ctx;
	const char *buf;
	apr_size_t bytes;

	if (r == NULL) {
		ap_remove_output_filter(f);
		ap_pass_brigade(f->next, bb);
		return APR_SUCCESS;
	}

	if (f->r->main || (f->r->method_number != M_GET)) {
		ap_remove_output_filter(f);
		ap_pass_brigade(f->next, bb);
		return APR_SUCCESS;
	}

	ctx = f->ctx;
	if (ctx == NULL) {
		ctx = f->ctx = apr_pcalloc(f->r->pool, sizeof(mod_cband_brigade_ctx));
		ctx->bb = apr_brigade_create(f->r->pool, alloc);
	}

	BC_START;
	// BC_START


	while (b != APR_BRIGADE_SENTINEL(bb)) {
		if (f->r->connection->aborted) {
			return APR_SUCCESS;
		}

		if (APR_BUCKET_IS_EOS(b) || APR_BUCKET_IS_FLUSH(b)) {
			APR_BUCKET_REMOVE(b);
			APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
			ap_pass_brigade(f->next, ctx->bb);
			return APR_SUCCESS;
		}


		if (apr_bucket_read(b, &buf, &bytes, APR_NONBLOCK_READ) == APR_SUCCESS) {
			if (ac && (AC_TCP > 0 || AC_TCS > 0)) {
				//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "#### BC_CORE");
				//ap_pass_brigade(f->next, ctx->bb);
				while (bytes > 0) {
					BC_CORE(bytes);
					apr_size_t bytes_split = (apr_size_t)(1024 * 1024 / 8);

					if (bytes_split > bytes) {
						bytes_split = bytes;
					}

					apr_bucket_split(b, bytes_split);
					APR_BUCKET_REMOVE(b);
					APR_BRIGADE_INSERT_TAIL(ctx->bb, b);

					bytes -= bytes_split;
					//      ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "#### BC_CORE %d, %d", bytes, bytes_split );

					ap_pass_brigade(f->next, ctx->bb);

					b = APR_BRIGADE_FIRST(bb);

					BC_CORE(bytes_split);
					dav_shared_ts_transfer_data(r, OUTB, bytes_split, 0);
				}
			}
			else {
				dav_shared_ts_transfer_data(r, OUTB, bytes, 0);
			}
		}

		APR_BUCKET_REMOVE(b);
		APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
		b = APR_BRIGADE_FIRST(bb);
		ap_pass_brigade(f->next, ctx->bb);

	}

	return APR_SUCCESS;

}
