#!/usr/bin/env python
# vim: set fileencoding=utf-8 :

import collections
import json
import logging
import os

from bob.bio.base.database.utils import search_and_open

logger = logging.getLogger(__name__)

_idiap_annotations = {
    1: "reyeo",
    2: "reyet",
    3: "reyep",
    4: "reyeb",
    5: "reyei",
    6: "leyei",
    7: "leyet",
    8: "leyep",
    9: "leyeb",
    10: "leyeo",
    11: "rbrowo",
    12: "rbrowi",
    13: "lbrowi",
    14: "lbrowo",
    15: "noser",
    16: "noset",
    17: "nosel",
    18: "mouthr",
    19: "moutht",
    20: "mouthb",
    21: "mouthl",
    22: "chin",
}


def read_annotation_file(file_name, annotation_type):
    """This function provides default functionality to read annotation files.

    Parameters
    ----------
    file_name : str
        The full path of the annotation file to read. The path can also be like
        ``base_path:relative_path`` where the base_path can be both a directory or
        a tarball. This allows you to read annotations from inside a tarball.
    annotation_type : str
        The type of the annotation file that should be read. The following
        annotation_types are supported:

          * ``eyecenter``: The file contains a single row with four entries:
            ``re_x re_y le_x le_y``
          * ``named``: The file contains named annotations, one per line, e.g.:
            ``reye re_x re_y`` or ``pose 25.7``
          * ``idiap``: The file contains enumerated annotations, one per line,
            e.g.: ``1 key1_x key1_y``, and maybe some additional annotations like
            gender, age, ...
          * ``json``: The file contains annotations of any format, dumped in a
            text json file.

    Returns
    -------
    dict
        A python dictionary with the keypoint name as key and the
        position ``(y,x)`` as value, and maybe some additional annotations.

    Raises
    ------
    IOError
        If the annotation file is not found.
    ValueError
        If the annotation type is not known.
    """
    if not file_name:
        return None

    if ":" in file_name:
        base_path, tail = file_name.split(":", maxsplit=1)
        f = search_and_open(search_pattern=tail, base_dir=base_path)
    else:
        if not os.path.exists(file_name):
            raise IOError("The annotation file '%s' was not found" % file_name)
        f = open(file_name)

    annotations = {}

    try:
        if str(annotation_type) == "eyecenter":
            # only the eye positions are written, all are in the first row
            line = f.readline()
            positions = line.split()
            assert len(positions) == 4
            annotations["reye"] = (float(positions[1]), float(positions[0]))
            annotations["leye"] = (float(positions[3]), float(positions[2]))

        elif str(annotation_type) == "named":
            # multiple lines, no header line, each line contains annotation and
            # position or single value annotation
            for line in f:
                positions = line.split()
                if len(positions) == 3:
                    annotations[positions[0]] = (
                        float(positions[2]),
                        float(positions[1]),
                    )
                elif len(positions) == 2:
                    annotations[positions[0]] = float(positions[1])
                else:
                    logger.error(
                        "Could not interpret line '%s' in annotation file '%s'",
                        line,
                        file_name,
                    )

        elif str(annotation_type) == "idiap":
            # Idiap format: multiple lines, no header, each line contains an integral
            # keypoint identifier, or other identifier like 'gender', 'age',...
            for line in f:
                positions = line.rstrip().split()
                if positions:
                    if positions[0].isdigit():
                        # position field
                        assert len(positions) == 3
                        id = int(positions[0])
                        annotations[_idiap_annotations[id]] = (
                            float(positions[2]),
                            float(positions[1]),
                        )
                    else:
                        # another field, we take the first entry as key and the rest as
                        # values
                        annotations[positions[0]] = positions[1:]
            # finally, we add the eye center coordinates as the center between the
            # eye corners; the annotations 3 and 8 are the pupils...
            if "reyeo" in annotations and "reyei" in annotations:
                annotations["reye"] = (
                    (annotations["reyeo"][0] + annotations["reyei"][0]) / 2.0,
                    (annotations["reyeo"][1] + annotations["reyei"][1]) / 2.0,
                )
            if "leyeo" in annotations and "leyei" in annotations:
                annotations["leye"] = (
                    (annotations["leyeo"][0] + annotations["leyei"][0]) / 2.0,
                    (annotations["leyeo"][1] + annotations["leyei"][1]) / 2.0,
                )

        elif str(annotation_type) == "json":
            annotations = json.load(
                f, object_pairs_hook=collections.OrderedDict
            )
        else:
            raise ValueError(
                "The given annotation type '%s' is not known, choose one of ('eyecenter', 'named', 'idiap')"
                % annotation_type
            )
    finally:
        f.close()

    if (
        annotations is not None
        and "leye" in annotations
        and "reye" in annotations
        and annotations["leye"][1] < annotations["reye"][1]
    ):
        logger.warn(
            "The eye annotations in file '%s' might be exchanged!" % file_name
        )

    return annotations
