/*
  +----------------------------------------------------------------------+
  | PHP Version 5                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 1997-2004 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 3.0 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_0.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 <andrey _at_ php _dot_ net>                       |
  +----------------------------------------------------------------------+
*/

/* $Id: header,v 1.15 2004/01/08 16:46:52 sniper Exp $ */

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

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_ccmath.h"
#include "ccmath.h"

/* If you declare any globals in php_ccmath.h uncomment this:
ZEND_DECLARE_MODULE_GLOBALS(ccmath)
*/

/* True global resources - no need for thread safety here */
static int le_ccmath;
static zend_object_handlers ccmath_complex_handlers;
static zend_class_entry *ccmath_ce_complex_class_entry;

typedef struct complex (*ccmath_complex_func_c__c_c)(struct complex c1, struct complex c2);
typedef struct complex (*ccmath_complex_func_c__d_c)(double d1, struct complex c1);
typedef struct complex (*ccmath_complex_func_c__c)(struct complex c1);
typedef         double (*ccmath_complex_func_d__c)(struct complex c1);

typedef struct _ccmath_complex_object {
	zend_object		zo;
	struct complex	cnum;
} ccmath_complex_object;


static function_entry ccmath_complex_class_functions[] = {
	PHP_ME(complex, __construct,	NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, add,			NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, sub,			NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, mul,			NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, div,			NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, print,			NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, realMul,		NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, imagMul,		NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, cng,			NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, exp,			NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, log,			NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, sinh,			NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, cosh,			NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, tanh,			NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, asinh,			NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, acosh,			NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, atanh,			NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, asin,			NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, acos,			NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, atan,			NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, sqrt,			NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, sin,			NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, cos,			NULL, ZEND_ACC_PUBLIC)
	PHP_ME(complex, tan,			NULL, ZEND_ACC_PUBLIC)

	PHP_ME(complex, addS,			NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, subS,			NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, mulS,			NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, divS,			NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, realMulS,		NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, imagMulS,		NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, cngS,			NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, expS,			NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, logS,			NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, sinhS,			NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, coshS,			NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, tanhS,			NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, asinhS,			NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, acoshS,			NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, atanhS,			NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, asinS,			NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, acosS,			NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, atanS,			NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, sqrtS,			NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, sinS,			NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, cosS,			NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(complex, tanS,			NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)

	{NULL, NULL, NULL}	/* Must be the last line in ccmath_functions[] */
};


/* {{{ ccmath_functions[]
 *
 * Every user visible function must have an entry in ccmath_functions[].
 */
function_entry ccmath_functions[] = {
	{NULL, NULL, NULL}	/* Must be the last line in ccmath_functions[] */
};
/* }}} */

/* {{{ ccmath_module_entry
 */
zend_module_entry ccmath_module_entry = {
	STANDARD_MODULE_HEADER,
	"ccmath",
	ccmath_functions,
	PHP_MINIT(ccmath),
	NULL,
	NULL,
	NULL,
	PHP_MINFO(ccmath),
	"0.1",
	STANDARD_MODULE_PROPERTIES
};
/* }}} */

#ifdef COMPILE_DL_CCMATH
ZEND_GET_MODULE(ccmath)
#endif

/* {{{ ccmath_complex_object_dtor */
static void	ccmath_complex_object_dtor(void *object, zend_object_handle handle TSRMLS_DC)
{
	ccmath_complex_object *intern = (ccmath_complex_object *) object;
	zend_hash_destroy(intern->zo.properties);
	FREE_HASHTABLE(intern->zo.properties);
	if (intern->zo.ce == ccmath_ce_complex_class_entry) {
		//do nothing
	}
	efree(object);
}
/* }}} */


/* {{{ ccmath_complex_object_new */
static zend_object_value ccmath_complex_object_new(zend_class_entry *class_type TSRMLS_DC)
{
	zend_object_value retval;
	ccmath_complex_object *intern;
	zval *tmp;

	intern = emalloc(sizeof(ccmath_complex_object));
	memset(intern, 0, sizeof(ccmath_complex_object));
	intern->zo.ce = class_type;

	ALLOC_HASHTABLE(intern->zo.properties);
	zend_hash_init(intern->zo.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
	zend_hash_copy(intern->zo.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref,
					(void *) &tmp, sizeof(zval *));

	retval.handle = zend_objects_store_put(intern, ccmath_complex_object_dtor, NULL TSRMLS_CC);
	retval.handlers = &ccmath_complex_handlers;

	return retval;
}
/* }}} */

/* {{{ PHP_MINIT_FUNCTION
 */
PHP_MINIT_FUNCTION(ccmath)
{
	zend_class_entry ce;

	INIT_CLASS_ENTRY(ce, "complex", ccmath_complex_class_functions);
	ccmath_ce_complex_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
	ccmath_ce_complex_class_entry->create_object = ccmath_complex_object_new;
	memcpy(&ccmath_complex_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));

	return SUCCESS;
}
/* }}} */

/* {{{ PHP_MINFO_FUNCTION
 */
PHP_MINFO_FUNCTION(ccmath)
{
	php_info_print_table_start();
	php_info_print_table_header(2, "ccmath support", "enabled");
	php_info_print_table_end();
}
/* }}} */

/* {{{ proto void complex::__construct(float rpart, float impart)
 Creates a fresh complex object */
PHP_METHOD(complex, __construct)
{
	zval *obj = getThis();
	ccmath_complex_object *intern;
	double rpart = 0, impart = 0;

	php_set_error_handling(EH_THROW, zend_exception_get_default() TSRMLS_CC);

	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|dd", &rpart, &impart)) {
		return;
	}
	intern = (ccmath_complex_object *) zend_object_store_get_object(obj TSRMLS_CC);
	CCMATH_SET_COMPLEX(intern->cnum, rpart, impart);

	php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
}
/* }}} */


static void php_ccmath_complex_c__c_c(INTERNAL_FUNCTION_PARAMETERS, ccmath_complex_func_c__c_c complex_func, int static_call)
{
	zval *obj = getThis(), *obj2;
	ccmath_complex result;

	if (static_call) {
		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "OO",
						&obj, ccmath_ce_complex_class_entry, &obj2, ccmath_ce_complex_class_entry)) {
			return;
		}
	} else {
		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &obj2, ccmath_ce_complex_class_entry)) {
			return;
		}
	}

	result = complex_func(
					((ccmath_complex_object *) zend_object_store_get_object(obj TSRMLS_CC))->cnum,
					((ccmath_complex_object *) zend_object_store_get_object(obj2 TSRMLS_CC))->cnum
				);
	CCMATH_RETURN_COMPLEX_OBJECT(result.re, result.im);
}

/* {{{ proto object complex::add(object c)
 Adds a complex number to the current one and returns the result as new object */
PHP_METHOD(complex, add)
{
	php_ccmath_complex_c__c_c(INTERNAL_FUNCTION_PARAM_PASSTHRU, cadd, 0);
}
/* }}} */

/* {{{ proto object complex::addS(object c1, object c2)
 Adds two complex numbers and returns the result as new object */
PHP_METHOD(complex, addS)
{
	php_ccmath_complex_c__c_c(INTERNAL_FUNCTION_PARAM_PASSTHRU, cadd, 1);
}
/* }}} */

/* {{{ proto object complex::sub(object c)
  Subtracts complex numbers (non-static) */
PHP_METHOD(complex, sub)
{
	php_ccmath_complex_c__c_c(INTERNAL_FUNCTION_PARAM_PASSTHRU, csub, 0);
}
/* }}} */

/* {{{ proto object complex::subS(object c1, object c2)
 Substracts two complex numbers and returns the result as new object */
PHP_METHOD(complex, subS)
{
	php_ccmath_complex_c_c(INTERNAL_FUNCTION_PARAM_PASSTHRU, csub, 1);
}
/* }}} */

/* {{{ proto object complex::mul(object c)
  Multiplies complex numbers (non-static) */
PHP_METHOD(complex, mul)
{
	php_ccmath_complex_c__c_c(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmul, 0);
}
/* }}} */

/* {{{ proto object complex::mulS(object c1, object c2)
 Multiplies two complex numbers and returns the result as new object */
PHP_METHOD(complex, mulS)
{
	php_ccmath_complex_c__c_c(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmul, 1);
}
/* }}} */

/* {{{ proto object complex::div(object c)
  Divides complex numbers (non-static) */
PHP_METHOD(complex, div)
{
	php_ccmath_complex_c__c_c(INTERNAL_FUNCTION_PARAM_PASSTHRU, cdiv, 0);
}
/* }}} */

/* {{{ proto object complex::divS(object c1, object c2)
 Divides two complex numbers and returns the result as new object */
PHP_METHOD(complex, divS)
{
	php_ccmath_complex_c__c_c(INTERNAL_FUNCTION_PARAM_PASSTHRU, cdiv, 1);
}
/* }}} */


static void php_ccmath_complex_c__d_c(INTERNAL_FUNCTION_PARAMETERS, ccmath_complex_func_c__d_c complex_func, int static_call)
{
	zval *obj = getThis();
	ccmath_complex_object	*intern;
	ccmath_complex			 result;
	double					 dval;

	if (static_call) {
		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Od",
						&obj, ccmath_ce_complex_class_entry, &dval)) {
			return;
		}
	} else {
		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval)) {
			return;
		}
	}

	result = complex_func(dval, ((ccmath_complex_object *) zend_object_store_get_object(obj TSRMLS_CC))->cnum);

	CCMATH_RETURN_COMPLEX_OBJECT(result.re, result.im);
}

/* {{{ proto object complex::realMul(double mul)
  (non-static) */
PHP_METHOD(complex, realMul)
{
	php_ccmath_complex_c__d_c(INTERNAL_FUNCTION_PARAM_PASSTHRU, crmu, 0);
}
/* }}} */

/* {{{ proto object complex::realMulS(object c, double mul)
  */
PHP_METHOD(complex, realMulS)
{
	php_ccmath_complex_c__d_c(INTERNAL_FUNCTION_PARAM_PASSTHRU, crmu, 1);
}
/* }}} */

/* {{{ proto object complex::imagMul(object c)
  (non-static) */
PHP_METHOD(complex, imagMul)
{
	php_ccmath_complex_c__d_c(INTERNAL_FUNCTION_PARAM_PASSTHRU, crmu, 0);
}
/* }}} */

/* {{{ proto object complex::imagMulS(object c, double mul)
 */
PHP_METHOD(complex, imagMulS)
{
	php_ccmath_complex_c__d_c(INTERNAL_FUNCTION_PARAM_PASSTHRU, cimu, 1);
}
/* }}} */


static void php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAMETERS, ccmath_complex_func_c__c complex_func, int static_call)
{
	zval *obj = getThis();
	ccmath_complex result;

	if (static_call) {
		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O",
						&obj, ccmath_ce_complex_class_entry)) {
			return;
		}
	} else if (ZEND_NUM_ARGS() != 0) {
		WRONG_PARAM_COUNT;
	}

	result = complex_func(((ccmath_complex_object *) zend_object_store_get_object(obj TSRMLS_CC))->cnum);

	CCMATH_RETURN_COMPLEX_OBJECT(result.re, result.im);
}

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, cng)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, ccng, 0);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, cngS)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, ccng, 1);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, exp)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, cexp, 0);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, expS)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, cexp, 1);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, log)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, clog, 0);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, logS)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, clog, 1);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, sinh)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, csinh, 0);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, sinhS)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, csinh, 1);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, cosh)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, ccosh, 0);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, coshS)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, ccosh, 1);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, tanh)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, ctanh, 0);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, tanhS)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, ctanh, 1);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, asinh)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, casinh, 0);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, asinhS)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, casinh, 1);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, acosh)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, cacosh, 0);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, acoshS)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, cacosh, 1);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, atanh)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, catanh, 0);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, atanhS)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, catanh, 1);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, asin)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, casin, 0);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, asinS)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, casin, 1);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, acos)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, cacos, 0);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, acosS)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, cacos, 1);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, atan)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, catan, 0);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, atanS)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, catan, 1);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, sqrt)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, csqrt, 0);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, sqrtS)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, csqrt, 1);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, sin)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, csin, 0);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, sinS)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, csin, 1);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, cos)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, ccos, 0);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, cosS)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, ccos, 1);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, tan)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, ctan, 0);
}
/* }}} */

/* {{{ proto object complex::(object c) */
PHP_METHOD(complex, tanS)
{
	php_ccmath_complex_c__c(INTERNAL_FUNCTION_PARAM_PASSTHRU, ctan, 1);
}
/* }}} */




/* {{{ proto object complex::print()
 Adds a complex number to the current one and returns the result as new object */
PHP_METHOD(complex, print)
{
	zval *obj = getThis();
	ccmath_complex_object *intern = (ccmath_complex_object *) zend_object_store_get_object(obj TSRMLS_CC);
	char string[256];
	long len;

	len = sprintf(string, "Complex : re = %5.5f im=%5.5f", intern->cnum.re, intern->cnum.im);
	RETURN_STRINGL(string, len, 1);
}
/* }}} */


/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */
