#ifndef SpecialPixel_h
#define SpecialPixel_h
/**
 * @file
 * $Revision: 1.2 $
 * $Date: 2008/01/17 19:25:27 $
 *
 *   Unless noted otherwise, the portions of Isis written by the USGS are public
 *   domain. See individual third-party library and package descriptions for
 *   intellectual property information,user agreements, and related information.
 *
 *   Although Isis has been used by the USGS, no warranty, expressed or implied,
 *   is made by the USGS as to the accuracy and functioning of such software
 *   and related material nor shall the fact of distribution constitute any such
 *   warranty, and no responsibility is assumed by the USGS in connection
 *   therewith.
 *
 *   For additional information, launch
 *   $ISISROOT/doc//documents/Disclaimers/Disclaimers.html in a browser or see
 *   the Privacy &amp; Disclaimers page on the Isis website,
 *   http://isis.astrogeology.usgs.gov, and the USGS privacy and disclaimers on
 *   http://www.usgs.gov/privacy.html.
 */
#include <vector>
#include <cmath>
#include <cfloat>
#include <QString>
#include <QStringList>

#include "IException.h"
#include "IString.h"

namespace Isis {

  /**
   * @brief Manipulate special pixel values
   *
   * This is not technically a class but instead a set of constants and
   * functions which operate on Isis special pixels. There are several types of
   * special pixels defined under the namespace Isis which include:
   *   Isis::Null Pixel has no data available
   *   Isis::Lis Pixel was saturated on the instrument
   *   Isis::His Pixel was saturated on the instrument
   *   Isis::Lrs Pixel was saturated during a computation
   *   Isis::Hrs Pixel was saturated during a computation
   * As indicated, saturated pixels are either generated by the instrument or
   * generated by a computation. For example, on the instrument with a 8-bit
   * Analog-to-Digital converter, a gain setting could cause the conversion of
   * many pixels to exceed 255. Because their are only 8-bits the value 255 is
   * likely a High Instrument Saturation (His) value. Likewise, 0 would be a Low
   * Instrument Saturation (Lis) value. The Lrs and Hrs values indicate Low or
   * High Representation Saturation respectively. When an application program
   * operates on non-special pixels using standard arithmetic operators (and
   * other functions) it is possible that the operation could generate an Lrs or
   * Hrs. Application programmer should take care when processing pixels to
   * ensure that special pixels are not used in computations. These tests can be
   * performed with various functions provided in this include file.
   *
   * @ingroup Utility
   *
   * @author 2002-04-11 Kris Becker
   *
   * @internal
   *   @history 2003-02-11 Jeff Anderson - Wrote unitTest and documentation
   *   @history 2003-05-16 Stuart Sides - Modified schema from astrogeology...
   *                           isis.astrogeology...
   *   @history 2005-02-15 Elizabeth Ribelin - Modified file to support Doxygen
   *                           documentation
   *   @history 2005-05-18 Jeff Anderson - Changed long to int for 64-bit port
   *   @history 2008-01-09 Jeannie Walldren - Added new unitTests
   *                           and new method, StringToPixel()
   *   @history 2008-01-10 Jeannie Walldren - Added new unitTests
   *                           and modified StringToPixel() to allow
   *                           the user to enter any legal and unique
   *                           shortened version of special pixels.
   *   @history 2016-04-20 Jeannie Backer - Added Janet Barret's changes
   *                           IVALID_MAX4 definition to handle SignedInteger
   *                           imports.
   *
   *  @todo 2005-02-15 Kris Becker - finish class documentation
   *
   */

/// @cond INTERNAL
  //  Set up special kludge for double precision initialization
  typedef union {
    unsigned int i[2];
    double d;
  } DBL_UNION;

#if ISIS_LITTLE_ENDIAN
#  define DBL_INIT(a,b)  {(b), (a)}
#else
#  define DBL_INIT(a,b)  {(a), (b)}
#endif
/// @endcond

  // Define 8-byte special pixel values for IEEE floating point
  const DBL_UNION IVALID_MIN8 = { DBL_INIT(0xFFEFFFFF, 0xFFFFFFFA) };
  const double VALID_MIN8     = IVALID_MIN8.d;
  const double ValidMinimum   = IVALID_MIN8.d; /**< The minimum valid double
                                                    value for Isis pixels.
                                                    All special pixels have
                                                    a value lower than this
                                                    minimum.*/

  const DBL_UNION INULL8 = { DBL_INIT(0xFFEFFFFF, 0xFFFFFFFB) };
  const double NULL8     = INULL8.d;
  const double Null      = INULL8.d;  //!<Value for an Isis Null pixel

  const DBL_UNION ILOW_REPR_SAT8 = { DBL_INIT(0xFFEFFFFF, 0xFFFFFFFC) };
  const double LOW_REPR_SAT8     = ILOW_REPR_SAT8.d;
  const double Lrs               = ILOW_REPR_SAT8.d;  /**< Value for an Isis
                                                            Low Representation
                                                            Saturation pixel.*/

  const DBL_UNION ILOW_INSTR_SAT8 = { DBL_INIT(0xFFEFFFFF, 0xFFFFFFFD) };
  const double LOW_INSTR_SAT8     = ILOW_INSTR_SAT8.d;
  const double Lis                = ILOW_INSTR_SAT8.d;  /**< Value for an Isis
                                                             Low Instrument
                                                             Saturation pixel.*/

  const DBL_UNION IHIGH_INSTR_SAT8 = { DBL_INIT(0xFFEFFFFF, 0xFFFFFFFE) };
  const double HIGH_INSTR_SAT8     = IHIGH_INSTR_SAT8.d;
  const double His                 = IHIGH_INSTR_SAT8.d; /**<Value for an Isis
                                                             High Instrument
                                                             Saturation pixel.*/

  const DBL_UNION IHIGH_REPR_SAT8  = { DBL_INIT(0xFFEFFFFF, 0xFFFFFFFF) };
  const double HIGH_REPR_SAT8      = IHIGH_REPR_SAT8.d;
  const double Hrs                 = IHIGH_REPR_SAT8.d;  /**<Value for an Isis
                                                             High Representation
                                                             Saturation pixel.*/

  const double VALID_MAX8   = DBL_MAX;
  const double ValidMaximum = DBL_MAX;  /**<The maximum valid double value for
                                            Isis pixels.*/


#undef DBL_INIT

  // Define 4-byte special pixel values for IEEE floating point
  const int  IVALID_MIN4 = 0xFF7FFFFA;
  const float VALID_MIN4 = (*((const float *) &IVALID_MIN4));

  const int  INULL4 = 0xFF7FFFFB;
  const float NULL4 = (*((const float *) &INULL4));

  const int  ILOW_REPR_SAT4 = 0xFF7FFFFC;
  const float LOW_REPR_SAT4 = (*((const float *) &ILOW_REPR_SAT4));

  const int  ILOW_INSTR_SAT4 = 0xFF7FFFFD;
  const float LOW_INSTR_SAT4 = (*((const float *) &ILOW_INSTR_SAT4));

  const int  IHIGH_INSTR_SAT4 = 0xFF7FFFFE;
  const float HIGH_INSTR_SAT4 = (*((const float *) &IHIGH_INSTR_SAT4));

  const int  IHIGH_REPR_SAT4 = 0xFF7FFFFF;
  const float HIGH_REPR_SAT4 = (*((const float *) &IHIGH_REPR_SAT4));

  const float VALID_MAX4 = FLT_MAX;
  const int IVALID_MAX4  = (*((const int *) &VALID_MAX4));

  // 2-byte signed special pixel values
  const short VALID_MIN2     = ((short)(-32752));
  const short NULL2          = ((short)(-32768));
  const short LOW_REPR_SAT2  = ((short)(-32767));
  const short LOW_INSTR_SAT2 = ((short)(-32766));
  const short HIGH_INSTR_SAT2 = ((short)(-32765));
  const short HIGH_REPR_SAT2  = ((short)(-32764));
  const short VALID_MAX2      = ((short)   32767);

  // 2-byte unsigned special pixel values
  const unsigned short VALID_MINU2      = ((unsigned short)       3);
  const unsigned short NULLU2           = ((unsigned short)       0);
  const unsigned short LOW_REPR_SATU2   = ((unsigned short)       1);
  const unsigned short LOW_INSTR_SATU2  = ((unsigned short)       2);
  const unsigned short HIGH_INSTR_SATU2 = ((unsigned short)   65534);
  const unsigned short HIGH_REPR_SATU2  = ((unsigned short)   65535);
  const unsigned short VALID_MAXU2      = ((unsigned short)   65522);

  // 1-byte special pixel values
  const unsigned char VALID_MIN1      = ((unsigned char) 1);
  const unsigned char NULL1           = ((unsigned char) 0);
  const unsigned char LOW_REPR_SAT1   = ((unsigned char) 0);
  const unsigned char LOW_INSTR_SAT1  = ((unsigned char) 0);
  const unsigned char HIGH_INSTR_SAT1 = ((unsigned char) 255);
  const unsigned char HIGH_REPR_SAT1  = ((unsigned char) 255);
  const unsigned char VALID_MAX1      = ((unsigned char) 254);

  /**
   * Returns if the input pixel is special. Not special implies it is valid to
   * use in computations.
   *
   * @param d Pixel value to test
   *
   * @return bool
   */
  inline bool IsSpecial(const double d) {
    return (d < VALID_MIN8);
  }


  /**
   * Returns if the input pixel is special. Not special implies it is valid to
   * use in computations. This method applies to a 4-byte floating point rather
   * than an 8-byte double
   *
   * @param f Pixel value to test
   *
   * @return bool
   */
  inline bool IsSpecial(const float f) {
    return (f < VALID_MIN4);
  }


  /**
   * Returns if the input pixel is valid
   *
   * @param d Pixel value to test
   *
   * @return bool
   */
  inline bool IsValidPixel(const double d) {
    return (d >= VALID_MIN8);
  }


  /**
   * Returns if the input pixel is null
   *
   * @param d Pixel value to test
   *
   * @return bool
   */
  inline bool IsNullPixel(const double d) {
    return (d == NULL8);
  }


  /**
   * Returns if the input pixel is one of the high saturation types
   *
   * @param d Pixel value to test
   *
   * @return bool
   */
  inline bool IsHighPixel(const double d) {
    return (d == HIGH_REPR_SAT8) || (d == HIGH_INSTR_SAT8);
  }


  /**
   * Returns if the input pixel is one of the low saturation types
   *
   * @param d Pixel value to test
   *
   * @return bool
   */
  inline bool IsLowPixel(const double d) {
    return (d == LOW_REPR_SAT8) || (d == LOW_INSTR_SAT8);
  }


  /**
   * Returns if the input pixel is high representation saturation
   *
   * @param d Pixel value to test
   *
   * @return bool
   */
  inline bool IsHrsPixel(const double d) {
    return (d == HIGH_REPR_SAT8);
  }


  /**
   * Returns if the input pixel is high instrument saturation
   *
   * @param d Pixel value to test
   *
   * @return bool
   */
  inline bool IsHisPixel(const double d) {
    return (d == HIGH_INSTR_SAT8);
  }


  /**
   * Returns if the input pixel is low instrument saturation
   *
   * @param d Pixel value to test
   *
   * @return bool
   */
  inline bool IsLisPixel(const double d) {
    return (d == LOW_INSTR_SAT8);
  }


  /**
   * Returns if the input pixel is low representation saturation
   *
   * @param d Pixel value to test
   *
   * @return bool
   */
  inline bool IsLrsPixel(const double d) {
    return (d == LOW_REPR_SAT8);
  }


  /**
   * Converts float pixels to double pixels with special pixel translations
   *
   * @param t Float pixel value to be converted to a double
   *
   * @return double The double pixel value
   */
  inline double TestPixel(const float t) {
    if(t < VALID_MIN4) {
      if(t == NULL4) return (NULL8);
      if(t == LOW_REPR_SAT4) return (LOW_REPR_SAT8);
      if(t == LOW_INSTR_SAT4) return (LOW_INSTR_SAT8);
      if(t == HIGH_REPR_SAT4) return (HIGH_REPR_SAT8);
      if(t == HIGH_INSTR_SAT4) return (HIGH_INSTR_SAT8);
      return (LOW_REPR_SAT8);
    }
    else if(t > VALID_MAX4) {
      return (HIGH_REPR_SAT8);
    }
    else {
      return ((double) t);
    }
  }


  /**
   * Converts double to float with special pixel translations and
   * care for overflows (underflows are assumed to cast to 0!)
   *
   * @param t Double pixel value to be converted to a float
   *
   * @return float The float pixel value
   */
  inline float TestPixel(const double t) {
    if(t < (double) VALID_MIN4) {
      if(t == NULL8) return (NULL4);
      if(t == LOW_REPR_SAT8) return (LOW_REPR_SAT4);
      if(t == LOW_INSTR_SAT8) return (LOW_INSTR_SAT4);
      if(t == HIGH_INSTR_SAT8) return (HIGH_INSTR_SAT4);
      if(t == HIGH_REPR_SAT8) return (HIGH_REPR_SAT4);
      return (LOW_REPR_SAT4);
    }
    else if(t > (double) VALID_MAX4) {
      return (HIGH_REPR_SAT8);
    }
    else {
      return ((float) t);
    }
  }


  /**
   * Takes a double pixel value and returns the name of the pixel type as a
   * string
   *
   * @param d Pixel value
   *
   * @return string The name of the pixel type
   */
  inline QString PixelToString(double d) {
    if(Isis::IsSpecial(d)) {
      if(Isis::IsNullPixel(d)) return "Null";
      if(Isis::IsLrsPixel(d)) return "Lrs";
      if(Isis::IsHrsPixel(d)) return "Hrs";
      if(Isis::IsHisPixel(d)) return "His";
      if(Isis::IsLisPixel(d)) return "Lis";
      return "Invalid";
    }

    QString result;
    return result.setNum(d, 'g', 8);
  }


  /**
   * Takes the name of the pixel type as a string and returns a
   * double pixel value.
   *
   * @param str The name of the pixel type
   *
   * @return double Pixel value
   */
  inline double StringToPixel(const QString &str) {

    QString s = str.toUpper();

    QStringList legal;
    legal.push_back("NULL");
    legal.push_back("HRS");
    legal.push_back("LRS");
    legal.push_back("HIS");
    legal.push_back("LIS");
    int matches = 0;
    for(int i = 0; i < (int) legal.size(); i++) {
      if(legal[i].mid(0, s.size()) == s) {
        matches++;
      }
    }
    if(matches > 1) {
      QString msg = "Input [" + str + "] is not a unique abbreviation. Use " + s + "I or " + s + "R.";
      throw IException(IException::User, msg, _FILEINFO_);
    }
    if(matches == 0) {
      try {
        return toDouble(s);
      }
      catch(IException &e) {
        QString msg = "Input [" + str + "] does not appear to be a legal special pixel abbreviation or double value.";
        throw IException(e, IException::User, msg, _FILEINFO_);
      }
    }
    if(s[0] == 'N') return Null;
    if(s.mid(0, 2) == "HR") return Hrs;
    if(s.mid(0, 2) == "LR") return Lrs;
    if(s.mid(0, 2) == "HI") return His;
    else  return Lis;//(s.substr(0,2) == "LI")

  }
}

#endif

