#!/usr/bin/python3

import json
import sys, re, os

_license_header = """/*
 * Author: XueShuming
 *
 * Copyright (C) 2021 FMSoft <https://www.fmsoft.cn>
 *
 * This file is a part of PurC (short for Purring Cat), an HVML interpreter.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>
 */

 // NOTE
 // This file is auto-generated by using 'make-sbst.py'.
 // Please take care when you modify this file mannually.

"""

def entities_bst(json_filename, name):
    index = entities_bst_load(json_filename)
    bst = entities_bst_create(index);
    result = entities_bst_result(name, bst)
    return ''.join(result).replace('\\\\x', '\\x')

def entities_bst_load(json_filename):
    jdata = json.load(open(json_filename))
    index = {}

    for name in jdata:
        entities_bst_create_layer(name, jdata[name], index)

    return index

def entities_bst_save(json_filename):
    jdata = json.load(open(json_filename))
    index = {}

    for name in jdata:
        entities_bst_create_layer(name, jdata[name], index)

    return index

def entities_bst_create_layer(name, entry, index):
    chars = list(name)
    last = len(chars)

    for pos in range(1, last):
        char = chars[pos]

        if char not in index:
            index[char] = {'next': {}, 'values': []}

        if pos == (last - 1):
            index[char]['values'].append(entry);

        index = index[char]['next']

def entities_bst_create(index):
    bst = {}
    bst[0] = ["\0", 0, 0, 0, "NULL"]

    begin = 1
    idx = end = entities_bst_create_tree(index, bst, begin)

    stack = []
    while True:
        while begin < end:
            if len(bst[begin][5]) != 0:
                index = bst[begin][5]

                bst[begin][5] = 0
                bst[begin][3] = idx

                stack.append([begin + 1, end])

                begin = idx
                end = idx = entities_bst_create_tree(index, bst, idx)

            else:
                begin = begin + 1

        if len(stack) == 0:
            break

        entry = stack.pop();
        begin = entry[0]
        end = entry[1]

    return bst

def entities_bst_create_tree(index, bst, idx):
    keys = list(index.keys())
    keys.sort()

    optv = []
    inr = -1

    while len(keys):
        split = entities_bst_split(keys);

        idx_left = idx + 2 if len(split[1]) != 0 else 0
        idx_right = idx + 1 if len(split[2]) != 0 else 0

        if idx_right == 0 and idx_left != 0:
            idx_left = idx_left - 1

        inr = bst[inr][2] if inr in bst else idx

        assert len(index[ split[0] ]['values']) < 2, 'Double values'

        if len(index[ split[0] ]['values']) == 0:
            value = "NULL"
        else:
            value = '(char*)"{}"'.format(toHex(index[ split[0] ]['values'][0]['characters']))

        #           key     , left    , right    , next, value,
        bst[inr] = [split[0], idx_left, idx_right,    0, value,
                    index[ split[0] ]['next'] ]

        if len(split[2]) != 0:
            optv.append([split[2], inr])

        if idx_left:
            idx = idx_left

        inr = -1
        keys = split[1]

        if len(keys) == 0:
            if len(optv) == 0:
                break

            entry = optv.pop()
            keys = entry[0];
            inr = entry[1];

    return idx + 1


def toHex(s):
    lst = []

    for ch in bytes(s, 'utf-8'):
        hv = hex(ch).replace('0x', '\\\\x')    # python 3.8
        lst.append(hv)

    return ''.join(lst)


def entities_bst_split(keys):
    srt = int(len(keys) / 2)
    return [keys[srt], keys[0:srt], keys[srt + 1:len(keys)]]


def entities_bst_result(name, bst):
    entries = list(bst.keys());
    entries.sort()

    result = []
    last_idx = entries.pop()

    result.append(_license_header)
    result.append('\n')
    result.append("static const sbst_entry_static_t {}[] =\n".format(name))
    result.append("{\n    ")

    for idx in entries:
        result.append("{{0x{0:02x}, {1}, {2}, {3}, {4}, {5}}}"
                      .format(ord(bst[idx][0]), bst[idx][4], bst[idx][4].count('\\x'),
                              bst[idx][1], bst[idx][2],
                              bst[idx][3]));
        result.append(",\n    ")

    result.append("{{0x{0:02x}, {1}, {2}, {3}, {4}, {5}}}\n"
                  .format(ord(bst[last_idx][0]), bst[last_idx][4],
                          bst[last_idx][4].count('\\x'),
                          bst[last_idx][1], bst[last_idx][2],
                          bst[last_idx][3]));

    result.append("};\n")

    return result


def entities_bst_print(bst):
    entries = list(bst.keys());
    entries.sort()

    result = []

    for idx in entries:
        print("{}: {{'{}', {}, {}, {}, {}, {}}}"
              .format(idx, bst[idx][0], bst[idx][4], bst[idx][4].count('\\x'),
                      bst[idx][1], bst[idx][2], bst[idx][3]));

    return result


if __name__ == "__main__":

    if len(sys.argv) < 2:
        raise Exception('expecting output_dir input.json')

    first_arg = True
    second_arg = False
    for parameter in sys.argv:
        if first_arg:
            first_arg = False
            second_arg = True
            continue
        if second_arg:
            base_dir = parameter
            second_arg = False
            continue

        fname = os.path.splitext(parameter)[0]
        dst_file = '%s/%s_sbst.inc' % (base_dir, fname)
        with open(dst_file, "w+") as output_file:
            output_file.write(entities_bst(parameter, fname))

    print("Done.")
