import sys
import json
import shared

TEMPLATE = """
// generated code! be nice
#include "sys/sys.h"

sys_maptile map_{{map_name}}_data[{{width * height}}] = { {{ tiles|join(",") }} };
sys_map map_{{map_name}} = {
    .tiles=map_{{map_name}}_data,
    .width={{width}},
    .height={{height}},
};

{% for entity_type in entity_types %}
void map_{{map_name}}_{{entity_type}}_create(sys_i32 x, sys_i32 y);
{% endfor %}

void map_{{map_name}}_create_entities() {
    {% for entity in entities %}
    map_{{map_name}}_{{entity.type}}_create({{entity.x}}, {{entity.y}});
    {% endfor %}
}
""".lstrip()

def main(map_name, fname_ldtk, fname_c):
    width, height, tiles, entities = load_mapdata(fname_ldtk)

    with open(fname_c, "wt") as output:
        output.write(
            shared.templates.from_string(TEMPLATE).render(
                map_name=map_name,
                tiles=tiles,
                width=width,
                height=height,
                entity_types=sorted(set(i["type"] for i in entities)),
                entities=entities,
            )
        )


def load_mapdata(fname_ldtk):
    sparse_tiles = {}
    entities = []

    with open(fname_ldtk, "rt") as f:
        data = json.load(f)
        for level in data["levels"]:
            level_x = level["worldX"] // 8
            level_y = level["worldY"] // 8
            for layer in level["layerInstances"]:
                if layer["__identifier"] == "conceptual":
                    # for right now I use the vague layer to assign tiles
                    for tile in layer["autoLayerTiles"]:
                        x, y = tile["px"]
                        x //= 8
                        y //= 8
                        ix = tile["t"]
                        sparse_tiles[level_x + x, level_y + y] = ix
                
                if layer["__identifier"] == "entities":
                    for e in layer["entityInstances"]:
                        # TODO: Other fields?
                        entities.append({
                            "type": e["__identifier"],
                            "x": e["__worldX"] // 8,
                            "y": e["__worldY"] // 8,
                        })
    
    x_min = 0
    y_min = 0
    assert not any(x for (x, _) in sparse_tiles if x < x_min), "level can't be left of (0, 0)"
    assert not any(y for (_, y) in sparse_tiles if y < y_min), "level can't be up from (0, 0)"
    width = max(x for (x, _) in sparse_tiles) + 1
    height = max(y for (_, y) in sparse_tiles) + 1
    dense_tiles = []
    for y in range(height):
        for x in range(width):
            k = (x, y)
            if k in sparse_tiles:
                dense_tiles.append(sparse_tiles[k])
            else:
                dense_tiles.append(255)

    return width, height, dense_tiles, entities


def annot_xy(lst, w, h):
    assert len(lst) == w * h
    for y in range(h):
        for x in range(w):
            yield x, y, lst[y * w + x]


if __name__ == "__main__":
    assert len(sys.argv) == 4, \
        "there must be three args (map name, src ldtk, out c)"
    main(sys.argv[1], sys.argv[2], sys.argv[3])