import re
import sys


_CNTRL_CHARS = map(chr, range(0x0, 0x1f) + [0x7f])
_SERIALIZED_ARRAY_REGEX = re.compile(r'(a\:\d+\:\{[is]\:[^\}]+})')


class PHPScalarException(Exception):
    pass


class PHPSerializationException(Exception):
    pass


def _has_cntrl_chars(str):
    global cntrl_chars

    ret = False
    for char in str:
        if char in _CNTRL_CHARS:
            ret = True
            break

    return ret


def generate_scalar(scalar_val, upper_keywords=True):
    ret = None

    if scalar_val is None:
        ret = 'null'

        if upper_keywords:
            ret = 'NULL'
    elif type(scalar_val) is bool:
        if scalar_val:
            ret = 'TRUE'
        else:
            ret = 'FALSE'

        if not upper_keywords:
            ret = ret.lower()
    elif type(scalar_val) is str:
        quote_type = '\''

        if _has_cntrl_chars(scalar_val):
            quote_type = '"'

        if quote_type in scalar_val:
            replacement = '\\%s' % (quote_type,)
            scalar_val = scalar_val.replace(quote_type, replacement)

        ret = '%s%s%s' % (quote_type, scalar_val, quote_type)
    elif type(scalar_val) is int:
        ret = '%d' % (scalar_val)
    elif type(scalar_val) is float:
        ret = '%f' % (scalar_val)

    if ret is None:
        raise PHPScalarException('Non-acceptable type: %s' %
                                 (type(scalar_val)))

    return ret


def generate_array(list_or_array, indent=2, last_level=0, end=';'):
    spaces = ''.join([' ' for i in range(0, indent)])
    end_bracket_spaces = ''.join([' ' for i in range(0, indent - last_level)])

    if type(list_or_array) in (tuple, list, set):
        parts = [
            'array(',
        ]

        for item in list_or_array:
            if type(item) not in (tuple, list, set, dict):
                parts.append('%s%s,' % (spaces, generate_scalar(item)))
            else:
                parts.append('%s%s' % (spaces,
                                       generate_array(item,
                                                      indent=indent + indent,
                                                      end=',',
                                                      last_level=indent)))

        parts.append('%s)%s' % (end_bracket_spaces, end))

        return '\n'.join(parts)

    parts = [
        'array(',
    ]

    keys = list_or_array.keys()

    if '_order' in list_or_array:
        keys = list_or_array['_order']

    for key in keys:
        value = list_or_array[key]
        is_scalar = type(value) not in (tuple, list, set, dict)
        if not is_scalar:
            value = generate_array(value, indent=indent + indent,
                                   end=',', last_level=indent)
        else:
            value = generate_scalar(value)

        key_quote_type = '\''
        current_end = ','

        if _has_cntrl_chars(key):
            key_quote_type = '"'

        if not is_scalar:
            value_quote_type = current_end = ''

        parts.append('%s%s%s%s => %s%s' % (
            spaces,
            key_quote_type, key, key_quote_type,
            value,
            current_end,
        ))

    last_line = '%s)%s' % (end_bracket_spaces, end)

    if end == ';':
        last_line = last_line[indent:]

    parts.append(last_line)

    return '\n'.join(parts)


def serialize(mixed_value):
    try:
        from phpserialize import serialize as s
        return s(mixed_value)
    except ImportError:
        pass

    """Based on http://phpjs.org/functions/unserialize/"""
    ktype = vals = ''
    count = 0

    if mixed_value is None:
        return 'N;'
    elif type(mixed_value) is bool:
        if mixed_value:
            return 'b:1;'
        else:
            return 'b:0;'
    elif type(mixed_value) is int:
        return 'i:%d;' % (mixed_value)
    elif type(mixed_value) is float:
        return 'd:%f;' % (mixed_value)
    elif type(mixed_value) is str:
        return 's:%d:"%s";' % (len(mixed_value), mixed_value)

    if type(mixed_value) in (tuple, list, set):
        length = len(mixed_value)
        serialized_set = []
        index = 0
        for value in mixed_value:
            serialized_set.append(serialize(index))
            serialized_set.append(serialize(value))
            index += 1
        return 'a:%d:{%s}' % (length, ''.join(serialized_set),)

    # Dictionary
    length = len(mixed_value)
    serialized_set = []
    for (key, value) in mixed_value.items():
        serialized_set.append(serialize(key))
        serialized_set.append(serialize(value))
    return 'a:%d:{%s}' % (length, ''.join(serialized_set),)


def unserialize(str):
    from phpserialize import unserialize as u
    return u(str)

@@ -1243,16 +1243,17 @@
   ret = 
+(
 '%25f' %25 (
@@ -1263,16 +1263,29 @@
 lar_val)
+).rstrip('0')
 %0A%0A    if
@@ -1498,16 +1498,105 @@
 d=';'):%0A
+    val = indent%0A    if last_level %3E 0:%0A        val *= last_level%0A        val += indent%0A%0A
     spac
@@ -1631,22 +1631,19 @@
 ange(0, 
-indent
+val
 )%5D)%0A    
@@ -1698,27 +1698,20 @@
 (0, 
+val - 
 indent
- - last_level
 )%5D)%0A
@@ -2166,33 +2166,24 @@
 ndent=indent
- + indent
 ,%0A          
@@ -2300,22 +2300,30 @@
 t_level=
-indent
+last_level + 1
 )))%0A%0A   
@@ -2762,17 +2762,8 @@
 dent
- + indent
 ,%0A  
@@ -2815,22 +2815,30 @@
 t_level=
-indent
+last_level + 1
 )%0A      
@@ -3315,67 +3315,8 @@
 d)%0A%0A
-    if end == ';':%0A        last_line = last_line%5Bindent:%5D%0A%0A
     

Fix indenting for more than 2 levels of list or dict