#include "php_coregrade_api.h"
#include "php_coregrade_api_ns.h"
#include "coregrade.h"
#include "cfg.h"
#include "clog.h"
#include "vars.h"

#include "safestring.h"

#include <string.h>
#include <map>

#include <ext/standard/info.h>

using namespace std;

zend_object_handlers coregrade_object_handlers;

struct coregrade_object {
    zend_object std;
    CoreGrade *coregrade;
};

zend_class_entry *coregrade_ce;

void coregrade_free_storage(void *object TSRMLS_DC)
{
    coregrade_object *obj = (coregrade_object *)object;
    delete obj->coregrade; 

    zend_hash_destroy(obj->std.properties);
    FREE_HASHTABLE(obj->std.properties);

    efree(obj);
}

zend_object_value coregrade_create_handler(zend_class_entry *type TSRMLS_DC)
{
    zval *tmp;
    zend_object_value retval;

    coregrade_object *obj = (coregrade_object *)emalloc(sizeof(coregrade_object));
    memset(obj, 0, sizeof(coregrade_object));
    obj->std.ce = type;

    ALLOC_HASHTABLE(obj->std.properties);
    zend_hash_init(obj->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
#if PHP_VERSION_ID < 50399
    zend_hash_copy(obj->std.properties, &type->default_properties,
        (copy_ctor_func_t)zval_add_ref, (void *)&tmp, sizeof(zval *));
#else
    object_properties_init((zend_object*) &(obj->std), type);
#endif
    retval.handle = zend_objects_store_put(obj, NULL,
        coregrade_free_storage, NULL TSRMLS_CC);
    retval.handlers = &coregrade_object_handlers;

    return retval;
}

#define SET_ENV( env ) \
if (zend_hash_find(Z_ARRVAL_PP(array), env, sizeof(env), (void **) &token) == SUCCESS) {\
    setenv( env, Z_STRVAL_PP(token), 1 ); \
    logfmt( logINFO, env " = %s", Z_STRVAL_PP(token) ); \
} else {\
    unsetenv( env );\
}

PHP_METHOD(CoreGrade, __construct)
{
    CoreGrade *coregrade = NULL;
    zval *object = getThis();

    coregrade = new CoreGrade();
    coregrade_object *obj = (coregrade_object *)zend_object_store_get_object(object TSRMLS_CC);
    obj->coregrade = coregrade;

    zval **array;
    zval **token;

    zend_is_auto_global("_SERVER", sizeof("_SERVER")-1 TSRMLS_CC);
    if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &array) == SUCCESS &&
                Z_TYPE_PP(array) == IS_ARRAY) {
	logfmt( logINFO, "_SERVER found" );
	SET_ENV( "REMOTE_ADDR" );
	SET_ENV( "SERVER_NAME" );
	SET_ENV( "HTTP_COOKIE" );
	SET_ENV( "QUERY_STRING" );
	SET_ENV( "HTTP_X_FORWARDED_FOR" );
    } else {
	logfmt( logINFO, "_SERVER not found" );
    }
}
PHP_METHOD(CoreGrade, coregrade_api)
{
    zval *param, *param_out;
    CVars input, output;
    CoreGrade *coregrade;
    coregrade_object *obj = (coregrade_object *)zend_object_store_get_object(
	getThis() TSRMLS_CC);
    coregrade = obj->coregrade;
    if (coregrade != NULL) {
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char *>("zz"), &param, &param_out ) == FAILURE ) {
	    // Bad parameters
	    RETURN_NULL();
	}
	if (!PZVAL_IS_REF(param_out)) {
	    // out is not a reference
            /* This is not relevant in PHP 5.4 */
#if PHP_VERSION_ID < 50399
	    zend_error(E_WARNING, "Second parameter wasn't passed by reference");
	    RETURN_NULL();
#endif
	}
	HashTable *hash; 
	char *key;
	uint key_len;
	zval **value;
	
	convert_to_array_ex( &param );
	hash = HASH_OF( param );
	zend_hash_internal_pointer_reset( hash );
	
	while ( zend_hash_get_current_key( hash, &key, /*num_index*/ NULL, /*duplicate*/ false ) == HASH_KEY_IS_STRING ) {
	    zend_hash_get_current_data( hash, (void**)&value );
	    convert_to_string_ex( value );
	    input[ key ] = Z_STRVAL_PP(value);
	    zend_hash_move_forward( hash );
	}
	
	long retval = coregrade->coregrade_api(input, output);
	
	convert_to_array_ex( &param_out ); 
	
	CVars::iterator i; 
	int j = 0;
	char out_key[200], out_value[200];
	for ( i=output.begin(); i != output.end() && j<200 ; i++ ) {
	    j++;
	    logfmt( logINFO, "RET: %s=%s", i->first.c_str(), i->second.c_str() );
	    strsafecpy( out_key, i->first.c_str(), sizeof(out_key) );
	    strsafecpy( out_value, i->second.c_str(), sizeof(out_value) );
	    add_assoc_string( param_out, out_key, out_value, true );
	}
	
	RETURN_LONG(retval);
    }
    RETURN_NULL();
}
PHP_METHOD(CoreGrade, cfgReadChar)
{
    CoreGrade *coregrade;
    coregrade_object *obj = (coregrade_object *)zend_object_store_get_object(
        getThis() TSRMLS_CC);
    coregrade = obj->coregrade;
    if (coregrade != NULL) {
	char *parameter;
	int parameter_len;
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char *>("s"), &parameter, &parameter_len) == FAILURE) {
	    RETURN_NULL();
	}
	if (parameter_len == 0) {
	    RETURN_NULL();
	}
	RETURN_STRING(coregrade->cfgReadChar(parameter),1);
    }
    RETURN_NULL();
}
PHP_METHOD(CoreGrade, cfgReadLong)
{
    CoreGrade *coregrade;
    coregrade_object *obj = (coregrade_object *)zend_object_store_get_object(
        getThis() TSRMLS_CC);
    coregrade = obj->coregrade;
    if (coregrade != NULL) {
	char *parameter;
	int parameter_len;
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char *>("s"), &parameter, &parameter_len) == FAILURE) {
	    RETURN_NULL();
	}
	if (parameter_len == 0) {
	    RETURN_NULL();
	}
	RETURN_LONG(coregrade->cfgReadLong(parameter));
    }
    RETURN_NULL();
}
PHP_METHOD(CoreGrade, logMessage)
{
    CoreGrade *coregrade;
    coregrade_object *obj = (coregrade_object *)zend_object_store_get_object(
        getThis() TSRMLS_CC);
    coregrade = obj->coregrade;
    if (coregrade != NULL) {
	char *message;
	int message_len;
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char *>("s"), &message, &message_len) == FAILURE) {
	    RETURN_FALSE;
	}
	if (message_len == 0) {
	    RETURN_FALSE;
	}
	coregrade->logMessage((const char *)message);
    }
    RETURN_TRUE;
}
#if PHP_VERSION_ID < 50399
function_entry coregrade_methods[] = {
#else
zend_function_entry coregrade_methods[] = {
#endif
    PHP_ME(CoreGrade,  __construct,     NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
    PHP_ME(CoreGrade,  coregrade_api,         NULL, ZEND_ACC_PUBLIC)
    PHP_ME(CoreGrade,  cfgReadChar,     NULL, ZEND_ACC_PUBLIC)
    PHP_ME(CoreGrade,  cfgReadLong,     NULL, ZEND_ACC_PUBLIC)
    PHP_ME(CoreGrade,  logMessage,      NULL, ZEND_ACC_PUBLIC)
    {NULL, NULL, NULL}
};

PHP_MINIT_FUNCTION(coregrade_api)
{
    zend_class_entry ce;
    INIT_NS_CLASS_ENTRY(ce, COREGRADE_API_NS, "CoreGrade", coregrade_methods); 
    //INIT_CLASS_ENTRY(ce, "CoreGrade", coregrade_methods);
    coregrade_ce = zend_register_internal_class(&ce TSRMLS_CC);
    coregrade_ce->create_object = coregrade_create_handler;
    memcpy(&coregrade_object_handlers,
        zend_get_std_object_handlers(), sizeof(zend_object_handlers));
    coregrade_object_handlers.clone_obj = NULL;
    return SUCCESS;
}

PHP_MINFO_FUNCTION(coregrade_api)
{
    php_info_print_table_start();
    php_info_print_table_row(2, "COREGRADE API support", "enabled");
    php_info_print_table_row(2, "Log File", COREGRADE_LOG);
    php_info_print_table_row(2, "Config File", COREGRADE_CONFIG"coregrade_api.conf");
    php_info_print_table_row(2, "Build Date", __DATE__);
    php_info_print_table_row(2, "Build Time", __TIME__);
    php_info_print_table_row(2, "COREGRADE API Version", PHP_COREGRADE_API_EXTVER);
    php_info_print_table_end();
}

zend_module_entry coregrade_api_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    COREGRADE_API_NS,
    NULL,                  /* Functions */
    PHP_MINIT(coregrade_api),
    NULL,                  /* MSHUTDOWN */
    NULL,                  /* RINIT */
    NULL,                  /* RSHUTDOWN */
    PHP_MINFO(coregrade_api),    /* MINFO */
#if ZEND_MODULE_API_NO >= 20010901
    PHP_COREGRADE_API_EXTVER,
#endif
    STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_COREGRADE_API
extern "C" {
ZEND_GET_MODULE(coregrade_api)
}
#endif
