#include "php_mermsemr_api.h"
#include "php_mermsemr_api_ns.h"
#include "mermsemr.h"
#include "cfg.h"
#include "clog.h"
#include "vars.h"

#include "safestring.h"

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

#include <ext/standard/info.h>

zend_object_handlers mermsemr_object_handlers;

typedef struct _mermsemr_object {
    MermsEmr *mermsemr;
    zend_object std;
} mermsemr_object;

static inline mermsemr_object *php_mermsemr_obj_from_obj(zend_object *obj) {
    return (mermsemr_object*)((char*)(obj) - XtOffsetOf(mermsemr_object, std));
}

#define Z_TSTOBJ_P(zv)  php_mermsemr_obj_from_obj(Z_OBJ_P((zv)))

zend_class_entry *mermsemr_ce;

#define SET_ENV( env ) \
if (NULL != (token = zend_hash_str_find(_SERVER, env, sizeof(env)-1))) {\
    setenv( env, Z_STRVAL_P(token), 1 ); \
    logfmt( logINFO, env " = %s", Z_STRVAL_P(token) ); \
} else {\
    unsetenv( env );\
}

// ------------------------------------------------------------------
// ------------------------------------------------------------------
PHP_METHOD(MermsEmr, __construct)
{
    HashTable *_SERVER;
    zval *token;
    zval *id = getThis();
    mermsemr_object *intern;

    intern = Z_TSTOBJ_P(id);
    if(intern != NULL) {
        intern->mermsemr = new MermsEmr();
        // Load some _SERVER data into environment
        zend_string *_server = zend_string_init("_SERVER", strlen("_SERVER"), 0);
        zend_is_auto_global(_server);
        if (Z_TYPE(PG(http_globals)[TRACK_VARS_SERVER]) == IS_UNDEF) {
            logfmt( logINFO, "_SERVER not found" );
        } else {
            logfmt( logINFO, "_SERVER found" );
            _SERVER = Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]);
            SET_ENV( "REMOTE_ADDR" );
            SET_ENV( "SERVER_NAME" );
            SET_ENV( "HTTP_COOKIE" );
            SET_ENV( "QUERY_STRING" );
            SET_ENV( "HTTP_X_FORWARDED_FOR" );
        }
        zend_string_release(_server);
    }
}

// ------------------------------------------------------------------ // ------------------------------------------------------------------
PHP_METHOD(MermsEmr, mermsemr_api)
{
    zval *param, *param_out;
    zval *id = getThis();
    mermsemr_object *intern;
    CVars input, output;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &param, &param_out) == FAILURE) {
        // Bad parameters
	RETURN_NULL();
    }

    intern = Z_TSTOBJ_P(id);
    if(intern != NULL) {
        // Convert input

        HashTable *hash;
        zend_string *key, val;
        ulong num_key;
        uint key_len;
        zval *value;
        convert_to_array_ex( param );
        hash = HASH_OF( param );
        zend_hash_internal_pointer_reset( hash );
        ZEND_HASH_FOREACH_KEY_VAL(hash, num_key, key, value) {
            if (key) { //HASH_KEY_IS_STRING
                if (Z_TYPE_P(value) == IS_STRING) { 
                    input[ ZSTR_VAL(key) ] = Z_STRVAL_P(value);
                    /*
                } else if (Z_TYPE_P(value) == IS_LONG) {
                    input[ ZSTR_VAL(key) ] = Z_LVAL_P(value);
                } else if (Z_TYPE_P(value) == IS_DOUBLE) {
                    input[ ZSTR_VAL(key) ] = Z_DVAL_P(value);
                } else if (Z_TYPE_P(value) == _IS_BOOL) {
                    input[ ZSTR_VAL(key) ] = Z_BVAL(value) ? 1L : 0L;
                } else if (Z_TYPE_P(value) == IS_NULL) {
                    input[ ZSTR_VAL(key) ] = ""; */
                } else {
                    //logfmt( logINFO, "Item %s has unsupported value type", ZSTR_VAL(key) );
                    convert_to_string(value);
                    input[ ZSTR_VAL(key) ] = Z_STRVAL_P(value);
                }
            }
        } ZEND_HASH_FOREACH_END();

        long retval = intern->mermsemr->mermsemr_api(input, output);

        // Convert output
        convert_to_array_ex( param_out ); 
        CVars::iterator i;
        int j = 0;
        const int out_size = output.size(); // 200
        const int out_value_size = 200;
        char out_key[out_value_size], out_value[out_value_size];
        for ( i=output.begin(); i != output.end() && j<out_size ; i++ ) {
            bzero(out_key, out_value_size);
            bzero(out_value, out_value_size);
            j++;
            logfmt( logINFO, "RET: %s=%s", i->first.c_str(), i->second.c_str() );
            strsafecpy( out_key, i->first.c_str(), out_value_size );
            strsafecpy( out_value, i->second.c_str(), out_value_size );
            add_assoc_string( param_out, out_key, out_value);
        }                

        RETURN_LONG(retval);
    }

    RETURN_NULL();
}

// ------------------------------------------------------------------ // ------------------------------------------------------------------
PHP_METHOD(MermsEmr, cfgReadChar)
{
    unsigned char *parameter;
    zval *id = getThis();
    mermsemr_object *intern;

    if (zend_parse_parameters(
      ZEND_NUM_ARGS() TSRMLS_CC, "s", &parameter) == FAILURE) {
          RETURN_NULL();
    }

    intern = Z_TSTOBJ_P(id);
    if(intern != NULL) {
        std::string s = intern->mermsemr->cfgReadChar((char*)parameter);
        RETURN_STRING(s.c_str());
    }
    RETURN_NULL();
}

// ------------------------------------------------------------------ // ------------------------------------------------------------------
PHP_METHOD(MermsEmr, cfgReadLong)
{
    unsigned char *parameter;
    zval *id = getThis();
    mermsemr_object *intern;

    if (zend_parse_parameters(
      ZEND_NUM_ARGS() TSRMLS_CC, "s", &parameter) == FAILURE) {
          RETURN_NULL();
    }

    intern = Z_TSTOBJ_P(id);
    if(intern != NULL) {
        RETURN_LONG(intern->mermsemr->cfgReadLong((char*)parameter));
    }
    RETURN_NULL();
}

// ------------------------------------------------------------------ // ------------------------------------------------------------------
PHP_METHOD(MermsEmr, logMessage)
{
    unsigned char *message;
    zval *id = getThis();
    mermsemr_object *intern;

    if (zend_parse_parameters(
      ZEND_NUM_ARGS() TSRMLS_CC, "s", &message) == FAILURE) {
          RETURN_FALSE;
    }

    intern = Z_TSTOBJ_P(id);
    if(intern != NULL) {
        intern->mermsemr->logMessage((char*)message);
        RETURN_TRUE;
    }
    RETURN_FALSE;
}

// ------------------------------------------------------------------ // ------------------------------------------------------------------
const zend_function_entry mermsemr_methods[] = {
    PHP_ME(MermsEmr, __construct,   NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
    PHP_ME(MermsEmr,  mermsemr_api, NULL, ZEND_ACC_PUBLIC)
    PHP_ME(MermsEmr,  cfgReadChar,  NULL, ZEND_ACC_PUBLIC)
    PHP_ME(MermsEmr,  cfgReadLong,  NULL, ZEND_ACC_PUBLIC)
    PHP_ME(MermsEmr,  logMessage,   NULL, ZEND_ACC_PUBLIC)
    PHP_FE_END
};

// ------------------------------------------------------------------ // ------------------------------------------------------------------
zend_object *mermsemr_object_new(zend_class_entry *ce TSRMLS_DC)
{
    mermsemr_object *intern = (mermsemr_object*)ecalloc(1,
            sizeof(mermsemr_object) +
            zend_object_properties_size(ce));

    zend_object_std_init(&intern->std, ce TSRMLS_CC);
    object_properties_init(&intern->std, ce);

    intern->std.handlers = &mermsemr_object_handlers;

    return &intern->std;
}

// ------------------------------------------------------------------ // ------------------------------------------------------------------
static void mermsemr_object_destroy(zend_object *object)
{
    mermsemr_object *my_obj;
    my_obj = (mermsemr_object*)((char *)
        object - XtOffsetOf(mermsemr_object, std));

    // Call __destruct() from user-land.
    zend_objects_destroy_object(object);
}

static void mermsemr_object_free(zend_object *object)
{
    mermsemr_object *my_obj;
    my_obj = (mermsemr_object *)((char *)
        object - XtOffsetOf(mermsemr_object, std));
    delete my_obj->mermsemr;
    // Free the object using Zend macro.
    zend_object_std_dtor(object); 
}

// ------------------------------------------------------------------ // ------------------------------------------------------------------
PHP_MINIT_FUNCTION(mermsemr_api_sameye)
{
    zend_class_entry ce;
    INIT_NS_CLASS_ENTRY(ce, MERMSEMR_API_SAMEYE_NS, "MermsEmr", mermsemr_methods);
    mermsemr_ce = zend_register_internal_class(&ce TSRMLS_CC);
    mermsemr_ce->create_object = mermsemr_object_new;

    memcpy(&mermsemr_object_handlers, 
        zend_get_std_object_handlers(), 
        sizeof(mermsemr_object_handlers));

    // Handler for free'ing the object.
    mermsemr_object_handlers.free_obj = mermsemr_object_free;

    // Handler for the destructor.
    mermsemr_object_handlers.dtor_obj = mermsemr_object_destroy; 

    // Offset into the engine.
    mermsemr_object_handlers.offset = XtOffsetOf(mermsemr_object, std); 

    return SUCCESS;
}

// ------------------------------------------------------------------ // ------------------------------------------------------------------
PHP_MINFO_FUNCTION(mermsemr_api_sameye)
{
    php_info_print_table_start();
    php_info_print_table_row(2, "MERMSEMR API support", "enabled");
    php_info_print_table_row(2, "Log File", MERMSEMR_LOG);
    php_info_print_table_row(2, "Config File", MERMSEMR_CONFIG"mermsemr_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, "Namespace", MERMSEMR_API_SAMEYE_NS);
    php_info_print_table_row(2, "Version", PHP_MERMSEMR_API_SAMEYE_EXTVER);
    php_info_print_table_end();
}

// ------------------------------------------------------------------ // ------------------------------------------------------------------
zend_module_entry mermsemr_api_sameye_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_MERMSEMR_API_SAMEYE_EXTNAME,
    NULL,                  /* Functions */
    PHP_MINIT(mermsemr_api_sameye),
    NULL,                  /* MSHUTDOWN */
    NULL,                  /* RINIT */
    NULL,                  /* RSHUTDOWN */
    PHP_MINFO(mermsemr_api_sameye), /* MINFO */
#if ZEND_MODULE_API_NO >= 20010901
    PHP_MERMSEMR_API_SAMEYE_EXTVER,
#endif
    STANDARD_MODULE_PROPERTIES
};

//#ifdef COMPILE_DL_MERMSEMR_API_SAMEYE
extern "C" {
  ZEND_GET_MODULE(mermsemr_api_sameye)
}
//#endif
