<?php
/*
 * PHP-GTK - The PHP language bindings for GTK+
 *
 * Copyright (C) 2001-2008 Andrei Zmievski <andrei@php.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/* $Id$ */

/*
 * Significant portions of this generator are based on the pygtk code generator
 * developed by James Henstridge <jamesh@daa.com.au>.
 *
 */

set_time_limit(300);

// override the default PHP 8Mb as this script tends to use a lot more
// and hopefully reduce the support questions a bit..
// Make it -1 instead :)
ini_set('memory_limit','-1');

if (strstr(PHP_OS, 'WIN')) {
	define('WIN_OS', true);
} else {
	define('WIN_OS', false);
}

require dirname(__FILE__) . "/Getopt.php";
require dirname(__FILE__) . "/override.php";
require dirname(__FILE__) . "/arg_types.php";
require dirname(__FILE__) . "/scheme.php";
require dirname(__FILE__) . "/templates.php";
require dirname(__FILE__) . "/array_printf.php";
require dirname(__FILE__) . "/lineoutput.php";

class Generator {
    var $parser             = null;
    var $overrides          = null;
    var $prefix             = null;
    var $lprefix            = null;
    var $function_class     = null;
    var $logfile            = null;
    var $diversions         = null;

    var $gtype_constants    = '';

    var $template_map       = array('object' => array('constructor' => Templates::constructor_body,
                                                      'static_constructor' => Templates::static_constructor_body,
                                                      'method' => Templates::method_body,
                                                      'prop' => Templates::prop_access),
                                    'boxed'  => array('constructor' => Templates::boxed_constructor_body,
                                                      'static_constructor' => Templates::boxed_static_constructor_body,
                                                      'method' => Templates::boxed_method_body,
                                                      'prop' => Templates::boxed_prop_access),
                                    'interface' => array('method' => Templates::method_body),
                                    'pointer' => array('method' => Templates::pointer_method_body),
                                   );
    var $handlers           = array('read_property', 'write_property', 'get_properties',
                                    'read_dimension', 'write_dimension', 'has_dimension',
                                    'unset_dimension', 'count_elements');
    var $cover              = array();

    function Generator(&$parser, &$overrides, $prefix, $function_class)
    {
        $this->parser    = &$parser;
        $this->overrides = &$overrides;
        $this->prefix    = ucfirst($prefix);
        $this->lprefix   = strtolower($prefix);
        $this->function_class = $function_class;

        $this->cover["funcs"] = new Coverage("Functions");
        $this->cover["methods"] = new Coverage("Methods");
        $this->cover["ctors"] = new Coverage("Constructors");
        $this->cover["props"] = new Coverage("Property accessors");
    }

    function set_logfile($logfile) {
        $this->logfile = fopen($logfile, 'w');
    }
    
    function log_print()
    {
        $args = func_get_args();
        if (count($args) == 0) return;

        $format = array_shift($args);

        $output = vsprintf($format, $args);

        if (!WIN_OS) {
            echo $output;
        }

        fwrite($this->logfile, $output);
    }

    function log()
    {
        $args = func_get_args();
        if (count($args) == 0) return;

        $format = array_shift($args);

        fwrite($this->logfile, vsprintf($format, $args));
    }

    function divert()
    {
        $args = func_get_args();
        if (count($args) < 2) return;

        list ($divert_id, $format) = $args;

        @$this->diversions[$divert_id] .= vsprintf($format, array_slice($args, 2));
    }

    function register_types($parser = null)
    {
        global  $matcher;

        if (!$parser)
            $parser = $this->parser;

        foreach ($parser->objects as $object) {
            $matcher->register_object($object->c_name, $object->typecode);
        }

        foreach ($parser->interfaces as $interface) {
            $matcher->register_object($interface->c_name, $interface->typecode);
        }

        foreach ($parser->enums as $enum) {
            if ($enum->def_type == 'flags')
                $matcher->register_flag($enum->c_name, $enum->typecode);
            else
                $matcher->register_enum($enum->c_name, $enum->typecode);
        }

        foreach ($parser->boxed as $boxed) {
            $matcher->register_boxed($boxed->c_name, $boxed->typecode);
        }

        foreach ($parser->pointers as $pointer) {
            $matcher->register_pointer($pointer->c_name, $pointer->typecode);
        }
    }


    function write_override($override, $id)
    {
        $args = array_slice(func_get_args(), 1);
        list($lineno, $file_name) = $this->overrides->get_line_info(join('.', $args)); 
        $this->fp->set_line($lineno, $file_name);
        $this->fp->write($override);
        $this->fp->reset_line();
        $this->fp->write("\n\n");
    }


    function write_callable($callable, $template, $handle_return = false, $is_method = false, $dict = array())
    {
        global $matcher;

        if ($callable->varargs) {
            throw new Exception('varargs methods not supported');
        }

        $info = new Wrapper_Info();
        /*
         * Slight hack that relies on both parameters being false when used to
         * write out a constructor.
         */
        if (!$handle_return && !$is_method) {
            $info->error_action = "PHPG_THROW_CONSTRUCT_EXCEPTION($dict[class])";
        }

        /* need the extra comma for methods */
        if ($is_method) {
            $info->arg_list[] = '';
        }

        foreach ($callable->params as $params_array) {
            list($param_type, $param_name, $param_default, $param_null) = $params_array;

            if (isset($param_default) && strpos($info->specifiers, '|') === false) {
                $info->add_parse_list('|');
            }

            $handler = $matcher->get($param_type);
            $handler->write_param($param_type, $param_name, $param_default, $param_null, $info);
        }

        $dict['return'] = '';
        if ($handle_return) {
            if ($callable->return_type !== null &&
                $callable->return_type != 'none') {
                $dict['return'] = 'php_retval = ';
            }
            $handler = $matcher->get($callable->return_type);
            $handler->write_return($callable->return_type, $callable->caller_owns_return, $info);
        }

        if (isset($callable->deprecated)) {
            $info->pre_code[] = sprintf(Templates::deprecation_msg, $callable->deprecated ? '"'.$callable->deprecated.'"' : 'NULL');
        }

        if (!isset($dict['name'])) {
            $dict['name'] = $callable->name;
        }
        $dict['cname'] = $callable->c_name;
        $dict['var_list'] = $info->get_var_list();
        $dict['specs'] = $info->specifiers;
        $dict['parse_list'] = $info->get_parse_list();
        $dict['arg_list'] = $info->get_arg_list();
        $dict['pre_code'] = $info->get_pre_code();
        $dict['post_code'] = $info->get_post_code();

        if (!empty($dict['ctor_with_props'])) {
            $dict['n_params'] = count((array)$callable->params);
            $dict['n_args'] = $dict['n_params'] + 1;
            $prop_names = '';
            foreach ($callable->properties as $prop) {
                $prop_names .= '"' . $prop[0] . '", ';
            }
            $dict['props'] = $prop_names;
            $n = 0;
            for ($i = 0; $i < count($info->parse_list); $i++) {
                if ($info->parse_list[$i] && $info->parse_list[$i]{0} == '&') {
                    $info->parse_list[$i] = "&php_args[$n]";
                    $n++;
                }
            }
            $dict['specs'] = preg_replace(',([^|!/]),', '^$1', $dict['specs']);
            $dict['parse_list'] = $info->get_parse_list();
            $dict['pre_code'] = '';
            $dict['post_code'] = '';

            // Replace any removed deprecation messages
            if (isset($callable->deprecated)) {
                $dict['pre_code'] = sprintf(Templates::deprecation_msg, $callable->deprecated ? '"'.$callable->deprecated.'"' : 'NULL');
            }
        }

        return aprintf($template, $dict);
    }


    function write_methods($object)
    {
        $this->log_print("  %-20s ", "methods");

        $num_written = $num_skipped = 0;
        $method_entries = array();

        $methods = $this->parser->find_methods($object);

        $dict['class'] = $object->c_name;
        $dict['scope'] = $object->c_name;
        $dict['typecode'] = $object->typecode;

        switch ($object->def_type) {
            case 'object':
            case 'interface':
                $dict['cast'] = preg_replace('!_TYPE_!', '_', $object->typecode, 1);
                break;

            case 'boxed':
            case 'pointer':
                $dict['cast'] = $object->c_name . ' *';
                break;

            default:
                throw new Exception("unhandled definition type");
                break;
        }

        $object->methods = array();
        $methodarginfos = '';

        foreach ($methods as $method) {
            $method_name = $method->c_name;
            
            /* skip ignored methods */
            if ($this->overrides->is_ignored($method_name)) continue;

            try {
                list($arginfo, $reflection_func) = $this->genReflectionArgInfo($method, $object);
                
                if (($overriden = $this->overrides->is_overriden($method_name))) {
                    list($method_name, $method_override, $flags) = $this->overrides->get_override($method_name);
                    if (!isset($method_name))
                        $method_name = $method->name;
                    $method_override = preg_replace('!^.*(PHP_METHOD).*$!m', "static $1($object->in_module$object->name, $method_name)", $method_override);
                    $this->write_override($method_override, $method->c_name);
                    $method_entries[$method_name] = array($object->in_module . $object->name,
                                                          $method_name, $reflection_func, $flags ?  $flags : ($method->static ? 'ZEND_ACC_PUBLIC|ZEND_ACC_STATIC' : 'ZEND_ACC_PUBLIC'));
                } else {
                    if ($this->overrides->is_deprecated($method->c_name)) {
                        $method->deprecated = $this->overrides->get_deprecated($method->c_name);
                    }
                    if ($method->static) {
                        $code = $this->write_callable($method, Templates::function_body, true, false, $dict);
                        $flags = 'ZEND_ACC_PUBLIC|ZEND_ACC_STATIC';
                    } else {
                        $template = $this->template_map[$object->def_type]['method'];
                        $code = $this->write_callable($method, $template, true, true, $dict);
                        $flags = 'ZEND_ACC_PUBLIC';
                    }
                    $this->fp->write($code);
                    $method_entries[$method->name] = array($object->in_module . $object->name,
                                                           $method->name, $reflection_func, $flags);
                }
                
                $this->divert("gen", "%s  %-11s %s::%s\n", $overriden ? "%%":"  ", "method", $object->c_name, $method->name);
                $num_written++;
                $this->cover["methods"]->written();
                
                if ($arginfo === null) {
                    $object->methods[$method->name] = 1;
                } else {
                    $methodarginfos .= $arginfo;
                    $object->methods[$method->name] = 2;
                }
            } catch (Exception $e) {
                $this->divert("notgen", "  %-11s %s::%s: %s\n", "method", $object->c_name, $method->name, $e->getMessage());
                $num_skipped++;
                $this->cover["methods"]->skipped();
            }
        }

        if ($this->overrides->have_extra_methods($object->c_name)) {
            foreach ($this->overrides->get_extra_methods($object->c_name) as $method_name => $override) {
                list($method_body, $flags) = $override;
                list($arginfo, $reflection_func) = $this->genReflectionArgInfo(null, $object, $method_name);

                $method_body = preg_replace('!^.*(PHP_METHOD).*$!m', "static $1($object->c_name, $method_name)", $method_body);
                $this->write_override($method_body, $object->c_name, $method_name);
                $method_entries[$method_name] = array($object->c_name, $method_name,
                                                      $reflection_func, $flags ? $flags : 'ZEND_ACC_PUBLIC');
                $this->divert("gen", "%s  %-11s %s::%s\n", "%%", "method", $object->c_name, $method_name);
                $num_written++;
                $this->cover["methods"]->written();

                if ($arginfo === null) {
                    $object->methods[$method_name] = 1;
                } else {
                    $methodarginfos .= $arginfo;
                    $object->methods[$method_name] = 2;
                }
            }
        }

        $this->log_print("(%d written, %d skipped)\n", $num_written, $num_skipped);

        return array($method_entries, $methodarginfos);
    }

    function write_constructor($object)
    {
        $this->log_print("  %-20s ", "constructors");
        $num_written = $num_skipped = 0;

        $ctors = $this->parser->find_constructor($object, $this->overrides);

        $ctor_defs = array();
        $ctor_arginfos = '';

        if ($ctors) {
            $dict['class'] = $object->c_name;
            $dict['typecode'] = $object->typecode;
            $first = 1;

            foreach ($ctors as $ctor) {
                if ($this->overrides->is_deprecated($ctor->c_name)) {
                    $ctor->deprecated = $this->overrides->get_deprecated($ctor->c_name);
                }
                $ctor_name = $ctor->c_name;
                if ($first) {
                    $ctor_fe_name = '__construct';
                    $flags = 'ZEND_ACC_PUBLIC';
                    $template_name = 'constructor';
                    if (!$ctor->caller_owns_return) {
                        $dict['post_create_code'] = "\tg_object_ref(wrapped_obj);";
                    }
                } else {
                    // remove class name from the constructor name, i.e. turn
                    // gtk_button_new_with_mnemonic into new_with_mnemonic
                    $ctor_fe_name = substr($ctor_name, strlen(convert_typename($ctor->is_constructor_of)));
                    $flags = 'ZEND_ACC_PUBLIC|ZEND_ACC_STATIC';
                    $template_name = 'static_constructor';
                }

                $dict['ctor_with_props'] = false;
                /* Use simple no-prop constructor only for default constructors
                   with no parameters. */
                if ($object->def_type == 'object' && !$ctor->params && $first) {
                    $template = Templates::constructor_without_props;
                    $dict['ctor_with_props'] = true;
                } else if ($object->def_type == 'object' && $ctor->properties) {
                    $template = Templates::constructor_with_props;
                    $dict['ctor_with_props'] = true;
                } else {
                    $template = $this->template_map[$object->def_type][$template_name];
                }

                try {
                    list($arginfo, $reflection_func) = $this->genReflectionArgInfo($ctor, $object, $ctor_fe_name);

                    if (($overriden = $this->overrides->is_overriden($ctor_name))) {
                        list(, $ctor_override, $ctor_flags) = $this->overrides->get_override($ctor_name);
                        if (!empty($ctor_flags))
                            $flags = $ctor_flags;
                        $ctor_override = preg_replace('!^.*(PHP_METHOD).*$!m', "static $1($ctor->is_constructor_of, $ctor_fe_name)", $ctor_override);
                        $this->write_override($ctor_override, $ctor->c_name);
                    } else {
                        $dict['name'] = $ctor_fe_name;
                        $code = $this->write_callable($ctor, $template, false, false, $dict);
                        $this->fp->write($code);
                    }

                    $ctor_defs[] = sprintf(Templates::method_entry,
                                           $ctor->is_constructor_of,
                                           $ctor_fe_name, $reflection_func, $flags);
                    $this->divert("gen", "%s  %-11s %s::%s\n", $overriden?"%%":"  ", "constructor", $object->c_name, $ctor_fe_name);
                    $num_written++;
                    $this->cover["ctors"]->written();
                    
                    if ($arginfo !== null) {
                        $ctor_arginfos .= $arginfo;
                    }
                } catch (Exception $e) {
                    $this->divert("notgen", "  %-11s %s::%s: %s\n", "constructor", $object->c_name, $ctor_fe_name, $e->getMessage());
                    $num_skipped++;
                    $this->cover["ctors"]->skipped();
                    // mark class as non-instantiable directly if we were trying
                    // to generate default constructor
                    if ($ctor_fe_name == '__construct') {
                        $ctor_defs[] = sprintf(Templates::function_entry, $ctor_fe_name, 'no_direct_constructor', $ctor_fe_name, 'no_direct_constructor');
                    }
                }
                $first = 0;
            }
        } else {
            if ($this->overrides->have_extra_methods($object->c_name)) {
                $ctor_name = '__construct';
                $extras = $this->overrides->get_extra_methods($object->c_name);
                if (isset($extras[$ctor_name])) {
                    $ctor_body = $extras[$ctor_name];
                    $ctor_body = preg_replace('!^.*(PHP_METHOD).*$!m', "static $1($object->c_name, $ctor_name)", $ctor_body);
                    $this->write_override($ctor_body, $object->c_name, $ctor_name);
                    $ctor_defs[] = sprintf(Templates::method_entry,
                                           $object->c_name, $ctor_name,
                                           'NULL', 'ZEND_ACC_PUBLIC');
                    $this->divert("gen", "%%%%  %-11s %s::%s\n", "constructor", $object->c_name, $ctor_name);
                    $num_written++;
                }
            } else {
                // mark class as non-instantiable directly, only if it's not
                // GObject. For GObject's we let it chain up to GObject
                // constructor
                if ($object->def_type != 'object') {
                    $ctor_defs[] = sprintf(Templates::function_entry, '__construct', 'no_direct_constructor', '__construct', 'no_direct_constructor');
                }
            }
        }

        $this->log_print("(%d written, %d skipped)\n", $num_written, $num_skipped);
        return array($ctor_defs, $ctor_arginfos);
    }//function write_constructor($object)

    function write_classes()
    {
        $register_classes = '';

        if ($this->parser->functions || $this->parser->enums) {
            $this->log_print("\n%s\n%s\n", $this->prefix, str_repeat('~', 50));
            $func_defs = $this->write_functions();

            $register_classes .= aprintf(Templates::register_class,
                                         array('ce' => $this->lprefix . '_ce',
                                               'class' => $this->prefix,
                                               'methods' => $func_defs ? $this->lprefix . '_methods' : 'NULL',
                                               'parent' => 'NULL',
                                               'ce_flags' => 0,
                                               'propinfo' => 'NULL',
                                               'create_func' => 'NULL',
                                               'typecode' => 0));
        }

        /* GInterface's */
        $this->log_print("\n\n" . $this->make_header("Interfaces", 50, '-'));
        foreach ($this->parser->interfaces as $interface) {
            $reg_info = $this->write_class($interface);
            $register_classes .= aprintf(Templates::register_interface, $reg_info);
            if ($this->overrides->have_post_registration($interface->c_name)) {
                $register_classes .= $this->overrides->get_post_registration($interface->c_name);
            }

            /* size check goes here */
            if ($this->fp->check_size()) {
                // we hit size too big, write out end
                $this->fp->write(sprintf(Templates::register_classes,
                                  $this->lprefix . $this->fp->get_current(),
                                  $register_classes));
                $register_classes = '';
                $this->fp->new_file();
            }
        }
        if (!$this->parser->interfaces) {
            $this->log_print("  -- none --  ");
        }

        /* GObject's */
        $this->log_print("\n\n" . $this->make_header("Objects", 50, '-'));
        foreach ($this->parser->objects as $object) {
            /* skip ignored objects */
            if ($this->overrides->is_ignored($object->c_name)) {
                $this->log_print("\n%s %s\n", $object->c_name, "is ignored in the overrides file");
                continue;
            }
            $reg_info = $this->write_class($object);
            $register_classes .= aprintf(Templates::register_class, $reg_info);
            if ($this->overrides->have_post_registration($object->c_name)) {
                $register_classes .= $this->overrides->get_post_registration($object->c_name);
            }
            /* size check goes here */
            if ($this->fp->check_size()) {
                // we hit size too big, write out end
                $this->fp->write(sprintf(Templates::register_classes,
                                  $this->lprefix . $this->fp->get_current(),
                                  $register_classes));
                $register_classes = '';
                $this->fp->new_file();
            }
        }
        if (!$this->parser->objects) {
            $this->log_print("  -- none --  ");
        }

        /* GBoxed */
        $this->log_print("\n\n" . $this->make_header("Boxed Types", 50, '-'));
        foreach ($this->parser->boxed as $boxed) {
            $reg_info = $this->write_class($boxed);
            $register_classes .= aprintf(Templates::register_boxed, $reg_info);
            if ($this->overrides->have_post_registration($boxed->c_name)) {
                $register_classes .= $this->overrides->get_post_registration($boxed->c_name);
            }
            /* size check goes here */
            if ($this->fp->check_size()) {
                // we hit size too big, write out end
                $this->fp->write(sprintf(Templates::register_classes,
                                  $this->lprefix . $this->fp->get_current(),
                                  $register_classes));
                $register_classes = '';
                $this->fp->new_file();
            }
        }
        if (!$this->parser->boxed) {
            $this->log_print("  -- none --  ");
        }

        /* GPointer */
        $this->log_print("\n\n" . $this->make_header("Pointers", 50, '-'));
        foreach ($this->parser->pointers as $pointer) {
            $reg_info = $this->write_class($pointer);
            $register_classes .= aprintf(Templates::register_pointer, $reg_info);
            if ($this->overrides->have_post_registration($pointer->c_name)) {
                $register_classes .= $this->overrides->get_post_registration($pointer->c_name);
            }
            /* size check goes here */
            if ($this->fp->check_size()) {
                // we hit size too big, write out end
                $this->fp->write(sprintf(Templates::register_classes,
                                  $this->lprefix . $this->fp->get_current(),
                                  $register_classes));
                $register_classes = '';
                $this->fp->new_file();
            }
        }
        if (!$this->parser->pointers) {
            $this->log_print("  -- none --  ");
        }

        /* register all classes */
        if ($register_classes !== '') {
                $this->fp->write(sprintf(Templates::register_classes,
                                          $this->lprefix . $this->fp->get_current(),
                                          $register_classes));
        }

        $register_classes = '';
        $total = $this->fp->get_total();
        $current = 0;
        while($current < $total) {
                $register_classes .= '	phpg_' . $this->lprefix . $current . "_register_classes(TSRMLS_C);\n";
                $this->fp->write_header('void phpg_' . $this->lprefix . $current . "_register_classes(TSRMLS_D);\n");
                $current++;
        }
        /* write the "meta" register class */
        $this->fp->write(sprintf(Templates::register_classes,
                                          $this->lprefix,
                                          $register_classes));

    }
    
    function write_prop_handlers($object)
    {
        global $matcher;

        if (!$object->fields && !$this->overrides->have_extra_props($object->c_name)) {
            return 'NULL';
        }

        $this->log_print("  %-20s ", "property accessors");
        $num_written = $num_skipped = 0;

        $class = strtolower($object->c_name);
        $prop_defs = array();
        $dict = array();

        switch ($object->def_type) {
            case 'object':
                $dict['cast'] = preg_replace('!_TYPE_!', '_', $object->typecode, 1);
                break;

            case 'boxed':
            case 'pointer':
                $dict['cast'] = $object->c_name . ' *';
                break;

            default:
                throw new Exception("unhandled definition type");
                break;
        }

        foreach ($object->fields as $field) {
            list($field_type, $field_name) = $field;

            $read_func = "PHPG_PROP_READ_FN($object->c_name, $field_name)";
            $write_func = 'NULL';
            $info = new Wrapper_Info();

            try {
                if ($this->overrides->is_prop_overriden($object->c_name, $field_name)) {
                    $overrides = $this->overrides->get_prop_override($object->c_name, $field_name);
                    if (isset($overrides['read'])) {
                        $read_override = preg_replace('!^.*(PHPG_PROP_READER).*$!m', "$1($object->c_name, $field_name)", $overrides['read']);
                        $this->write_override($read_override, $object->c_name, $field_name, 'read');
                        $this->divert("gen", "%%%%  %-11s %s->%s\n", "reader for", $object->c_name, $field_name);
                    } else {
                        $read_func = 'NULL';
                    }
                    if (isset($overrides['write'])) {
                        $write_override = preg_replace('!^.*(PHPG_PROP_WRITER).*$!m', "static $1($object->c_name, $field_name)", $overrides['write']);
                        $this->write_override($overrides['write'], $object->c_name, $field_name, 'write');
                        $this->divert("gen ", "%%%%  %-11s %s->%s\n", "writer for", $object->c_name, $field_name);
                    }
                } else {
                    $handler = $matcher->get($field_type);
                    $handler->write_return($field_type, false, $info);

                    $dict['name'] = $field_name;

                    $this->fp->write(aprintf(Templates::prop_reader,
                                              array('class' => $object->c_name,
                                                    'name' => $field_name,
                                                    'var_list' => $info->get_var_list(),
                                                    'pre_code' => $info->get_pre_code(),
                                                    'post_code' => $info->get_post_code(),
                                                    'prop_access' => aprintf($this->template_map[$object->def_type]['prop'], $dict)
                                                   )));
                    $this->divert("gen", "    %-11s %s->%s\n", "reader for", $object->c_name, $field_name);
                }
                $prop_defs[] = sprintf(Templates::prop_info_entry, 
                                       $field_name, $read_func, $write_func);
                $num_written++;
                $this->cover["props"]->written();
            } catch (Exception $e) {
                $this->divert("notgen", "  %-11s %s->%s: %s\n", "reader for", $object->c_name, $field_name, $e->getMessage());
                $num_skipped++;
                $this->cover["props"]->skipped();
            }
        }

        if ($this->overrides->have_extra_props($object->c_name)) {
            foreach ($this->overrides->get_extra_props($object->c_name) as $prop_name => $overrides) {

                $read_func = "PHPG_PROP_READ_FN($object->c_name, $prop_name)";
                $write_func = 'NULL';
                $info = new Wrapper_Info();

                if (isset($overrides['read'])) {
                    $read_override = preg_replace('!^.*(PHPG_PROP_READER).*$!m', "$1($object->c_name, $prop_name)", $overrides['read']);
                    $this->write_override($read_override, $object->c_name, $prop_name, 'read', 'extra');
                    $this->divert("gen", "%%%%  %-11s %s->%s\n", "reader for", $object->c_name, $prop_name);
                    $this->cover["props"]->written();
                } else {
                    $read_func = 'NULL';
                }

                if (isset($overrides['write'])) {
                    $write_override = preg_replace('!^.*(PHPG_PROP_WRITER).*$!m', "static $1($object->c_name, $prop_name)", $overrides['write']);
                    $this->write_override($overrides['write'], $object->c_name, $prop_name, 'write', 'extra');
                    $this->divert("gen ", "%%%%  %-11s %s->%s\n", "writer for", $object->c_name, $prop_name);
                }

                $prop_defs[] = sprintf(Templates::prop_info_entry, 
                                       $prop_name, $read_func, $write_func);
                $num_written++;
                $this->cover["props"]->written();
            }
        }

        $this->log_print("(%d written, %d skipped)\n", $num_written, $num_skipped);

        if ($prop_defs) {
            $this->fp->write(sprintf(Templates::prop_info_header, $class));
            $this->fp->write(join('', $prop_defs));
            $this->fp->write(Templates::prop_info_footer);
            return $class . '_prop_info';
        } else {
            return 'NULL';
        }
    }

    function write_object_handlers($object)
    {
        $handlers = array();

        foreach ($this->handlers as $handler) {
            if ($this->overrides->is_handler_overriden($object->c_name, $handler)) {
                $override = $this->overrides->get_handler_override($object->c_name, $handler);
                $this->write_override($override, $object->c_name, $handler);
                $handlers[] = $handler;
            }
        }

        if (!$handlers)
            return array('NULL', '');

        $dict['class'] = strtolower($object->in_module . $object->name);
        switch ($object->def_type) {
            case 'object':
                $dict['create_func'] = 'phpg_create_gobject';
                $dict['orig_handlers'] = 'phpg_gobject_handlers';
                break;

            case 'boxed':
                $dict['create_func'] = 'phpg_create_gboxed';
                $dict['orig_handlers'] = 'php_gtk_handlers';
                break;

            case 'pointer':
                $dict['create_func'] = 'phpg_create_gpointer';
                $dict['orig_handlers'] = 'php_gtk_handlers';
                break;

            default:
                throw new Exception("unhandled definition type");
                break;
        }

        $this->fp->write(aprintf(Templates::custom_create_func, $dict));
        $create_func = 'phpg_create_' . $dict['class'];

        $extra_reg_info = aprintf(Templates::custom_handlers_init, $dict);
        foreach ($handlers as $handler) {
            $dict['handler'] = $handler;
            $extra_reg_info .= aprintf(Templates::custom_handler_set, $dict);
        }

        return array($create_func, $extra_reg_info);
    }

    function make_method_defs($class, $method_entries)
    {
        $method_defs = array();

        if ($class->def_type == 'object') {
            foreach ($method_entries as $me) {
                $method_defs[] = vsprintf(Templates::method_entry, $me);
            }
            if ($class->implements) {
                foreach ($class->implements as $interface_name) {
                    $interface = $this->parser->interfaces[$interface_name];
                    $iface_methods = array_diff_key($interface->methods, $method_entries);
                    $method_defs[] = "\n\t/***   $interface_name interface implementations   ***/\n\n";
                    
                    foreach ($iface_methods as $iface_method=>$dummy) {
                        if ($dummy == 1) {
                            $reflection_func = 'NULL';
                        } else {
                            $reflection_func = 'arginfo_' . strtolower($interface->in_module) . '_' . strtolower($interface->c_name) . '_'. $iface_method;
                        }
                        $method_defs[] = sprintf(Templates::alias_entry,
                                                 $interface_name, $iface_method,
                                                 $iface_method, $reflection_func, 'ZEND_ACC_PUBLIC');
                    }
                }
            }
        } else if ($class->def_type == 'boxed' || $class->def_type == 'pointer') {
            foreach ($method_entries as $me) {
                $method_defs[] = vsprintf(Templates::method_entry, $me);
            }
        } else if ($class->def_type == 'interface') {
            foreach ($method_entries as $me) {
                $method_defs[] = sprintf(Templates::abs_method_entry, $me[1], $me[0], $me[1], $me[2], 'ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT');
            }
        }

        return $method_defs;
    }

    function write_class($class)
    {
        $this->log_print("\n%s\n%s\n", $class->c_name, str_repeat('~', 50));

        $class_module = strtolower($class->in_module);
        $class_lname = strtolower($class->name);

        $create_func = 'NULL';
        $extra_reg_info = '';
        $ctor_defs = null;
        $prop_info = 'NULL';
        $ctor_arginfo = '';

        if ($class->def_type != 'interface') {
            /* interfaces don't have these */
            list($ctor_defs, $ctor_arginfo) = $this->write_constructor($class);
            $prop_info = $this->write_prop_handlers($class);
            list($create_func, $extra_reg_info) = $this->write_object_handlers($class);
        }

        list($method_entries, $method_arginfo) = $this->write_methods($class);
        ksort($method_entries);
        $method_defs           = $this->make_method_defs($class, $method_entries);
        
        $arginfo = $ctor_arginfo . $method_arginfo;

        if ($ctor_defs) {
            $method_defs           = array_merge($ctor_defs, $method_defs);
        }

        if ($method_defs) {
            $this->fp->write($arginfo);
            $this->fp->write(sprintf(Templates::functions_decl, strtolower($class->c_name)));
            $this->fp->write(join('', $method_defs));
            $this->fp->write(Templates::functions_decl_end);
        }

        if ($class->def_type == 'object' && $class->implements) {
            $iface_entries = array();
            foreach ($class->implements as $interface) {
                $iface_entries[] = strtolower($interface) . '_ce';
            }
            $extra_reg_info .= aprintf(Templates::implement_interface,
                                       array('ce' => $class->ce,
                                             'ifaces' => join(', ', $iface_entries),
                                             'num_ifaces' => count($iface_entries)));
        }
        /* Do not register gtype constants for interfaces, since interfaces will 
           never be instantiated anyway. */
        if ($class->def_type != 'interface') {
            $this->gtype_constants .= sprintf(Templates::gtype_constant, $class->ce, $class->typecode);
        }

        return array('ce' => $class->ce,
                     'class' => $class->in_module . $class->name,
                     'methods' => $method_defs ? strtolower($class->c_name) . '_methods' : 'NULL',
                     'parent' => ($class->def_type == 'object' && $class->parent) ? strtolower($class->parent) . '_ce' : 'NULL',
                     'ce_flags' => $class->ce_flags ? implode('|', $class->ce_flags) : 0,
                     'typecode' => $class->typecode,
                     'create_func' => $create_func,
                     'extra_reg_info' => $extra_reg_info,
                     'propinfo' => $prop_info);
    }

    function write_constants()
    {
        $enums_code = '';

        foreach ($this->parser->enums as $enum) {
            if ($enum->typecode === null) {
                throw new Exception("unhandled enum type");
                foreach ($enum->values as $nick => $value) {
                }
            } else {
                $enums_code .= sprintf(Templates::register_enum, $enum->def_type,
                                       $enum->typecode, $this->lprefix . '_ce');
            }
        }

        $this->fp->write(sprintf(Templates::register_constants, $this->lprefix,
                                 $enums_code . "\n" . $this->overrides->get_constants(),
                                 $this->gtype_constants));
    }

    function write_functions()
    {
        $func_defs = array();

        $this->log_print("  %-20s", "functions");
        $num_written = $num_skipped = 0;

        $dict['scope'] = $this->prefix;
        
        $function_arginfos = '';
        
        $object = new stdclass;
        $object->in_module = $this->prefix;
        $object->c_name = $this->prefix;

        foreach ($this->parser->functions as $function) {
            $func_name = $function->name;
            if ($function->name == $function->c_name) {
                $func_name = substr($function->name, strlen($this->lprefix) + 1);
            }
            $dict['name'] = $func_name;

            /* skip ignored methods */
            if ($this->overrides->is_ignored($function->c_name)) continue;

            try {
                list($arginfo, $reflection_func) = $this->genReflectionArgInfo($function, $object);
                
                if (($overriden = $this->overrides->is_overriden($function->c_name))) {
                    list($func_name, $function_override, $flags) = $this->overrides->get_override($function->c_name);
                    if (empty($func_name) || $func_name == $function->c_name)
                        $func_name = $function->name;
                    $function_override = preg_replace('!^.*(PHP_METHOD).*$!m', "static $1($this->prefix, $func_name)", $function_override);
                    $this->write_override($function_override, $function->c_name);
                    $func_defs[] = sprintf(Templates::method_entry,
                                           $this->prefix,
                                           $func_name, $reflection_func, $flags ? $flags : 'ZEND_ACC_PUBLIC|ZEND_ACC_STATIC');
                } else {
                    $code = $this->write_callable($function, Templates::function_body, true, false, $dict);
                    $this->fp->write($code);
                    $func_defs[] = sprintf(Templates::method_entry,
                                           $this->prefix,
                                           $func_name, $reflection_func, 'ZEND_ACC_PUBLIC|ZEND_ACC_STATIC');
                }
                $this->divert("gen", "%s  %-11s %s::%s\n", $overriden?"%%":"  ", "function", $this->prefix, $function->name);
                $num_written++;
                $this->cover["funcs"]->written();

                if ($arginfo !== null) {
                    $function_arginfos .= $arginfo;
                }
            } catch (Exception $e) {
                $this->divert("notgen", "  %-11s %s::%s: %s\n", "function", $this->prefix, $function->name, $e->getMessage());
                $num_skipped++;
                $this->cover["funcs"]->skipped();
            }
        }

        if ($this->overrides->have_extra_methods($this->prefix)) {
            foreach ($this->overrides->get_extra_methods($this->prefix) as $func_name => $override) {
                list($func_body, $flags) = $override;
                list($arginfo, $reflection_func) = $this->genReflectionArgInfo(null, $object, $func_name);

                $func_body = preg_replace('!^.*(PHP_METHOD).*$!m', "static $1($this->prefix, $func_name)", $func_body);
                $this->write_override($func_body, $this->prefix, $func_name);
                $func_defs[] = sprintf(Templates::method_entry,
                                       $this->prefix,
                                       $func_name, $reflection_func, 'ZEND_ACC_PUBLIC|ZEND_ACC_STATIC');
                $this->divert("gen", "%%%%  %-11s %s::%s\n", "function", $this->prefix, $func_name);
                $num_written++;

                if ($arginfo !== null) {
                    $function_arginfos .= $arginfo;
                }
            }
        }

        sort($func_defs);
        if ($func_defs) {
            $this->fp->write($function_arginfos);
            $this->fp->write(sprintf(Templates::functions_decl, strtolower($this->lprefix)));
            $this->fp->write(join('', $func_defs));
            $this->fp->write(Templates::functions_decl_end);
        }

        $this->log_print("(%d written, %d skipped)\n", $num_written, $num_skipped);
        return $func_defs;
    }

    function write_prop_lists()
    {
        global  $class_prop_list_header,
                $class_prop_list_footer;
        
        $this->fp->write("\n");
        foreach ($this->parser->objects as $object) {
            if (count($object->fields) == 0) continue;

            $this->fp->write(sprintf($class_prop_list_header,
                                strtolower($object->in_module) . '_' .
                                strtolower($object->name)));
            foreach ($object->fields as $field_def) {
                list(, $field_name) = $field_def;
                $this->fp->write("\t\"$field_name\",\n");
            }
            $this->fp->write($class_prop_list_footer);
        }
    }

    function write_coverage_info()
    {
        $this->log_print("\n\n");
        $this->log_print($this->make_header("$this->prefix Coverage"));

        foreach ($this->cover as $coverage) {
            list($name, $written, $total, $percent) = $coverage->get_stats();
            if ($total) {
                $this->log_print("%-20s: %5.1f%% written (%d/%d)\n", $name, $percent, $written, $total);
            } else {
                $this->log_print("%-20s: none found\n", $name);
            }
        }
        $this->log_print("\n\n");
    }

    function write_class_entries()
    {
        $this->fp->write("\n");
        foreach ($this->parser->interfaces as $interface) {
            $this->fp->write_header(sprintf(Templates::class_entry, $interface->ce));
        }

        foreach ($this->parser->objects as $object) {
            if (!$this->overrides->is_ignored($object->c_name)) {
                $this->fp->write_header(sprintf(Templates::class_entry, $object->ce));
            }
        }

        foreach ($this->parser->boxed as $boxed) {
            $this->fp->write_header(sprintf(Templates::class_entry, $boxed->ce));
        }

        foreach ($this->parser->pointers as $pointer) {
            $this->fp->write_header(sprintf(Templates::class_entry, $pointer->ce));
        }

        if ($this->parser->functions || $this->parser->enums) {
            $this->fp->write_header(sprintf(Templates::class_entry, $this->lprefix . '_ce'));
        }
    }

    function make_header($string, $width = 72, $char = '=')
    {
        $res = "\n" . str_repeat($char, $width) . "\n";
        $res .= str_pad($string, $width, " ", STR_PAD_BOTH) . "\n";
        $res .= str_repeat($char, $width) . "\n";

        return $res;
    }

    function write_source($savefile)
    {
        $this->not_generated_list = array();
        $this->log_print($this->make_header("$this->prefix Summary"));

        $this->fp = new LineOutput($savefile);
        $this->fp->write_header("#include \"php_gtk.h\"\n");
        $this->fp->write_header("\n#if HAVE_PHP_GTK\n");
        $this->fp->write_header($this->overrides->get_headers());
        $this->write_class_entries();
        $this->write_classes();
        $this->write_constants();
        $this->fp->write_all("\n#endif /* HAVE_PHP_GTK */\n");

        $this->fp->save();

        $this->log("\n\n");
        $this->log($this->make_header("$this->prefix Generated Items"));
        $this->log("%%%% - overridden\n\n");
        $this->log("%s", $this->diversions["gen"]);

        $this->log("\n\n");
        $this->log($this->make_header("$this->prefix Not Generated Items"));
        if (!empty($this->diversions["notgen"])) {
            $this->log("%s", $this->diversions["notgen"]);
        }

        $this->write_coverage_info();
        $this->write_unused_info();
    }
    
    
    
    /**
    *   generates an ZEND_ARGINFO entry for this method
    *   based on the parameters and returns it
    *
    *   @param Method $method           The method (or constructor) to generate the arginfo for (if NULL, then we check for overridden only)
    *   @param Class  $class            The class of the method
    *   @param string $det_method_name  Special determined method name if it can't be calculated from the method object
    *   @return array The arginfo (string) and the name of the reflection function
    */
    function genReflectionArgInfo($method, $class, $det_method_name = null)
    {
        if ($det_method_name !== null) {
            $len = 20 - strlen($det_method_name);
        } else {
            $len = 20 - strlen($method->name);
        }
        if ($len < 0) { $len = 0; }

        if ($method === null || ($overriden = $this->overrides->is_overriden($method->c_name))) {
            //overridden function - extra arginfo in override file?
            $class_name  = $class->c_name;
            if ($det_method_name !== null) {
                $method_name = $det_method_name;
            } else {
                $overrideinfo = $this->overrides->get_override($method->c_name);
                $method_name = $overrideinfo[0];
                if (empty($method_name) || $method_name == $method->c_name) {
                    $method_name = $method->name;
                }
            }

            if ($this->overrides->has_extra_arginfo($class_name, $method_name)) {
                $reflection_funcname = Generator::getReflectionFuncName($method, $class, $det_method_name);
                $reflection_func = str_repeat(' ', $len) . $reflection_funcname;

                $arginfo = str_replace('ARGINFO_NAME', $reflection_funcname, $this->overrides->get_extra_arginfo($class_name, $method_name));
            } else {
                //no arginfo
                $reflection_func = str_repeat(' ', $len) . 'NULL';
                $arginfo = null;
                //var_dump('No arginfo for overridden ' . $reflection_funcname);
            }

        } else if (count($method->params) == 0) {
            $reflection_func = str_repeat(' ', $len) . 'NULL';
            $arginfo = null;
        } else {
            $reflection_funcname = Generator::getReflectionFuncName($method, $class);
            $reflection_func = str_repeat(' ', $len) . $reflection_funcname;

            $param_count = 0;
            $optparam_count = 0;
            $argparams = '';
            foreach ($method->params as $paraminfo) {
                $param_count++;
                if ($paraminfo[2] !== null) {
                    //if this is set, we've got a default value -> optional parameter
                    $optparam_count++;
                }

                $paramtype = str_replace('const-', '', str_replace('*', '', $paraminfo[0]));
                if (Generator::is_php_type($paramtype)) {
                    $argparams .= sprintf(Templates::reflection_arg, $paraminfo[1]);
                } else {
                    $argparams .= sprintf(Templates::reflection_objarg, $paraminfo[1], $paramtype);
                }
            }
            if ($optparam_count > 0) {
                //with optional count
                $arginfo = sprintf(Templates::reflection_arginfoex_begin, $reflection_funcname, $param_count - $optparam_count);
            } else {
                //simple one
                $arginfo = sprintf(Templates::reflection_arginfo_begin, $reflection_funcname);
            }

            $arginfo .= $argparams;
            $arginfo .= Templates::reflection_arginfo_end;
        }
        return array($arginfo, $reflection_func);
    }//function genReflectionArgInfo($method, $class)



    function getReflectionFuncName($method, $class, $det_method_name = null)
    {
        if ($method === null) {
            $method = new stdclass;
            $method->name = $det_method_name;
        }
        return 'arginfo_' . strtolower($class->in_module) . '_' . strtolower($class->c_name) . '_'. $method->name;
    }//function getReflectionFuncName($method, $class, $det_method_name = null)



    function write_unused_info()
    {
        $unused = $this->overrides->get_unused_arginfo();
        
        $unused_count = 0;
        $output = '';
        foreach ($unused as $class => $methods) {
            foreach ($methods as $method => $true) {
                $unused_count++;
                list($line, $filename) = $this->overrides->get_line_info("$class.$method.arginfo");
                $output .= ' ' . str_pad($class . '::' . $method, 40) . ' ' . $filename . '#' . $line . "\n";
            }
        }
        if ($unused_count > 0) {
            $this->log_print('Unused arginfo overrides:' . "\n");
            $this->log_print($output);
            $this->log_print("\n\n");
        }
    }



    /**
     * Checks if the given type is a simple php type
     *
     * All unsupported classes have to be here as php5 functions weird if
     *  reflection is used with this ones...
     * Use generator/reflection_class_checker.php to missing classes, and
     *  add them here
     *
     * @access public
     * @param  string $in_type The C type.
     * @return boolean true if its a simple type
     */
    function is_php_type($in_type)
    {
        // Key = C Type, Val = PHP type
        static $type_map = array('none'           => 'void',

                                 'char*'          => 'string',
                                 'gchar*'         => 'string',
                                 'const-char*'    => 'string',
                                 'const-gchar*'   => 'string',
                                 'string'         => 'string',
                                 'static_string'  => 'string',
                                 'unsigned-char*' => 'string',
                                 'guchar*'        => 'string',
                                 
                                 'char'           => 'char',
                                 'gchar'          => 'char',
                                 'guchar'         => 'char',
                                 
                                 'int'            => 'int',
                                 'gint'           => 'int',
                                 'guint'          => 'int',
                                 'short'          => 'int',
                                 'gshort'         => 'int',
                                 'gushort'        => 'int',
                                 'long'           => 'int',
                                 'glong'          => 'int',
                                 'gulong'         => 'int',
                                 
                                 'guint8'         => 'int',
                                 'gint8'          => 'int',
                                 'guint16'        => 'int',
                                 'gint16'         => 'int',
                                 'guint32'        => 'int',
                                 'gint32'         => 'int',
                                 
                                 'gsize'          => 'int',
                                 
                                 //all enumeration classes here
                                 //gtk enums
                                 'GtkAccelFlags'        => 'int',
                                 'GtkAnchorType'        => 'int',
                                 'GtkArrowType'         => 'int',
                                 'GtkAttachOptions'     => 'int',
                                 'GtkButtonBoxStyle'    => 'int',
                                 'GtkButtonsType'       => 'int',
                                 'GtkCalendarDisplayOptions' => 'int',
                                 'GtkCellRendererState' => 'int',
                                 'GtkCornerType'        => 'int',
                                 'GtkCTreeExpanderStyle'=> 'int',
                                 'GtkCTreeLineStyle'    => 'int',
                                 'GtkCurveType'         => 'int',
                                 'GtkDeleteType'        => 'int',
                                 'GtkDestroyNotify'     => 'int',
                                 'GtkDialogFlags'       => 'int',
                                 'GtkDirectionType'     => 'int',
                                 'GtkExpanderStyle'     => 'int',
                                 'GtkFileChooserAction' => 'int',
                                 'GtkFunction'          => 'int',
                                 'GtkIconLookupFlags'   => 'int',
                                 'GtkIconSize'          => 'int',
                                 'GtkIMPreeditStyle'    => 'int',
                                 'GtkIMStatusStyle'     => 'int',
                                 'GtkJustification'     => 'int',
                                 'GtkMatchType'         => 'int',
                                 'GtkMessageType'       => 'int',
                                 'GtkMetricType'        => 'int',
                                 'GtkMovementStep'      => 'int', 
                                 'GtkOrientation'       => 'int',
                                 'GtkPackDirection'     => 'int',
                                 'GtkPackType'          => 'int',
                                 'GtkPathPriorityType'  => 'int',
                                 'GtkPathType'          => 'int',
                                 'GtkPolicyType'        => 'int',
                                 'GtkPositionType'      => 'int',
                                 'GtkPreviewType'       => 'int',
                                 'GtkProgressBarOrientation' => 'int',
                                 'GtkProgressBarStyle'  => 'int',
                                 'GtkReliefStyle'       => 'int',
                                 'GtkResizeMode'        => 'int',
                                 'GtkScrollStep'        => 'int',
                                 'GtkScrollType'        => 'int',
                                 'GtkSelectionMode'     => 'int',
                                 'GtkShadowType'        => 'int',
                                 'GtkSideType'          => 'int',
                                 'GtkSizeGroupMode'     => 'int',
                                 'GtkSpinButtonUpdatePolicy' => 'int',
                                 'GtkSpinType'          => 'int',
                                 'GtkStateType'         => 'int',
                                 'GtkStockItem'         => 'int',
                                 'GtkSubmenuDirection'  => 'int',
                                 'GtkSubmenuPlacement'  => 'int',
                                 'GtkTextDirection'     => 'int',
                                 'GtkTextSearchFlags'   => 'int',
                                 'GtkTextWindowType'    => 'int',
                                 'GtkToolbarStyle'      => 'int',
                                 'GtkTreeCellDataFunc'  => 'int',
                                 'GtkTreeModel'         => 'int',
                                 'GtkTreePath'          => 'int',
                                 'GtkTreeViewColumnSizing' => 'int',
                                 'GtkTreeViewDropPosition' => 'int',
                                 'GtkType'              => 'int',
                                 'GtkUIManagerItemType' => 'int',
                                 'GtkUpdateType'        => 'int',
                                 'GtkVisibility'        => 'int',
                                 'GtkWindowPosition'    => 'int',
                                 'GtkWindowType'        => 'int',
                                 'GtkWrapMode'          => 'int',
                                 'GtkSortType'          => 'int',
								 // 2.10
                                 'GtkUnit'        => 'int',
                                 'GtkTreeViewGridLines'          => 'int',
                                 'GtkTextBufferTargetInfo'          => 'int',
                                 'GtkSensitivityType'        => 'int',
                                 'GtkRecentSortType'          => 'int',
                                 'GtkRecentManagerError'          => 'int',
                                 'GtkRecentChooserError '        => 'int',
                                 'GtkPrintStatus'          => 'int',
                                 'GtkPrintQuality'          => 'int',
                                 'GtkPrintPages'        => 'int',
                                 'GtkPrintOperationResult'          => 'int',
                                 'GtkPrintOperationAction'          => 'int',
                                 'GtkPrintError'          => 'int',
                                 'GtkPrintDuplex'          => 'int',
                                 'GtkPageSet'        => 'int',
                                 'GtkPageOrientation'          => 'int',
                                 'GtkCellRendererAccelMode'          => 'int',
                                 'GtkAssistantPageType'          => 'int',
                                 //gdk enums
                                 'GdkAxisUse'           => 'int',
                                 'GdkBitmap'            => 'int',
                                 'GdkByteOrder'         => 'int',
                                 'GdkCapStyle'          => 'int',
                                 'GdkColorspace'        => 'int',
                                 'GdkCursorType'        => 'int',
                                 'GdkCrossingMode'      => 'int',
                                 'GdkDragAction'        => 'int',
                                 'GdkDragProtocol'      => 'int',
                                 'GdkEventExpose'       => 'int',
                                 'GdkEventKey'          => 'int',
                                 'GdkEventSelection'    => 'int',
                                 'GdkEventType'         => 'int',
                                 'GdkEventMask'         => 'int',
                                 'GdkExtensionMode'     => 'int',
                                 'GdkGravity'           => 'int',
                                 'GdkFill'              => 'int',
                                 'GdkFillRule'          => 'int',
                                 'GdkFilterReturn'      => 'int',
                                 'GdkFontType'          => 'int',
                                 'GdkFunction'          => 'int',
                                 'GdkGCValuesMask'      => 'int',
                                 'GdkGrabStatus'        => 'int',
                                 'GdkImageType'         => 'int',
                                 'GdkInputCondition'    => 'int',
                                 'GdkInputSource'       => 'int',
                                 'GdkInputMode'         => 'int',
                                 'GdkInterpType'        => 'int',
                                 'GdkJoinStyle'         => 'int',
                                 'GdkLineStyle'         => 'int',
                                 'GdkModifierType'      => 'int',
                                 'GdkNativeWindow'      => 'int',
                                 'GdkNotifyType'        => 'int',
                                 'GdkOverlapType'       => 'int',
                                 'GdkOwnerChange'       => 'int',
                                 'GdkPixbufAlphaMode'   => 'int',
                                 'GdkPropertyState'     => 'int',
                                 'GdkPropMode'          => 'int',
                                 'GdkRgbDither'         => 'int',
                                 'GdkScrollDirection'   => 'int',
                                 'GdkSettingAction'     => 'int',
                                 'GdkSubwindowMode'     => 'int',
                                 'GdkVisibilityState'   => 'int',
                                 'GdkVisualType'        => 'int',
                                 'GdkWindowAttributesType'  => 'int',
                                 'GdkWindowClass'       => 'int',
                                 'GdkWindowEdge'        => 'int',
                                 'GdkWindowHints'       => 'int',
                                 'GdkWindowState'       => 'int',
                                 'GdkWindowTypeHint'    => 'int',
                                 'GdkWMDecoration'      => 'int',
                                 'GdkWMFunction'        => 'int',

                                 // libsexy enums
                                 'SexyIconEntryPosition' => 'int',
                                 'SexySpellError'        => 'int',

                                 // gtkextra enums
                                 'GtkIconListMode'            => 'int',
                                 'GtkPlotPlane'               => 'int',
                                 'GtkPlotBarUnits'            => 'int',
                                 'GtkPlotCanvasAction'        => 'int',
                                 'GtkPlotCanvasSelection'     => 'int',
                                 'GtkPlotCanvasPos'           => 'int',
                                 'GtkPlotCanvasSelectionMode' => 'int',
                                 'GtkPlotCanvasPlotPos'       => 'int',
                                 'GtkPlotProjection'          => 'int',
                                 'GtkPlotScale'               => 'int',
                                 'GtkPlotSymbolType'          => 'int',
                                 'GtkPlotSymbolStyle'         => 'int',
                                 'GtkPlotBorderStyle'         => 'int',
                                 'GtkPlotLineStyle'           => 'int',
                                 'GtkPlotConnector'           => 'int',
                                 'GtkPlotError'               => 'int',
                                 'GtkPlotOrientation'         => 'int',
                                 'GtkPlotAxisPos'             => 'int',
                                 'GtkPlotLabelStyle'          => 'int',
                                 'GtkPlotScale'               => 'int',
                                 'GtkPlotPageSize'            => 'int',
                                 'GtkPlotPageOrientation'     => 'int',
                                 'GtkPlotUnits'               => 'int',
                                 'GtkSheetAttrType'           => 'int',

                                 //Pango enums
                                 'PangoAlignment'       => 'int',
                                 'PangoDirection'       => 'int',
                                 'PangoEllipsizeMode'   => 'int',
                                 'PangoFontMask'        => 'int',
                                 'PangoRectangle'       => 'int',
                                 'PangoStretch'         => 'int',
                                 'PangoStyle'           => 'int',
                                 'PangoTabAlign'        => 'int',
                                 'PangoVariant'         => 'int',
                                 'PangoWeight'          => 'int',
                                 'PangoWrapMode'        => 'int',
                                 
                                 //Atk enums
                                 'AtkRole'              => 'int',
                                 'AtkRelationType'      => 'int',
                                 'AtkStateType'         => 'int',
                                 'AtkTextAttribute'     => 'int',
                                                                  
                                 //some more
                                 'GDestroyNotify'       => 'int',
                                 'GError'               => 'int',
                                 'GSList'               => 'int',
                                 'GValue'               => 'int',
                                 
                                 'gpointer'             => 'int',
                                 
                                 'gboolean'       => 'bool',
                                 
                                 'double'         => 'double',
                                 'gdouble'        => 'double',
                                 'float'          => 'double',
                                 'gfloat'         => 'double',
                                 
                                 'GdkDrawable*'   => 'GdkWindow',
                                 'GdkAtom'        => 'string');
        
        return isset($type_map[$in_type]);
    }//function is_php_type($in_type)
}

/* simple fatal_error function 
        - useful in 4.2.0-dev and later as it will actually halt exectution of make
    - needs to output to stderr as the default print would end up inside the code
    - TODO - the make file outputs an empty file to gen_XXX.c, there must be a
      better way  to delete it... - by using output buffering or somthing?
*/  
function fatal_error($message) {
    $fh = fopen("php://stderr", "w");
        fwrite($fh,"\n\n\n\n
========================================================================    
    There was a Serious error with the PHP-GTK generator script
========================================================================    
    $message
========================================================================    
    You should type this to ensure that the c source is correctly
    generated before attempting to make again.
    
    #find . | grep defs | xargs touch

    \n\n\n\n");
    fclose($fh);
    exit(1);
}


$old_error_reporting = error_reporting(E_ALL);

if (!isset($_SERVER['argv'])) 
    fatal_error("
        Could not read command line arguments for generator.php 
        Please ensure that this option is set in your php.ini
        register_argc_argv = On     
    ");


if (isset($_SERVER['argc']) &&
    isset($_SERVER['argv'])) {
    $argc = $_SERVER['argc'];
    $argv = $_SERVER['argv'];
}

 
$result = Console_Getopt::getopt($argv, 'l:o:p:c:r:f:v:');
if (!$result || count($result[1]) < 2)
    die("usage: php generator.php [-l logfile] [-o overridesfile] [-p prefix] [-c functionclass ] [-r typesfile] [-f savefile] [-v gtklibversion] defsfile\n");

list($opts, $argv) = $result;

$prefix = 'Gtk';
$function_class = null;
$overrides = null;
$register_defs = array();
$savefile = 'tmp.c';
$logfile = 'php://stderr';
$gtkversion = '2.6';

foreach ($opts as $opt) {
    list($opt_spec, $opt_arg) = $opt;
    if ($opt_spec == 'o') {
        $overrides = $opt_arg;
    } else if ($opt_spec == 'p') {
        $prefix = $opt_arg;
    } else if ($opt_spec == 'c') {
        $function_class = $opt_arg;
    } else if ($opt_spec == 'r') {
        $register_defs[] = $opt_arg;
    } else if ($opt_spec == 'f') {
        $savefile = $opt_arg;
    } else if ($opt_spec == 'l') {
        $logfile = $opt_arg;
    } else if ($opt_spec == 'v') {
        $gtkversion = $opt_arg;
    }
}

if (file_exists(dirname($argv[1]) . '/arg_types.php')) {
    include(dirname($argv[1]) . '/arg_types.php');
}

$overrides = new Overrides($overrides, $gtkversion);
$parser = new Defs_Parser($argv[1], $gtkversion);
$generator = new Generator($parser, $overrides, $prefix, $function_class);
$generator->set_logfile($logfile);
foreach ($register_defs as $defs) {
    $type_parser = new Defs_Parser($defs, $gtkversion);
    $type_parser->start_parsing();
    $generator->register_types($type_parser);
}
$parser->start_parsing();
$generator->register_types();
$generator->write_source($savefile);

error_reporting($old_error_reporting);

/* vim: set et sts=4: */
?>
