/*
         *--------------------------------------------------------------
         *
         * HuffDecode --
         *
         * Taken from Figure F.16: extract next coded symbol from
         * input stream.  This should becode a macro.
         *
         * Results:
         * Next coded symbol
         *
         * Side effects:
         * Bitstream is parsed.
         *
         *--------------------------------------------------------------
         */
        public int HuffDecodeNikon(BitPumpMSB bits)
        {
            int rv;
            int l, temp;
            int code, val;

            HuffmanTable dctbl1 = huff[0];

            bits.fill();
            code = (int)bits.peekBitsNoFill(14);
            val  = dctbl1.bigTable[code];
            if ((val & 0xff) != 0xff)
            {
                bits.skipBitsNoFill((uint)val & 0xff);
                return(val >> 8);
            }

            rv   = 0;
            code = (int)bits.peekByteNoFill();
            val  = (int)dctbl1.numbits[code];
            l    = val & 15;
            if (l != 0)
            {
                bits.skipBitsNoFill((uint)l);
                rv = val >> 4;
            }
            else
            {
                bits.skipBits(8);
                l = 8;
                while (code > dctbl1.maxcode[l])
                {
                    temp = (int)bits.getBitNoFill();
                    code = (code << 1) | temp;
                    l++;
                }

                if (l > 16)
                {
                    throw new Exception("Corrupt JPEG data: bad Huffman code:" + l);
                }
                else
                {
                    rv = (int)dctbl1.huffval[dctbl1.valptr[l] + (code - dctbl1.minCode[l])];
                }
            }

            if (rv == 16)
            {
                return(-32768);
            }

            /*
             * Section F.2.2.1: decode the difference and
             * Figure F.12: extend sign bit
             */
            Int32 len  = rv & 15;
            Int32 shl  = rv >> 4;
            int   diff = (int)((bits.getBits((uint)(len - shl)) << 1) + 1) << shl >> 1;

            if ((diff & (1 << (len - 1))) == 0)
            {
                //TODO optimise
                if (shl == 0)
                {
                    shl = 1;
                }
                else
                {
                    shl = 0;
                }
                diff -= (1 << len) - shl;
            }
            return(diff);
        }
        public void DecompressNikon(TIFFBinaryReader metadata, UInt32 w, UInt32 h, UInt32 bitsPS, UInt32 offset, UInt32 size)
        {
            metadata.Position = 0;
            UInt32 v0         = metadata.ReadByte();
            UInt32 v1         = metadata.ReadByte();
            UInt32 huffSelect = 0;
            UInt32 split      = 0;

            int[] pUp1 = new int[2];
            int[] pUp2 = new int[2];
            mUseBigtable = true;

            //_RPT2(0, "Nef version v0:%u, v1:%u\n", v0, v1);

            if (v0 == 73 || v1 == 88)
            {
                metadata.ReadBytes(2110);
            }

            if (v0 == 70)
            {
                huffSelect = 2;
            }
            if (bitsPS == 14)
            {
                huffSelect += 3;
            }

            pUp1[0] = metadata.ReadInt16();
            pUp1[1] = metadata.ReadInt16();
            pUp2[0] = metadata.ReadInt16();
            pUp2[1] = metadata.ReadInt16();

            int    _max  = 1 << (int)bitsPS & 0x7fff;
            UInt32 step  = 0;
            UInt32 csize = metadata.ReadUInt16();

            if (csize > 1)
            {
                step = (uint)_max / (csize - 1);
            }
            if (v0 == 68 && v1 == 32 && step > 0)
            {
                for (UInt32 i = 0; i < csize; i++)
                {
                    curve[i * step] = (ushort)metadata.ReadInt16();
                }
                for (int i = 0; i < _max; i++)
                {
                    curve[i] = (ushort)((curve[i - i % step] * (step - i % step) + curve[i - i % step + step] * (i % step)) / step);
                }
                metadata.Position = (562);
                split             = metadata.ReadUInt16();
            }
            else if (v0 != 70 && csize <= 0x4001)
            {
                for (UInt32 i = 0; i < csize; i++)
                {
                    curve[i] = metadata.ReadUInt16();
                }
                _max = (int)csize;
            }
            initTable(huffSelect);

            mRaw.whitePoint = curve[_max - 1];
            mRaw.blackLevel = curve[0];
            if (!uncorrectedRawValues)
            {
                mRaw.setTable(curve, _max, true);
            }

            UInt32     x, y;
            BitPumpMSB bits  = new BitPumpMSB(ref input, offset, size);
            UInt32     pitch = w;

            int    pLeft1 = 0;
            int    pLeft2 = 0;
            UInt32 cw     = w / 2;
            UInt32 random = bits.peekBits(24);

            for (y = 0; y < h; y++)
            {
                if (split != 0 && (y == split))
                {
                    initTable(huffSelect + 1);
                }
                pUp1[y & 1] += HuffDecodeNikon(bits);
                pUp2[y & 1] += HuffDecodeNikon(bits);
                pLeft1       = pUp1[y & 1];
                pLeft2       = pUp2[y & 1];
                uint dest = y * pitch;
                mRaw.setWithLookUp((ushort)Common.clampbits(pLeft1, 15), ref mRaw.rawData, dest++, ref random);
                mRaw.setWithLookUp((ushort)Common.clampbits(pLeft2, 15), ref mRaw.rawData, dest++, ref random);
                for (x = 1; x < cw; x++)
                {
                    bits.checkPos();
                    pLeft1 += HuffDecodeNikon(bits);
                    pLeft2 += HuffDecodeNikon(bits);
                    mRaw.setWithLookUp((ushort)Common.clampbits(pLeft1, 15), ref mRaw.rawData, dest++, ref random);
                    mRaw.setWithLookUp((ushort)Common.clampbits(pLeft2, 15), ref mRaw.rawData, dest++, ref random);
                }
            }

            if (uncorrectedRawValues)
            {
                mRaw.setTable(curve, _max, false);
            }
            else
            {
                mRaw.table = (null);
            }
        }
        /* Attempt to decode the image */

        /* A RawDecoderException will be thrown if the image cannot be decoded
         * public void readUncompressedRaw(ref TIFFBinaryReader input, iPoint2D size, iPoint2D offset, int inputPitch, int bitPerPixel, BitOrder order)
         * {
         *  UInt32 outPitch = mRaw.pitch;
         *  uint w = (uint)size.x;
         *  uint h = (uint)size.y;
         *  UInt32 cpp = mRaw.cpp;
         *  UInt64 ox = (ulong)offset.x;
         *  UInt64 oy = (ulong)offset.y;
         *  if (this.mRaw.rawData == null)
         *  {
         *      mRaw.rawData = new ushort[w * h * cpp];
         *  }
         *  if (input.getRemainSize() < (inputPitch * (int)h))
         *  {
         *      if (input.getRemainSize() > inputPitch)
         *      {
         *          h = (uint)(input.getRemainSize() / inputPitch - 1);
         *          mRaw.errors.Add("Image truncated (file is too short)");
         *      }
         *      else
         *          throw new IOException("readUncompressedRaw: Not enough data to decode a single line. Image file truncated.");
         *  }
         *  if (bitPerPixel > 16)
         *      throw new RawDecoderException("readUncompressedRaw: Unsupported bit depth");
         *
         *  UInt32 skipBits = (uint)(inputPitch - (int)w * cpp * bitPerPixel / 8);  // Skip per line
         *  if (oy > (ulong)mRaw.dim.y)
         *      throw new RawDecoderException("readUncompressedRaw: Invalid y offset");
         *  if (ox + (ulong)size.x > (ulong)mRaw.dim.x)
         *      throw new RawDecoderException("readUncompressedRaw: Invalid x offset");
         *
         *  UInt64 y = oy;
         *  h = (uint)Math.Min(h + (uint)oy, mRaw.dim.y);
         *  /*
         *  if (mRaw.getDataType() == RawImageType.TYPE_FLOAT32)
         *  {
         *      if (bitPerPixel != 32)
         *          throw new RawDecoderException("readUncompressedRaw: Only 32 bit float point supported");
         *      BitBlt(&data[offset.x * sizeof(float) * cpp + y * outPitch], outPitch,
         *          input.getData(), inputPitch, w * mRaw.bpp, h - y);
         *      return;
         *  }
         *
         *  if (BitOrder.Jpeg == order)
         *  {
         *      BitPumpMSB bits = new BitPumpMSB(ref input);
         *      w *= cpp;
         *      for (; y < h; y++)
         *      {
         *          bits.checkPos();
         *          for (UInt32 x = 0; x < w; x++)
         *          {
         *              UInt32 b = bits.getBits((uint)bitPerPixel);
         *              mRaw.rawData[(((int)(offset.x * sizeof(UInt16) * cpp) + (int)y * (int)outPitch)) + x] = (ushort)b;
         *          }
         *          bits.skipBits(skipBits);
         *      }
         *  }
         *  else if (BitOrder.Jpeg16 == order)
         *  {
         *      BitPumpMSB16 bits = new BitPumpMSB16(input);
         *      w *= cpp;
         *      for (; y < h; y++)
         *      {
         *          bits.checkPos();
         *          for (UInt32 x = 0; x < w; x++)
         *          {
         *              UInt32 b = bits.getBits((uint)bitPerPixel);
         *              mRaw.rawData[(offset.x * sizeof(ushort) * (int)cpp + (int)y * (int)outPitch) + x] = (ushort)b;
         *          }
         *          bits.skipBits(skipBits);
         *      }
         *  }
         *  else if (BitOrder.Jpeg32 == order)
         *  {
         *      BitPumpMSB32 bits = new BitPumpMSB32(input);
         *      w *= cpp;
         *      for (; y < h; y++)
         *      {
         *          bits.checkPos();
         *          for (UInt32 x = 0; x < w; x++)
         *          {
         *              UInt32 b = bits.getBits((uint)bitPerPixel);
         *              mRaw.rawData[(offset.x * sizeof(ushort) * (int)cpp + (int)y * (int)outPitch) + x] = (ushort)b;
         *          }
         *          bits.skipBits(skipBits);
         *      }
         *  }
         *  else
         *  {
         *      if (bitPerPixel == 16)
         *      {
         *          Decode16BitRawUnpacked(input, w, h);
         *          return;
         *      }
         *      if (bitPerPixel == 12 && (int)w == inputPitch * 8 / 12)
         *      {
         *          Decode12BitRaw(input, w, h);
         *          return;
         *      }
         *      BitPumpPlain bits = new BitPumpPlain(input);
         *      w *= cpp;
         *      for (; y < h; y++)
         *      {
         *          bits.checkPos();
         *          for (UInt32 x = 0; x < w; x++)
         *          {
         *              UInt32 b = bits.getBits((uint)bitPerPixel);
         *              mRaw.rawData[(offset.x * sizeof(ushort) + (int)y * (int)outPitch) + x] = (ushort)b;
         *          }
         *          bits.skipBits(skipBits);
         *      }
         *  }
         * }
         */

        public unsafe void readUncompressedRaw(ref TIFFBinaryReader input, iPoint2D size, iPoint2D offset, int inputPitch, int bitPerPixel, BitOrder order)
        {
            fixed(ushort *d = mRaw.rawData)
            {
                byte *data     = (byte *)d;
                uint  outPitch = mRaw.pitch;
                int   w        = size.x;
                int   h        = size.y;
                uint  cpp      = mRaw.cpp;
                int   ox       = offset.x;
                int   oy       = offset.y;

                if (input.getRemainSize() < (inputPitch * h))
                {
                    if ((int)input.getRemainSize() > inputPitch)
                    {
                        h = input.getRemainSize() / inputPitch - 1;
                        mRaw.errors.Add("Image truncated (file is too short)");
                    }
                    else
                    {
                        throw new IOException("readUncompressedRaw: Not enough data to decode a single line. Image file truncated.");
                    }
                }
                if (bitPerPixel > 16)
                {
                    throw new RawDecoderException("readUncompressedRaw: Unsupported bit depth");
                }

                uint skipBits = (uint)(inputPitch - w * cpp * bitPerPixel / 8);  // Skip per line

                if (oy > mRaw.dim.y)
                {
                    throw new RawDecoderException("readUncompressedRaw: Invalid y offset");
                }
                if (ox + size.x > mRaw.dim.x)
                {
                    throw new RawDecoderException("readUncompressedRaw: Invalid x offset");
                }

                int y = oy;

                h = (int)Math.Min(h + oy, (uint)mRaw.dim.y);

                /*if (mRaw.getDataType() == TYPE_FLOAT32)
                 * {
                 *  if (bitPerPixel != 32)
                 *      throw new RawDecoderException("readUncompressedRaw: Only 32 bit float point supported");
                 *  BitBlt(&data[offset.x * sizeof(float) * cpp + y * outPitch], outPitch,
                 *      input.getData(), inputPitch, w * mRaw.bpp, h - y);
                 *  return;
                 * }*/

                if (BitOrder.Jpeg == order)
                {
                    BitPumpMSB bits = new BitPumpMSB(ref input);
                    w *= (int)cpp;
                    for (; y < h; y++)
                    {
                        bits.checkPos();
                        for (uint x = 0; x < w; x++)
                        {
                            uint b = bits.getBits((uint)bitPerPixel);
                            mRaw.rawData[x + (offset.x * cpp + y * mRaw.dim.x * cpp)] = (ushort)b;
                        }
                        bits.skipBits(skipBits);
                    }
                }
                else if (BitOrder.Jpeg16 == order)
                {
                    BitPumpMSB16 bits = new BitPumpMSB16(ref input);
                    w *= (int)cpp;
                    for (; y < h; y++)
                    {
                        UInt16 *dest = (UInt16 *)&data[offset.x * sizeof(UInt16) * cpp + y * outPitch];
                        bits.checkPos();
                        for (uint x = 0; x < w; x++)
                        {
                            uint b = bits.getBits((uint)bitPerPixel);
                            dest[x] = (ushort)b;
                        }
                        bits.skipBits(skipBits);
                    }
                }
                else if (BitOrder.Jpeg32 == order)
                {
                    BitPumpMSB32 bits = new BitPumpMSB32(ref input);
                    w *= (int)cpp;
                    for (; y < h; y++)
                    {
                        UInt16 *dest = (UInt16 *)&data[offset.x * sizeof(UInt16) * cpp + y * outPitch];
                        bits.checkPos();
                        for (uint x = 0; x < w; x++)
                        {
                            uint b = bits.getBits((uint)bitPerPixel);
                            dest[x] = (ushort)b;
                        }
                        bits.skipBits(skipBits);
                    }
                }
                else
                {
                    if (bitPerPixel == 16 && Common.getHostEndianness() == Endianness.little)
                    {
                        Decode16BitRawUnpacked(input, (uint)w, (uint)h);
                        return;
                    }
                    if (bitPerPixel == 12 && (int)w == inputPitch * 8 / 12 && Common.getHostEndianness() == Endianness.little)
                    {
                        Decode12BitRaw(input, (uint)w, (uint)h);
                        return;
                    }
                    BitPumpPlain bits = new BitPumpPlain(ref input);
                    w *= (int)cpp;
                    for (; y < h; y++)
                    {
                        UInt16 *dest = (UInt16 *)&data[offset.x * sizeof(UInt16) + y * outPitch];
                        bits.checkPos();
                        for (uint x = 0; x < w; x++)
                        {
                            uint b = bits.getBits((uint)bitPerPixel);
                            dest[x] = (ushort)b;
                        }
                        bits.skipBits(skipBits);
                    }
                }
            }
        }