# Path: fastavro/repository/base.py
# class SchemaRepositoryError(Exception):
#     pass
# 
# Path: fastavro/repository/flat_dict.py
# class FlatDictRepository(AbstractSchemaRepository):
#     def __init__(self, path):
#         self.path = path
#         self.file_ext = "avsc"
# 
#     def load(self, name):
#         file_path = path.join(self.path, f"{name}.{self.file_ext}")
#         try:
#             with open(file_path) as schema_file:
#                 return json.load(schema_file)
#         except IOError as error:
#             raise SchemaRepositoryError(
#                 f"Failed to load '{name}' schema",
#             ) from error
#         except json.decoder.JSONDecodeError as error:
#             raise SchemaRepositoryError(
#                 f"Failed to parse '{name}' schema",
#             ) from error
# 
# Path: fastavro/_schema_common.py
# PRIMITIVES = {
#     "boolean",
#     "bytes",
#     "double",
#     "float",
#     "int",
#     "long",
#     "null",
#     "string",
# }
# 
# class UnknownType(ValueError):
#     def __init__(self, name):
#         super().__init__(name)
#         self.name = name
# 
# class SchemaParseException(Exception):
#     pass
# 
# RESERVED_PROPERTIES = {
#     "type",
#     "name",
#     "namespace",
#     "fields",  # Record
#     "items",  # Array
#     "size",  # Fixed
#     "symbols",  # Enum
#     "values",  # Map
#     "doc",
# }
# 
# OPTIONAL_FIELD_PROPERTIES = {
#     "doc",
#     "aliases",
#     "default",
# }
# 
# RESERVED_FIELD_PROPERTIES = {"type", "name"} | OPTIONAL_FIELD_PROPERTIES
# 
# JAVA_FINGERPRINT_MAPPING = {"SHA-256": "sha256", "MD5": "md5"}
# 
# FINGERPRINT_ALGORITHMS = (
#     hashlib.algorithms_guaranteed | JAVA_FINGERPRINT_MAPPING.keys() | {RABIN_64}
# )
# 
# RABIN_64 = "CRC-64-AVRO"
# 
# def rabin_fingerprint(data):
#     empty_64 = 0xC15D213AA4D7A795
# 
#     fp_table = []
#     for i in range(256):
#         fp = i
#         for j in range(8):
#             mask = -(fp & 1)
#             fp = (fp >> 1) ^ (empty_64 & mask)
#         fp_table.append(fp)
# 
#     result = empty_64
#     for byte in data:
#         result = (result >> 8) ^ fp_table[(result ^ byte) & 0xFF]
# 
#     # Although not mentioned in the Avro specification, the Java
#     # implementation gives fingerprint bytes in little-endian order
#     return result.to_bytes(length=8, byteorder="little", signed=False).hex()


import hashlib
import math
import re
from io import StringIO
from os import path
from copy import deepcopy
from .repository import FlatDictRepository, SchemaRepositoryError
from ._schema_common import (
    PRIMITIVES,
    UnknownType,
    SchemaParseException,
    RESERVED_PROPERTIES,
    OPTIONAL_FIELD_PROPERTIES,
    RESERVED_FIELD_PROPERTIES,
    JAVA_FINGERPRINT_MAPPING,
    FINGERPRINT_ALGORITHMS,
    RABIN_64,
    rabin_fingerprint,
)
            size = schema["size"]
            fo.write(f'{{"name":"{name}","type":"{schema_type}","size":{size}}}')

        elif schema_type == "record" or schema_type == "error":
            name = schema["name"]
            fo.write(f'{{"name":"{name}","type":"record","fields":[')

            for idx, field in enumerate(schema["fields"]):
                if idx != 0:
                    fo.write(",")
                name = field["name"]
                fo.write(f'{{"name":"{name}","type":')
                _to_parsing_canonical_form(field["type"], fo)
                fo.write("}")
            fo.write("]}")

        elif schema_type in PRIMITIVES:
            fo.write(f'"{schema_type}"')


def fingerprint(parsing_canonical_form, algorithm):
    """Returns a string represening a fingerprint/hash of the parsing canonical
    form of a schema.

    For more details on the fingerprint, see here:
    https://avro.apache.org/docs/current/spec.html#schema_fingerprints
    """
    if algorithm not in FINGERPRINT_ALGORITHMS:
        raise ValueError(
            f"Unknown schema fingerprint algorithm {algorithm}. "

# Path: fastavro/repository/base.py
# class SchemaRepositoryError(Exception):
#     pass
# 
# Path: fastavro/repository/flat_dict.py
# class FlatDictRepository(AbstractSchemaRepository):
#     def __init__(self, path):
#         self.path = path
#         self.file_ext = "avsc"
# 
#     def load(self, name):
#         file_path = path.join(self.path, f"{name}.{self.file_ext}")
#         try:
#             with open(file_path) as schema_file:
#                 return json.load(schema_file)
#         except IOError as error:
#             raise SchemaRepositoryError(
#                 f"Failed to load '{name}' schema",
#             ) from error
#         except json.decoder.JSONDecodeError as error:
#             raise SchemaRepositoryError(
#                 f"Failed to parse '{name}' schema",
#             ) from error
# 
# Path: fastavro/_schema_common.py
# PRIMITIVES = {
#     "boolean",
#     "bytes",
#     "double",
#     "float",
#     "int",
#     "long",
#     "null",
#     "string",
# }
# 
# class UnknownType(ValueError):
#     def __init__(self, name):
#         super().__init__(name)
#         self.name = name
# 
# class SchemaParseException(Exception):
#     pass
# 
# RESERVED_PROPERTIES = {
#     "type",
#     "name",
#     "namespace",
#     "fields",  # Record
#     "items",  # Array
#     "size",  # Fixed
#     "symbols",  # Enum
#     "values",  # Map
#     "doc",
# }
# 
# OPTIONAL_FIELD_PROPERTIES = {
#     "doc",
#     "aliases",
#     "default",
# }
# 
# RESERVED_FIELD_PROPERTIES = {"type", "name"} | OPTIONAL_FIELD_PROPERTIES
# 
# JAVA_FINGERPRINT_MAPPING = {"SHA-256": "sha256", "MD5": "md5"}
# 
# FINGERPRINT_ALGORITHMS = (
#     hashlib.algorithms_guaranteed | JAVA_FINGERPRINT_MAPPING.keys() | {RABIN_64}
# )
# 
# RABIN_64 = "CRC-64-AVRO"
# 
# def rabin_fingerprint(data):
#     empty_64 = 0xC15D213AA4D7A795
# 
#     fp_table = []
#     for i in range(256):
#         fp = i
#         for j in range(8):
#             mask = -(fp & 1)
#             fp = (fp >> 1) ^ (empty_64 & mask)
#         fp_table.append(fp)
# 
#     result = empty_64
#     for byte in data:
#         result = (result >> 8) ^ fp_table[(result ^ byte) & 0xFF]
# 
#     # Although not mentioned in the Avro specification, the Java
#     # implementation gives fingerprint bytes in little-endian order
#     return result.to_bytes(length=8, byteorder="little", signed=False).hex()


# Path: fastavro/_schema_py.py
import hashlib
import math
import re
from io import StringIO
from os import path
from copy import deepcopy
from .repository import FlatDictRepository, SchemaRepositoryError
from ._schema_common import (
    PRIMITIVES,
    UnknownType,
    SchemaParseException,
    RESERVED_PROPERTIES,
    OPTIONAL_FIELD_PROPERTIES,
    RESERVED_FIELD_PROPERTIES,
    JAVA_FINGERPRINT_MAPPING,
    FINGERPRINT_ALGORITHMS,
    RABIN_64,
    rabin_fingerprint,
)

            size = schema["size"]
            fo.write(f'{{"name":"{name}","type":"{schema_type}","size":{size}}}')

        elif schema_type == "record" or schema_type == "error":
            name = schema["name"]
            fo.write(f'{{"name":"{name}","type":"record","fields":[')

            for idx, field in enumerate(schema["fields"]):
                if idx != 0:
                    fo.write(",")
                name = field["name"]
                fo.write(f'{{"name":"{name}","type":')
                _to_parsing_canonical_form(field["type"], fo)
                fo.write("}")
            fo.write("]}")

        elif schema_type in PRIMITIVES:
            fo.write(f'"{schema_type}"')


def fingerprint(parsing_canonical_form, algorithm):
    """Returns a string represening a fingerprint/hash of the parsing canonical
    form of a schema.

    For more details on the fingerprint, see here:
    https://avro.apache.org/docs/current/spec.html#schema_fingerprints
    """
    if algorithm not in FINGERPRINT_ALGORITHMS:
        raise ValueError(
            f"Unknown schema fingerprint algorithm {algorithm}. "

            + f"Valid values include: {FINGERPRINT_ALGORITHMS}"