# Copyright 2015 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Schemas for BigQuery tables / queries."""


class SchemaField(object):
    """Describe a single field within a table schema.

    :type name: str
    :param name: the name of the field.

    :type field_type: str
    :param field_type: the type of the field (one of 'STRING', 'INTEGER',
                       'FLOAT', 'BOOLEAN', 'TIMESTAMP' or 'RECORD').

    :type mode: str
    :param mode: the mode of the field (one of 'NULLABLE', 'REQUIRED',
                 or 'REPEATED').

    :type description: str
    :param description: optional description for the field.

    :type fields: tuple of :class:`SchemaField`
    :param fields: subfields (requires ``field_type`` of 'RECORD').
    """
    def __init__(self, name, field_type, mode='NULLABLE',
                 description=None, fields=()):
        self._name = name
        self._field_type = field_type
        self._mode = mode
        self._description = description
        self._fields = tuple(fields)

    @property
    def name(self):
        """str: The name of the field."""
        return self._name

    @property
    def field_type(self):
        """str: The type of the field.

        Will be one of 'STRING', 'INTEGER', 'FLOAT', 'BOOLEAN',
        'TIMESTAMP' or 'RECORD'.
        """
        return self._field_type

    @property
    def mode(self):
        """str: The mode of the field.

        Will be one of 'NULLABLE', 'REQUIRED', or 'REPEATED'.
        """
        return self._mode

    @property
    def is_nullable(self):
        """Check whether 'mode' is 'nullable'."""
        return self._mode == 'NULLABLE'

    @property
    def description(self):
        """Optional[str]: Description for the field."""
        return self._description

    @property
    def fields(self):
        """tuple: Subfields contained in this field.

        If ``field_type`` is not 'RECORD', this property must be
        empty / unset.
        """
        return self._fields

    def _key(self):
        """A tuple key that unique-ly describes this field.

        Used to compute this instance's hashcode and evaluate equality.

        Returns:
            tuple: The contents of this :class:`SchemaField`.
        """
        return (
            self._name,
            self._field_type.lower(),
            self._mode,
            self._description,
            self._fields,
        )

    def __eq__(self, other):
        if isinstance(other, SchemaField):
            return self._key() == other._key()
        else:
            return NotImplemented

    def __ne__(self, other):
        if isinstance(other, SchemaField):
            return self._key() != other._key()
        else:
            return NotImplemented

    def __hash__(self):
        return hash(self._key())

    def __repr__(self):
        return 'SchemaField{}'.format(self._key())
