from typing import Mapping
from typing import MutableMapping
from typing import Iterable
from typing import MutableSequence


def to_rec_avro_destructive(o, types_to_str=()):
    """
    Converts a python nested data structure and returns a data structure
    in rec_avro format. The input data structure is destroyed and reused.

    rec_avro format is conforming to the avro schema generated by rec_avro_schema()

    For fastavro::

        fastavro.writer(
            out_stream,
            schema.parse_schema(rec_schema),
            (to_rec_avro_destructive(record) for record in input_records))

    See also::

        rec_avro_schema()
        from_rec_avro_destructive()

    :param o: a data structure in rec_avro format.
    :param types_to_str: values of these types will be converted to str
        in the output data structure.
        Note that this is irreversible, i.e. they will be read back as strings.
    :return: python data structure in rec_avro format
    """

    if isinstance(o, str):
        return o

    if isinstance(o, types_to_str) and types_to_str:
        return str(o)

    if isinstance(o, Mapping):
        if isinstance(o, MutableMapping):
            for k, v in o.items():
                o[k] = to_rec_avro_destructive(v, types_to_str)
            return {'_': o}
        else:
            return {'_': {
                k: to_rec_avro_destructive(v, types_to_str)
                for k, v in o.items()}
            }

    if isinstance(o, Iterable):
        if isinstance(o, MutableSequence):
            for i in range(len(o)):
                o[i] = to_rec_avro_destructive(o[i], types_to_str)
            return {'_': o}
        else:
            return {'_': [to_rec_avro_destructive(i, types_to_str) for i in o]}

    return o