# -*- coding: utf-8 -*-

"""Constants for Isis Special Pixels.

The constants are provided as SpecialPixel :func:collection.namedtuple:
objects, and have the following keys: Min, Null, Lrs, Lis, His,
Hrs, and Max.

Null
    A null pixel indicates no data was collected at the particular
    location.

Lis
    A low instrument saturation pixel occurred meaning the
    instrument readout was at its lowest possible value.

His
    A high instrument saturation pixel occurred meaning the
    instrument readout was at its highest possible value.

Lrs
    A low representation saturation pixel occurred meaning a
    program computed a new pixel value at this location that
    was lower than native bit-type for the file (e.g., less
    than zero for an 8-bit file).

Hrs:
    A high representation saturation pixel occurred meaning a
    program computed a new pixel value at this location that
    was greater than native bit-type for the file (e.g., greater
    than 255 for an 8-bit file).

The above definitions come from the `Logical Cube Format Guide
<https://isis.astrogeology.usgs.gov/documents/LogicalCubeFormatGuide/LogicalCubeFormatGuide.html>`_
and the numerical values come from the ISIS `SpecialPixel.h
<https://github.com/USGS-Astrogeology/ISIS3/blob/dev/isis/src/base/objs/SpecialPixel/SpecialPixel.h>`_ file.

In order to be able to know whether value should result in a
*Lrs* or *Hrs* value, we must also know:

Min
    The minimum valid value for a pixel.

Max
    The maximum valid value for a pixel.
"""  # noqa

# Copyright 2015, William Trevor Olson
# Copyright 2020, Ross A. Beyer (rbeyer@seti.org)
#
# Reuse is permitted under the terms of the license.
# The AUTHORS file and the LICENSE file are at the
# top level of this library.

import collections
import struct
import sys


SpecialPixels = collections.namedtuple('SpecialPixels',
                                       ['Min', 'Null',
                                        'Lrs', 'Lis',
                                        'His', 'Hrs',
                                        'Max'])

# 1-byte special pixel values from SpecialPixel.h
UnsignedByte = SpecialPixels(Min=1, Null=0, Lrs=0, Lis=0,
                             His=255, Hrs=255, Max=254)

# 2-byte unsigned special pixel values from SpecialPixel.h
UnsignedWord = SpecialPixels(Min=3, Null=0, Lrs=1, Lis=2,
                             His=65534, Hrs=65535, Max=65522)

# 2-byte signed special pixel values from SpecialPixel.h
SignedWord = SpecialPixels(Min=-32752, Null=-32768,
                           Lrs=-32767, Lis=-32766,
                           His=-32765, Hrs=-32764, Max=32767)

# 4-byte unsigned special pixel values from SpecialPixel.h
# This deals with unsigned long int values.  Not sure why there
# is so much space between 'Max' and the 'His' and 'Hrs' values, but
# that's what's in SpecialPixel.h.
UnsignedInteger = SpecialPixels(Min=3, Null=0, Lrs=1, Lis=2,
                                His=4294967294, Hrs=4294967295,
                                Max=4294967282)

# This was in the original pysis, but I don't see it in SpecialPixel.h?
# The 'Max' value indicates that it is like a long int, but the 'Min'
# and other negative values are far from the possible representation
# minimum of a long int, so not sure what's going on here.
SignedInteger = SpecialPixels(Min=-8388614, Null=-8388613,
                              Lrs=-8388612, Lis=-8388611,
                              His=-8388610, Hrs=-8388609, Max=2147483647)

# 4-byte special pixel values for IEEE floating point from SpecialPixel.h
Real = SpecialPixels(
    Min=struct.unpack('>f', bytes.fromhex('FF7FFFFA'))[0],
    Null=struct.unpack('>f', bytes.fromhex('FF7FFFFB'))[0],
    Lrs=struct.unpack('>f', bytes.fromhex('FF7FFFFC'))[0],
    Lis=struct.unpack('>f', bytes.fromhex('FF7FFFFD'))[0],
    His=struct.unpack('>f', bytes.fromhex('FF7FFFFE'))[0],
    Hrs=struct.unpack('>f', bytes.fromhex('FF7FFFFF'))[0],
    Max=sys.float_info.max)

# 8-byte special pixel values for IEEE floating point from SpecialPixel.h
Double = SpecialPixels(
    Min=struct.unpack('>d', bytes.fromhex('FFEFFFFF FFFFFFFA'))[0],
    Null=struct.unpack('>d', bytes.fromhex('FFEFFFFF FFFFFFFB'))[0],
    Lrs=struct.unpack('>d', bytes.fromhex('FFEFFFFF FFFFFFFC'))[0],
    Lis=struct.unpack('>d', bytes.fromhex('FFEFFFFF FFFFFFFD'))[0],
    His=struct.unpack('>d', bytes.fromhex('FFEFFFFF FFFFFFFE'))[0],
    Hrs=struct.unpack('>d', bytes.fromhex('FFEFFFFF FFFFFFFF'))[0],
    Max=sys.float_info.max)
