"""
NetCDF reader/writer module.

This module is used to read and create NetCDF files. NetCDF files are
accessed through the `netcdf_file` object. Data written to and from NetCDF
files are contained in `netcdf_variable` objects. Attributes are given
as member variables of the `netcdf_file` and `netcdf_variable` objects.

This module implements the Scientific.IO.NetCDF API to read and create
NetCDF files. The same API is also used in the PyNIO and pynetcdf
modules, allowing these modules to be used interchangeably when working
with NetCDF files.

Only NetCDF3 is supported here; for NetCDF4 see
`netCDF4-python <http://unidata.github.io/netcdf4-python/>`__,
which has a similar API.

"""

from __future__ import division, print_function, absolute_import

# TODO:
# * properly implement ``_FillValue``.
# * fix character variables.
# * implement PAGESIZE for Python 2.6?

# The Scientific.IO.NetCDF API allows attributes to be added directly to
# instances of ``netcdf_file`` and ``netcdf_variable``. To differentiate
# between user-set attributes and instance attributes, user-set attributes
# are automatically stored in the ``_attributes`` attribute by overloading
#``__setattr__``. This is the reason why the code sometimes uses
#``obj.__dict__['key'] = value``, instead of simply ``obj.key = value``;
# otherwise the key would be inserted into userspace attributes.


__all__ = ['netcdf_file', 'netcdf_variable']


import sys
import warnings
import weakref
from operator import mul
from collections import OrderedDict

import mmap as mm

import numpy as np
from numpy.compat import asbytes, asstr
from numpy import frombuffer, dtype, empty, array, asarray
from numpy import little_endian as LITTLE_ENDIAN
from functools import reduce

from scipy._lib.six import integer_types, text_type, binary_type

IS_PYPY = ('__pypy__' in sys.modules)

ABSENT = b'\x00\x00\x00\x00\x00\x00\x00\x00'
ZERO = b'\x00\x00\x00\x00'
NC_BYTE = b'\x00\x00\x00\x01'
NC_CHAR = b'\x00\x00\x00\x02'
NC_SHORT = b'\x00\x00\x00\x03'
NC_INT = b'\x00\x00\x00\x04'
NC_FLOAT = b'\x00\x00\x00\x05'
NC_DOUBLE = b'\x00\x00\x00\x06'
NC_DIMENSION = b'\x00\x00\x00\n'
NC_VARIABLE = b'\x00\x00\x00\x0b'
NC_ATTRIBUTE = b'\x00\x00\x00\x0c'
FILL_BYTE = b'\x81'
FILL_CHAR = b'\x00'
FILL_SHORT = b'\x80\x01'
FILL_INT = b'\x80\x00\x00\x01'
FILL_FLOAT = b'\x7C\xF0\x00\x00'
FILL_DOUBLE = b'\x47\x9E\x00\x00\x00\x00\x00\x00'

TYPEMAP = {NC_BYTE: ('b', 1),
           NC_CHAR: ('c', 1),
           NC_SHORT: ('h', 2),
           NC_INT: ('i', 4),
           NC_FLOAT: ('f', 4),
           NC_DOUBLE: ('d', 8)}

FILLMAP = {NC_BYTE: FILL_BYTE,
           NC_CHAR: FILL_CHAR,
           NC_SHORT: FILL_SHORT,
           NC_INT: FILL_INT,
           NC_FLOAT: FILL_FLOAT,
           NC_DOUBLE: FILL_DOUBLE}

REVERSE = {('b', 1): NC_BYTE,
           ('B', 1): NC_CHAR,
           ('c', 1): NC_CHAR,
           ('h', 2): NC_SHORT,
           ('i', 4): NC_INT,
           ('f', 4): NC_FLOAT,
           ('d', 8): NC_DOUBLE,

           # these come from asarray(1).dtype.char and asarray('foo').dtype.char,
           # used when getting the types from generic attributes.
           ('l', 4): NC_INT,
           ('S', 1): NC_CHAR}


class netcdf_file(object):
    """
    A file object for NetCDF data.

    A `netcdf_file` object has two standard attributes: `dimensions` and
    `variables`. The values of both are dictionaries, mapping dimension
    names to their associated lengths and variable names to variables,
    respectively. Application programs should never modify these
    dictionaries.

    All other attributes correspond to global attributes defined in the
    NetCDF file. Global file attributes are created by assigning to an
    attribute of the `netcdf_file` object.

    Parameters
    ----------
    filename : string or file-like
        string -> filename