#!/usr/bin/python
"""
Utility script that extracts tiles with the same class and
outputs C enum and functions for them (so there is no string checking).
"""

import json
import sys
import os
from collections import defaultdict

types = defaultdict(list)

path = sys.argv[1]
content = open(path).read()

tiles = json.loads(content)["tiles"]

for tile in tiles:
    if "type" not in tile:
        continue
    id = tile["id"]
    type = tile["type"]
    types[type].append(id)

enum_type = os.path.basename(path).split(".")[0].capitalize()
enum_name = enum_type.upper()
if sys.argv[2]:
    enum_type = sys.argv[2]
fun_name = enum_type
if sys.argv[3]:
    fun_name = sys.argv[3]
enum_prefix = enum_name
if sys.argv[4]:
    enum_prefix = sys.argv[4]

indent_level = 0
indent_offset = 4


def indent():
    return ' ' * indent_level


def enum_string(name):
    return f"{enum_prefix}_{name.upper()}"


# ============================
header_guard = f"{enum_type.upper()}_H"
print(f"#ifndef {header_guard}")
print(f"#define {header_guard}")
script_name = os.path.basename(__file__)
print(f"// This file was generated by: {script_name}")
print()
print("#include <breeze.h>")
print("#include <string.h>")
print()
print()

# ============================

enum_none = enum_string("none")
enum_count = enum_string("count")

print(f"{indent()}typedef enum {enum_type} {{")
indent_level += indent_offset
print(f"{indent()}{enum_none},")
for enum in types:
    print(f"{indent()}{enum_string(enum)},")
print(f"{indent()}{enum_count}")
indent_level -= indent_offset
print(f"{indent()}}} {enum_type};")

print()
print()

# ============================

print(f"{indent()}static {enum_type} getTile{fun_name}(BzTile tile) {{")
indent_level += indent_offset
print(f"{indent()}switch (tile) {{")
for enum, ids in types.items():
    indent_level += indent_offset
    for id in ids:
        print(f"{indent()}case {id}:")

    indent_level += indent_offset
    print(f"{indent()} return {enum_string(enum)};")
    indent_level -= indent_offset

    indent_level -= indent_offset

indent_level += indent_offset
print(f"{indent()}default:")
indent_level += indent_offset
print(f"{indent()}return {enum_string('none')};")
indent_level -= indent_offset
indent_level -= indent_offset
print(f"{indent()}}}")
indent_level -= indent_offset
print(f"{indent()}}}")

print()

# ============================

print(f"{indent()}static BzTile get{fun_name}Tile({enum_type} type) {{")
indent_level += indent_offset
print(f"{indent()}switch (type) {{")
indent_level += indent_offset
for type, ids in types.items():
    min_id = min(ids)
    print(f"{indent()}case {enum_string(type)}: return {min_id};")

print(f"{indent()}default: return -1;")
indent_level -= indent_offset
print(f"{indent()}}}")

indent_level -= indent_offset

print(f"{indent()}}}")
print()

# ============================

print(f"{indent()}static {enum_type} get{fun_name}FromStr(const char *str) {{")
indent_level += indent_offset

# trie would be much better
for type in types:
    print(f"{indent()}if (strncmp(\"{type}\", str, {len(type)}) == 0) return {enum_string(type)};")
print(f"{indent()}else return {enum_string('none')};")

indent_level -= indent_offset
print(f"{indent()}}}")

# ============================

print(f"{indent()}static const char *get{fun_name}Str({enum_type} type) {{")
indent_level += indent_offset

print(f"{indent()}switch (type) {{")
indent_level += indent_offset

print(f"{indent()}case {enum_string('none')}: return \"none\";")
for type in types:
    print(f"{indent()}case {enum_string(type)}: return \"{type}\";")
print(f"{indent()}default: return NULL;")
indent_level -= indent_offset
print(f"{indent()}}}")

indent_level -= indent_offset
print(f"{indent()}}}")
print()


# ============================

print(f"{indent()}static void get{fun_name}Size({enum_type} type, BzTile *outWidth, BzTile *outHeight) {{")
indent_level += indent_offset
print(f"{indent()} switch (type) {{")
indent_level += indent_offset
for enum, ids in types.items():
    print(f"{indent()}case {enum_string(enum)}:")
    indent_level += indent_offset
    w = 1
    h = 1
    for curr, next in zip(ids, ids[1:]):
        if next > curr + 1:
            h += 1
        elif h == 1:
            w += 1
    print(f"{indent()}if (outWidth) *outWidth = {w};")
    print(f"{indent()}if (outHeight) *outHeight = {h};")
    print(f"{indent()}break;")
    indent_level -= indent_offset

print(f"{indent()}default:")
indent_level += indent_offset
print(f"{indent()}if (outWidth) *outWidth = 0;")
print(f"{indent()}if (outHeight) *outHeight = 0;")
print(f"{indent()}break;")
indent_level -= indent_offset
indent_level -= indent_offset
print(f"{indent()}}}")


indent_level -= indent_offset
indent_level -= indent_offset

indent_level -= indent_offset
print(f"{indent()}}}")
print()


# ============================

print(f"#endif //{header_guard}")