import sys
from dataclasses import dataclass
from os.path import splitext

from csv_input import read_input
from generate import renderer

from pprint import pprint

def parse_bp(s):
    if ' to ' in s:
        start0, end0 = s.split(' to ')
        if '(' in end0 and ')' in end0 and 'LEN_' in end0:
            end0 = int(start0) - 1
            start, end = int(start0), int(end0)
            return start, end
        else:
            start, end = int(start0), int(end0)
            assert start <= end, (start, end)
            return start, end
    else:
        start = int(s)
        return start, start

def bp_range(start, end):
    return set(range(start, end+1))

reserved = 0
def sanitize_field_name(name):
    global reserved
    if name == "(Reserved for future standardization)" or name == "Unused field":
        reserved += 1
        return f"_res{reserved}";
    if '(' in name:
        assert 'LEN_' in name, name
        name = name.split('(')[0].strip()

    name = name.lower().replace(' ', '_')
    return name

def sanitize_content_name(name):
    if name == 'Numerical value':
        return 'numerical_value'
    else:
        return 'bytes'

@dataclass
class Field:
    start: int
    end: int
    name: str
    content: str

def parse(rows):
    seen_bps = set()
    seen_names = set()

    for row in rows:
        start, end = parse_bp(row['BP'])
        _range = bp_range(start, end)
        assert seen_bps.intersection(_range) == set(), row
        seen_bps = seen_bps.union(_range)
        field_name = sanitize_field_name(row["Field name"])
        assert field_name not in seen_names
        seen_names.add(field_name)
        content_name = sanitize_content_name(row["Content"])

        yield Field(
            start=start,
            end=end,
            name=field_name,
            content=content_name
        )

type_dict = {
    1: 'uint8_t',
    2: 'uint16_t',
    4: 'uint16_le_be',
    8: 'uint32_le_be',
}

def header():
    yield "#pragma once"
    yield ""
    yield "#include <cstdint>"
    yield "#include <cstddef>"
    yield ""
    yield '#include "uint_le_be.hpp"'
    yield ""

def render_fields(input_name, fields):
    yield f"struct {input_name} {{"
    for field in fields:
        field_size = (field.end - field.start) + 1
        if field.content == 'numerical_value':
            assert field_size in type_dict, field
            type = type_dict[field_size]
            yield f"const {type} {field.name};"
        else:
            if field_size == 1:
                yield f"const uint8_t {field.name};"
            elif field_size == 0:
                yield f"const uint8_t {field.name}[];"
            else:
                yield f"const uint8_t {field.name}[{field_size}];"
    yield "};"

    for field in fields:
        yield f"static_assert((offsetof (struct {input_name}, {field.name})) == {field.start - 1});"

if __name__ == "__main__":
    input_file = sys.argv[1]
    input_name, _ = splitext(input_file)
    rows = read_input(input_file)
    fields = list(parse(rows))
    render, out = renderer()
    render(header())
    render(render_fields(input_name, fields))
    sys.stdout.write(out.getvalue())
