#coding: utf8
import copy
import re

from blocks import Block, EmptyBlock, KeyValueOption, Comment, Location


def parse(s, parent_block):
    config = copy.copy(s)
    pos, brackets_level, param_start = 0, 0, 0

    while pos < len(config):
        if config[pos] == '#' and brackets_level == 0:
            re_sharp_comment = re.search('(?P<offset>[\s\n]*)#(?P<comment>.*)$', config, re.M)
            sharp_comment = re_sharp_comment.groupdict()
            parent_block.add_comment(Comment(sharp_comment['offset'], sharp_comment['comment']))
            config = config[re_sharp_comment.end():]
            pos, param_start = 0, 0
            continue

        if config[pos] == ';' and brackets_level == 0:
            re_option = re.search('\s*(?P<param_name>\w+)\s*(?P<param_options>.*?);', config[param_start:], re.S)
            if not re_option:
                raise Exception('Wrong option')

            option = re_option.groupdict()
            parent_block[option['param_name']] = KeyValueOption(re.sub('[ \n]+', ' ', option['param_options']))

            config = config[re_option.end():]
            pos, param_start = 0, 0
            continue

        if config[pos] == '{':
            brackets_level += 1
        elif config[pos] == '}':
            brackets_level -= 1

            if brackets_level == 0 and param_start is not None:
                re_block = re.search(
                    '(?P<param_name>\w+)\s*(?P<param_options>.*)\s*{(\n){0,1}(?P<block>(.|\n)*)}',
                    config[param_start:pos + 1],
                )
                block = re_block.groupdict()

                if block['param_name'].lower() == 'location':
                    new_block = Location(block['param_options'])
                    parent_block.add_location(new_block)
                else:
                    new_block = Block()
                    parent_block[block['param_name']] = new_block

                if block['block']:
                    parse(block['block'], new_block)

                config = config[re_block.end():]
                pos, param_start = 0, 0
                continue

        pos += 1

    if brackets_level != 0:
        raise Exception('Not closed bracket')



qwe = EmptyBlock()

parse("""#{ asd #qweqeqwe{}
servername qweqweqweqweqwe;    # comment {lalalal} #1

server {
    listen 
    8080
    tls;
    root /data/up1;

    location / {
        l200;
    }

    location /qwe{
        s 500;
    }#123
}#qweqwe""", qwe)

print(qwe.render())


qwe = EmptyBlock()
parse(""" servername wqeqweqwe;
http {
    ##
    # Basic Settings
    ##

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    # server_tokens off;

    # server_names_hash_bucket_size 64;
    # server_name_in_redirect off;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    ##
    # Logging Settings
    ##

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    ##
    # Gzip Settings
    ##

    gzip on;
    gzip_disable "msie6";
}#123123
""", qwe)

print(qwe.render())

[((66, 6, 66, 18), 'blocks.EmptyBlock', 'EmptyBlock', ({}, {}), '()', False, 'from blocks import Block, EmptyBlock, KeyValueOption, Comment, Location\n'), ((89, 6, 89, 18), 'blocks.EmptyBlock', 'EmptyBlock', ({}, {}), '()', False, 'from blocks import Block, EmptyBlock, KeyValueOption, Comment, Location\n'), ((9, 13, 9, 25), 'copy.copy', 'copy.copy', ({(9, 23, 9, 24): 's'}, {}), '(s)', False, 'import copy\n'), ((14, 31, 14, 94), 're.search', 're.search', ({(14, 41, 14, 79): '"""(?P<offset>[\\\\s\n]*)#(?P<comment>.*)$"""', (14, 81, 14, 87): 'config', (14, 89, 14, 93): 're.M'}, {}), '("""(?P<offset>[\\\\s\n]*)#(?P<comment>.*)$""", config, re.M)', False, 'import re\n'), ((22, 24, 22, 113), 're.search', 're.search', ({(22, 34, 22, 84): '"""\\\\s*(?P<param_name>\\\\w+)\\\\s*(?P<param_options>.*?);"""', (22, 86, 22, 106): 'config[param_start:]', (22, 108, 22, 112): 're.S'}, {}), "('\\\\s*(?P<param_name>\\\\w+)\\\\s*(?P<param_options>.*?);', config[\n    param_start:], re.S)", False, 'import re\n'), ((16, 37, 16, 95), 'blocks.Comment', 'Comment', ({(16, 45, 16, 68): "sharp_comment['offset']", (16, 70, 16, 94): "sharp_comment['comment']"}, {}), "(sharp_comment['offset'], sharp_comment['comment'])", False, 'from blocks import Block, EmptyBlock, KeyValueOption, Comment, Location\n'), ((27, 64, 27, 110), 're.sub', 're.sub', ({(27, 71, 27, 79): '"""[ \n]+"""', (27, 81, 27, 84): '""" """', (27, 86, 27, 109): "option['param_options']"}, {}), "('[ \\n]+', ' ', option['param_options'])", False, 'import re\n'), ((39, 27, 42, 17), 're.search', 're.search', ({(40, 20, 40, 97): '"""(?P<param_name>\\\\w+)\\\\s*(?P<param_options>.*)\\\\s*{(\n){0,1}(?P<block>(.|\n)*)}"""', (41, 20, 41, 47): 'config[param_start:pos + 1]'}, {}), '(\n    """(?P<param_name>\\\\w+)\\\\s*(?P<param_options>.*)\\\\s*{(\n){0,1}(?P<block>(.|\n)*)}"""\n    , config[param_start:pos + 1])', False, 'import re\n'), ((46, 32, 46, 64), 'blocks.Location', 'Location', ({(46, 41, 46, 63): "block['param_options']"}, {}), "(block['param_options'])", False, 'from blocks import Block, EmptyBlock, KeyValueOption, Comment, Location\n'), ((49, 32, 49, 39), 'blocks.Block', 'Block', ({}, {}), '()', False, 'from blocks import Block, EmptyBlock, KeyValueOption, Comment, Location\n')]