#! /usr/bin/env python
# -*- coding: UTF-8 -*-
#
# $Id$
#
# SWIG based PHP binding for Lasso Library
#
# Copyright (C) 2004-2007 Entr'ouvert
# http://lasso.entrouvert.org
#
# Authors: See AUTHORS file in top-level directory.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


"""Correct SWIG output for PHP binding.

The PHP binding of SWIG version 1.3.22 has several bugs:

(1) It wraps NULL pointers into non NULL PHP objects.

(2) It doesn't handle dynamic cast of function results well: After the C object is dynamically
    casted, it creates a statically casted PHP object.

(3) It handles badly optional arguments of methods.

This program corrects (1) and (2), by replacing things like:
    if (!result) {
        ZVAL_NULL(return_value);
    } else {
        swig_type_info *ty = SWIG_TypeDynamicCast(SWIGTYPE_p_LassoXXX, (void **) &result);
        SWIG_SetPointerZval(return_value, (void *)result, ty, 1);
    }

    /* Wrap this return value */
    if (this_ptr) {
        /* NATIVE Constructor, use this_ptr */
        zval *_cPtr; MAKE_STD_ZVAL(_cPtr);
        *_cPtr = *return_value;
        INIT_ZVAL(*return_value);
        add_property_zval(this_ptr,"_cPtr",_cPtr);
    } else if (! this_ptr) {
        /* ALTERNATIVE Constructor, make an object wrapper */
        zval *obj, *_cPtr;
        MAKE_STD_ZVAL(obj);
        MAKE_STD_ZVAL(_cPtr);
        *_cPtr = *return_value;
        INIT_ZVAL(*return_value);
        object_init_ex(obj,ptr_ce_swig_LassoXXX);
        add_property_zval(obj,"_cPtr",_cPtr);
        *return_value=*obj;
    }
with:
    if (!result) {
        ZVAL_NULL(return_value);
    } else {
        swig_type_info *ty = SWIG_TypeDynamicCast(SWIGTYPE_p_LassoXXX, (void **) &result);
        SWIG_SetPointerZval(return_value, (void *)result, ty, 1);
    /* Wrap this return value */
    if (this_ptr) {
        /* NATIVE Constructor, use this_ptr */
        zval *_cPtr; MAKE_STD_ZVAL(_cPtr);
        *_cPtr = *return_value;
        INIT_ZVAL(*return_value);
        add_property_zval(this_ptr,"_cPtr",_cPtr);
    } else if (! this_ptr) {
        /* ALTERNATIVE Constructor, make an object wrapper */
        zval *obj, *_cPtr;
        MAKE_STD_ZVAL(obj);
        MAKE_STD_ZVAL(_cPtr);
        *_cPtr = *return_value;
        INIT_ZVAL(*return_value);
        object_init_ex(obj,get_node_info_with_swig(ty)->php);
        add_property_zval(obj,"_cPtr",_cPtr);
        *return_value=*obj;
    }}
and
    if (!result) {
        ZVAL_NULL(return_value);
    } else {
        swig_type_info *ty = SWIG_TypeDynamicCast(SWIGTYPE_p_LassoXXX, (void **) &result);
        SWIG_SetPointerZval(return_value, (void *)result, ty, 0);
    }

    /* Wrap this return value */
    {
        /* ALTERNATIVE Constructor, make an object wrapper */
        zval *obj, *_cPtr;
        MAKE_STD_ZVAL(obj);
        MAKE_STD_ZVAL(_cPtr);
        *_cPtr = *return_value;
        INIT_ZVAL(*return_value);
        object_init_ex(obj,ptr_ce_swig_LassoXXX);
        add_property_zval(obj,"_cPtr",_cPtr);
        *return_value=*obj;
    }
with:
    if (!result) {
        ZVAL_NULL(return_value);
    } else {
        swig_type_info *ty = SWIG_TypeDynamicCast(SWIGTYPE_p_LassoXXX, (void **) &result);
        SWIG_SetPointerZval(return_value, (void *)result, ty, 0);
    /* Wrap this return value */
    {
        /* ALTERNATIVE Constructor, make an object wrapper */
        zval *obj, *_cPtr;
        MAKE_STD_ZVAL(obj);
        MAKE_STD_ZVAL(_cPtr);
        *_cPtr = *return_value;
        INIT_ZVAL(*return_value);
        object_init_ex(obj,get_node_info_with_swig(ty)->php);
        add_property_zval(obj,"_cPtr",_cPtr);
        *return_value=*obj;
    }}

In old SWIG versions, this program corrects (3), by replacing things like:
    if(zend_get_parameters_array_ex(arg_count-argbase,args)!=SUCCESS)
with:
    if(zend_get_parameters_array_ex(arg_count,args)!=SUCCESS)
and by replacing:
    if(arg_count > 1) {
with:
    if(arg_count > 1 - argbase) {

In newer SWIG versions, this program corrects (3), by replacing code like:
    if(arg_count<2 || arg_count>4)
with:
    if(arg_count<1 || arg_count>3)
whenever the function uses a this_ptr.
"""

import re
import sys

wrap = sys.stdin.read()
swig_version = sys.argv[1]
major, minor, release = re.match('(.*)\.(.*)\.(.*)', swig_version).groups()
major = int(major)
minor = int(minor)
release = int(release)
if major < 1 or (major == 1 and (minor < 3 or (minor == 3 and release < 32))):
# (1)
    begin = """
  }

  /* Wrap this return value */
"""
    end = """
    *return_value=*obj;
  }
"""
    i = wrap.find(begin)
    while i >= 0:
        j = wrap.find(end, i) + len(end)
        segment = wrap[i:j]
        segment = segment.replace(begin, """
  /* Wrap this return value */
""")
        segment = segment.replace(end, """
    *return_value=*obj;
  }}
""")
        wrap = '%s%s%s' % (wrap[:i], segment, wrap[j:])
        i = wrap.find(begin, i + len(segment))

# (2)
    begin = 'swig_type_info *ty = SWIG_TypeDynamicCast('
    end = """
    *return_value=*obj;
  }}
"""
    i = wrap.find(begin)
    while i >= 0:
        j = wrap.find(end, i) + len(end)
        #print >> sys.stderr, "END:", j, len(end)
        if j < len(end): # bails out if not found
            break
        segment = wrap[i:j]
        x = segment.find('object_init_ex(obj,') + len('object_init_ex(obj,')
        y = segment.find(')', x)
        segment = '%s%s%s' % (segment[:x], 'get_node_info_with_swig(ty)->php', segment[y:])
        wrap = '%s%s%s' % (wrap[:i], segment, wrap[j:])
        i = wrap.find(begin, i + len(segment))

# (3)
    wrap = wrap.replace('if(zend_get_parameters_array_ex(arg_count-argbase,args)!=SUCCESS)',
                        'if(zend_get_parameters_array_ex(arg_count,args)!=SUCCESS)')


    function_pattern = re.compile('ZEND_NAMED_FUNCTION(.*?)\n}', re.DOTALL)
    argcount_less_pattern = re.compile('if\(arg_count<(\d) \|\| arg_count>(\d)')
    argcount_more_pattern = re.compile('if\(arg_count > (\d)\)')


    def rep2(match):
        arg1 = int(match.group(1)) - 1
        arg2 = int(match.group(2)) - 1
        return 'if(arg_count<%s || arg_count>%s' % (arg1, arg2)

    def rep3(match):
        arg1 = int(match.group(1)) - 1
        return 'if(arg_count > %s)' % arg1

    def rep(match):
        m = match.group(0)
        if not 'This function uses a this_ptr' in m:
            return m
        if not 'arg_count<' in m:
            return m
        lines = match.group(0).splitlines()
        s = []
        for l in lines:
            if l.startswith('if(arg_count<'):
                l = argcount_less_pattern.sub(rep2, l)
            elif l.startswith('  if(arg_count >'):
                l = argcount_more_pattern.sub(rep3, l)
            s.append(l)

        return ''.join(s)

    wrap = function_pattern.sub(rep, wrap)

    wrap = re.sub(r'zend_register_internal_class_ex(.*)NULL,NULL\)',
        r'zend_register_internal_class_ex\1NULL,NULL TSRMLS_CC)',  wrap)

    wrap = re.sub('zend_rsrc_list_get_rsrc_type(.*)lval *\)',
        r'zend_rsrc_list_get_rsrc_type\1lval TSRMLS_CC)', wrap)
else:
# Bis for swig 1.3.33
# (1)
    begin = """
  }

  {
    /* Wrap this return value */
"""
    end = """
  }
"""
    i = wrap.find(begin)
    while i >= 0:
        j = wrap.find(end, i+len(begin)) + len(end)
        segment = wrap[i:j]
        segment = segment.replace(begin, """
  /* Wrap this return value */
""")
        segment = segment.replace(end, """
  }
""")
        wrap = '%s%s%s' % (wrap[:i], segment, wrap[j:])
        i = wrap.find(begin, i + len(segment))
# (2)
    begin = 'swig_type_info *ty = SWIG_TypeDynamicCast('
    end = """
  }
"""
    i = wrap.find(begin)
    while i >= 0:
        j = wrap.find(end, i+len(begin)) + len(end)
        if j < len(end): # bails out if not found
            i = wrap.find(begin, i + len(segment))
            break
        segment = wrap[i:j]
        if not 'object_init_ex' in segment:
            i = wrap.find(begin, i + len(segment))
            continue
        x = segment.find('object_init_ex(return_value,') + len('object_init_ex(return_value,')
        y = segment.find(')', x)
        segment = '%s%s%s' % (segment[:x], 'get_node_info_with_swig(ty)->php', segment[y:])
        wrap = '%s%s%s' % (wrap[:i], segment, wrap[j:])
        i = wrap.find(begin, i + len(segment))

print wrap
