#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# SPDX-FileCopyrightText: 2014 Denis Steckelmacher <steckdenis@yahoo.fr>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL

from jsgenerator import *

def license():
    # Print the license of the generated file (the same as the one of this file)
    print("""
/*
 * ==== FILE AUTOGENERATED FROM .idl FILES UNDER THE FOLLOWING LICENSE ====
 *
 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
""")

def get_type(decl):
    typename = ' '.join(decl[0:-1])

    if typename == 'void':
        typename = ''
    elif typename == 'any':
        typename = '_mixed'
    elif typename == 'boolean' or typename == 'bool':
        typename = 'true'
    elif typename == 'DOMString':
        typename = "''"
    elif typename == 'DOMObject':
        typename = 'new Object()'
    elif typename == 'DOMTimeStamp':
        typename = 'new Date()'
    elif 'unsigned' in typename or ' short' in typename or ' int' in typename or ' long' in typename or typename in ['short', 'int', 'long']:
        typename = '1'
    elif typename == 'float' or typename == 'double':
        typename = '1.0'
    elif typename == 'Array':
        typename = '[]'
    elif 'Callback' in typename or 'Handler' in typename:
        typename = 'function(){}'
    elif 'Constructor' in typename:
        typename = typename.replace('Constructor', '')
    else:
        typename = 'new %s()' % typename

    return typename

def get_name(decl):
    return decl[-1]

member_decl = []
param_decl = []
params = []
skip_end_of_line = False
in_module = False
in_interface = False
in_inherit = False
in_param = False
cl = None

def parse(token):
    global member_decl, param_decl, params, skip_end_of_line, in_module, in_interface, in_inherit, in_param, cl

    if token in ['in', 'readonly', 'optional', 'attribute', 'getter', 'setter', '{', '}']:
        pass
    elif token == 'raises':
        skip_end_of_line = True
    elif token == 'module':
        in_module = True
    elif token == 'interface':
        in_module = False
        in_interface = True
    elif in_module:
        # Skip the module declaration
        pass
    elif in_interface:
        # Interface name
        cl = Class(token)
        in_interface = False

        if token == 'DOMWindow':
            # Export window, the only thing that should be exposed to the outside world
            print('module.exports = DOMWindow;')

    elif token == ';':
        # End the current declaration
        if len(member_decl) == 0:
            # When the end of a class is reached, an empty declaration is produced
            cl.print()
            return

        if in_param:
            # Declare a method
            cl.member(F(get_type(member_decl), get_name(member_decl), \
                *[(get_name(p), get_type(p)) for p in params]
            ))
        else:
            # Declare a member variable
            if get_type(member_decl) is not None and get_name(member_decl)[0].isalpha():
                cl.member(Var(get_type(member_decl), get_name(member_decl)))

        member_decl.clear()
        params.clear()
        skip_end_of_line = False
        in_param = False
    elif skip_end_of_line:
        # Skip everything until the colon
        pass
    elif token == ':':
        in_inherit = True
    elif in_inherit:
        cl.prototype(token)
        in_inherit = False
    elif token == '(':
        # Begin the parameter list
        in_param = True
    elif token == ')' or token == ',':
        # End of a parameter
        if len(param_decl) != 0:
            params.append(param_decl[:])
            param_decl.clear()

        pass
    elif in_param:
        # Add a token to the parameter declaration
        param_decl.append(token)
    else:
        # Add a token to the member declaration
        member_decl.append(token)

def tokenize(data):
    token = ''
    prev_c = ''
    c = ''
    next_c = ''
    in_comment = 'none'
    bracket_depth = 0

    for i in range(len(data) + 1):
        prev_c = c
        c = next_c

        if i < len(data):
            next_c = data[i]

        # Handle single-line and multi-line comments
        if in_comment == 'singleline':
            if c == '\n':
                in_comment = 'none'
            continue

        if in_comment == 'multiline':
            if prev_c == '*' and c == '/':
                in_comment = 'none'
            continue

        if c == '/' and next_c == '*':
            in_comment = 'multiline'
            continue

        if c == '/' and next_c == '/':
            in_comment = 'singleline'
            continue

        if c == '#':
            # Skip preprocessor macros: consider them as comments
            in_comment = 'singleline'
            continue

        # Skip hints between brackets
        if c == '[':
            bracket_depth += 1
            continue
        elif c == ']':
            bracket_depth -= 1
            continue

        if bracket_depth > 0:
            continue

        # Spaces are used to separate tokens
        if not c.isalnum() or not token.isalnum():
            if token != '':
                parse(token)
                token = ''

            if c.isspace():
                continue

        token += c

def main():
    license()

    for fl in sys.argv[1:]:
        f = open(fl, 'r')
        tokenize(f.read())
        f.close()

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print('Usage: idltojs.py <idl...>')
    else:
        main()
