"""Rough delimiter-balance check for Dart sources.

A stand-in for `flutter analyze` when the SDK is unavailable: it strips
comments and string literals (handling interpolation) and verifies that
braces, parentheses and brackets balance in every file.
"""
import io
import os
import sys

BACKSLASH = chr(92)
SINGLE = chr(39)
DOUBLE = chr(34)


def strip_code(src: str) -> str:
    out = []
    i = 0
    n = len(src)
    while i < n:
        c = src[i]
        if c == '/' and i + 1 < n and src[i + 1] == '/':
            while i < n and src[i] != '\n':
                i += 1
        elif c == '/' and i + 1 < n and src[i + 1] == '*':
            i += 2
            while i + 1 < n and not (src[i] == '*' and src[i + 1] == '/'):
                i += 1
            i += 2
        elif c in (SINGLE, DOUBLE):
            triple = src[i:i + 3]
            if triple in (SINGLE * 3, DOUBLE * 3):
                quote = triple
                i += 3
                while i < n and src[i:i + 3] != quote:
                    if src[i] == BACKSLASH:
                        i += 1
                    i += 1
                i += 3
            else:
                quote = c
                i += 1
                depth = 0
                while i < n:
                    if src[i] == BACKSLASH:
                        i += 2
                        continue
                    # String interpolation can contain real code, so keep it.
                    if src[i] == '$' and i + 1 < n and src[i + 1] == '{':
                        depth += 1
                        out.append('{')
                        i += 2
                        continue
                    if depth and src[i] == '}':
                        depth -= 1
                        out.append('}')
                        i += 1
                        continue
                    if depth:
                        out.append(src[i])
                        i += 1
                        continue
                    if src[i] == quote:
                        i += 1
                        break
                    i += 1
        else:
            out.append(c)
            i += 1
    return ''.join(out)


def main() -> int:
    problems = []
    for root, _, files in os.walk('lib'):
        for name in files:
            if not name.endswith('.dart'):
                continue
            path = os.path.join(root, name)
            code = strip_code(io.open(path, encoding='utf-8').read())
            for opener, closer, label in (('{', '}', 'braces'),
                                          ('(', ')', 'parens'),
                                          ('[', ']', 'brackets')):
                diff = code.count(opener) - code.count(closer)
                if diff:
                    problems.append('{}: {} unbalanced by {}'.format(path, label, diff))

    if problems:
        print('\n'.join(problems))
        return 1

    print('All Dart files have balanced delimiters')
    return 0


if __name__ == '__main__':
    sys.exit(main())
