/*
 *  This file is part of RawTherapee.
 *
 *  Copyright (c) 2010 Fabio Suprani <ffsup2@yahoo.it>
 *
 *  RawTherapee is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  RawTherapee is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with RawTherapee.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef DCRAW_H
#define DCRAW_H

#include "myfile.h"
#include <csetjmp>

class DCraw {
public:
    typedef unsigned short ushort;
    typedef unsigned char uchar;
    typedef unsigned short (*dcrawImage_t)[4];
#ifdef WIN32
    typedef __int64 INT64;
    typedef unsigned __int64 UINT64;
#else
    typedef long long INT64;
    typedef unsigned long long UINT64;
#endif

    DCraw()
        : exif_base(-1), ciff_base(-1), ciff_len(0), ifp(nullptr), ofp(nullptr),
          order(0x4949), ifname(nullptr), meta_data(nullptr), shot_select(0),
          multi_out(0), row_padding(0), float_raw_image(nullptr),
          image(nullptr), bright(1.), half_size(0), four_color_rgb(0),
          document_mode(0), highlight(0), verbose(0), use_auto_wb(0),
          use_camera_wb(0), use_camera_matrix(1), output_color(1),
          output_bps(8), output_tiff(0), med_passes(0), no_auto_bright(0),
          RT_whitelevel_from_constant(ThreeValBool::X),
          RT_blacklevel_from_constant(ThreeValBool::X),
          RT_matrix_from_constant(ThreeValBool::X), RT_baseline_exposure(0),
          RT_OpcodeList2_start(-1), RT_OpcodeList2_len(0),
          getbithuff(this, ifp, zero_after_ff), nikbithuff(ifp)
    {
        shrink = 0;
        memset(&hbd, 0, sizeof(hbd));
        aber[0] = aber[1] = aber[2] = aber[3] = 1;
        gamm[0] = 0.45;
        gamm[1] = 4.5;
        gamm[2] = gamm[3] = gamm[4] = gamm[5] = 0;
        user_mul[0] = user_mul[1] = user_mul[2] = user_mul[3] = 0;
        greybox[0] = greybox[1] = 0;
        greybox[2] = greybox[3] = UINT_MAX;
        RT_canon_CR3_data.CR3_CTMDtag = 0;
    }

protected:
    int exif_base, ciff_base, ciff_len;
    IMFILE *ifp;
    FILE *ofp;
    short order;
    const char *ifname;
    char *meta_data;
    int xtrans[6][6], xtrans_abs[6][6];
    char cdesc[5], desc[512], make[64], model[64], model2[64], model3[64],
        artist[64];
    float flash_used, canon_ev, iso_speed, shutter, aperture, focal_len;
    time_t timestamp;
    unsigned shot_order, kodak_cbpp, filters, exif_cfa, unique_id;
    off_t strip_offset, data_offset;
    off_t thumb_offset, meta_offset, profile_offset;
    unsigned thumb_length, meta_length, profile_length;
    unsigned thumb_misc, *oprof, fuji_layout, shot_select, multi_out;
    unsigned tiff_nifds, tiff_samples, tiff_bps, tiff_compress;
    unsigned black, cblack[4102], maximum, mix_green, raw_color, zero_is_bad;
    unsigned zero_after_ff, is_raw, dng_version, is_foveon, data_error;
    unsigned tile_width, tile_length, gpsdata[32], load_flags, row_padding;
    bool xtransCompressed = false;
    struct fuji_compressed_params {
        char *q_table;  /* quantization table */
        int q_point[5]; /* quantization points */
        int max_bits;
        int min_value;
        int raw_bits;
        int total_values;
        int maxDiff;
        ushort line_width;
    };

    struct int_pair {
        int value1;
        int value2;
    };

    enum _xt_lines {
        _R0 = 0,
        _R1,
        _R2,
        _R3,
        _R4,
        _G0,
        _G1,
        _G2,
        _G3,
        _G4,
        _G5,
        _G6,
        _G7,
        _B0,
        _B1,
        _B2,
        _B3,
        _B4,
        _ltotal
    };

    struct fuji_compressed_block {
        int cur_bit;            // current bit being read (from left to right)
        int cur_pos;            // current position in a buffer
        INT64 cur_buf_offset;   // offset of this buffer in a file
        unsigned max_read_size; // Amount of data to be read
        int cur_buf_size;       // buffer size
        uchar *cur_buf;         // currently read block
        int fillbytes;          // Counter to add extra byte for block size N*16
        IMFILE *input;
        struct int_pair grad_even[3][41]; // tables of gradients
        struct int_pair grad_odd[3][41];
        ushort *linealloc;
        ushort *linebuf[_ltotal];
    };

    int fuji_total_lines, fuji_total_blocks, fuji_block_width, fuji_bits,
        fuji_raw_type;

    ushort raw_height, raw_width, height, width, top_margin, left_margin;
    ushort shrink, iheight, iwidth, fuji_width, thumb_width, thumb_height;
    unsigned raw_size;
    ushort *raw_image;
    float *float_raw_image;
    ushort white[8][8], curve[0x10000], cr2_slice[3], sraw_mul[4];
    int mask[8][4], flip, tiff_flip, colors;
    double pixel_aspect;
    double aber[4];
    double gamm[6];
    dcrawImage_t image;
    float bright, threshold, user_mul[4];

    int half_size, four_color_rgb, document_mode, highlight;
    int verbose, use_auto_wb, use_camera_wb, use_camera_matrix;
    int output_color, output_bps, output_tiff, med_passes;
    int no_auto_bright;
    unsigned greybox[4];
    enum class ThreeValBool { X = -1, F, T };
    ThreeValBool RT_whitelevel_from_constant;
    ThreeValBool RT_blacklevel_from_constant;
    ThreeValBool RT_matrix_from_constant;
    std::string RT_software;
    double RT_baseline_exposure;
    int RT_OpcodeList2_start;
    int RT_OpcodeList2_len;

    struct PanasonicRW2Info {
        ushort bpp;
        ushort encoding;
        PanasonicRW2Info(): bpp(0), encoding(0) {}
    };
    PanasonicRW2Info RT_pana_info;

public:
    //------------------------------------------------------------------------
    /*
     * Copyright (C) 2018-2019 Alexey Danilchenko
     * Copyright (C) 2019 Alex Tutubalin, LibRaw LLC
     *
       Canon CR3 file decoder

     LibRaw is free software; you can redistribute it and/or modify
     it under the terms of the one of two licenses as you choose:

    1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
       (See file LICENSE.LGPL provided in LibRaw distribution archive for
    details).

    2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
       (See file LICENSE.CDDL provided in LibRaw distribution archive for
    details).

    */
    struct CanonCR3Data {
        // contents of tag CMP1 for relevant track in CR3 file
        struct crx_data_header_t {
            int32_t version;
            int32_t f_width;
            int32_t f_height;
            int32_t tileWidth;
            int32_t tileHeight;
            int32_t nBits;
            int32_t nPlanes;
            int32_t cfaLayout;
            int32_t encType;
            int32_t imageLevels;
            int32_t hasTileCols;
            int32_t hasTileRows;
            int32_t mdatHdrSize;
            // Not from header, but from datastream
            uint32_t MediaSize;
            INT64 MediaOffset;
            uint32_t MediaType; /* 1 -> /C/RAW, 2-> JPEG */

            crx_data_header_t() = default;
        };
        static constexpr size_t CRXTRACKS_MAXCOUNT = 16;
        std::array<crx_data_header_t, CRXTRACKS_MAXCOUNT> crx_header;
        int crx_track_selected;
        short CR3_CTMDtag;
    };
    //------------------------------------------------------------------------
    struct CanonLevelsData {
        unsigned cblack[4];
        unsigned white;
        bool black_ok;
        bool white_ok;
        CanonLevelsData(): cblack{0}, white{0}, black_ok(false), white_ok(false)
        {
        }
    };

protected:
    CanonCR3Data RT_canon_CR3_data;
    CanonLevelsData RT_canon_levels_data;

    float cam_mul[4], pre_mul[4], cmatrix[3][4], rgb_cam[3][4];

    int histogram[4][0x2000];
    void (DCraw::*write_thumb)(), (DCraw::*write_fun)();
    void (DCraw::*load_raw)(), (DCraw::*thumb_load_raw)();
    jmp_buf failure;

    unsigned huff[1024]; // static inside foveon_decoder

    struct decode {
        struct decode *branch[2];
        int leaf;
    } first_decode[2048], *second_decode, *free_decode;

    struct tiff_ifd {
        int width, height, bps, comp, phint, offset, flip, samples, bytes;
        int tile_width, tile_length, sample_format, predictor;
        float shutter;
        int extrasamples;
    } tiff_ifd[10];

    struct ph1 {
        int format, key_off, tag_21a;
        int black, split_col, black_col, split_row, black_row;
        float tag_210;
    } ph1;
    struct hbd {
        off_t levels, unknown1, flatfield;
    } hbd;

    struct jhead {
        int algo, bits, high, wide, clrs, sraw, psv, restart, vpred[6];
        ushort quant[64], idct[64], *huff[20], *free[20], *row;
    };

    struct tiff_tag {
        ushort tag, type;
        int count;
        union {
            char c[4];
            short s[2];
            int i;
        } val;
    };

    struct tiff_hdr {
        ushort order, magic;
        int ifd;
        ushort pad, ntag;
        struct tiff_tag tag[23];
        int nextifd;
        ushort pad2, nexif;
        struct tiff_tag exif[4];
        ushort pad3, ngps;
        struct tiff_tag gpst[10];
        short bps[4];
        int rat[10];
        unsigned gps[26];
        char desc[512], make[64], model[64], soft[32], date[20], artist[64];
    };

protected:
    int fcol(int row, int col);
    void merror(void *ptr, const char *where);
    void derror();
    inline void derror(bool condition)
    {
        if (UNLIKELY(condition))
            ++data_error;
    }
    ushort sget2(uchar *s);
    ushort get2();
    unsigned sget4(uchar *s);
    unsigned get4();
    unsigned getint(int type);
    float int_to_float(int i);
    double getreal(int type);
    void read_shorts(ushort *pixel, int count);
    void cubic_spline(const int *x_, const int *y_, const int len);
    void canon_600_fixed_wb(int temp);
    int canon_600_color(int ratio[2], int mar);
    void canon_600_auto_wb();
    void canon_600_coeff();
    void canon_600_load_raw();
    void canon_600_correct();
    int canon_s2is();
    void redcine_load_raw();
    void parse_redcine();

    // getbithuff(int nbits, ushort *huff);
    class getbithuff_t {
    public:
        getbithuff_t(DCraw *p, IMFILE *&i, unsigned &z)
            : parent(p), bitbuf(0), vbits(0), reset(0), ifp(i), zero_after_ff(z)
        {
        }
        unsigned operator()(int nbits, ushort *huff);

    private:
        void derror() { parent->derror(); }
        DCraw *parent;
        unsigned bitbuf;
        int vbits, reset;
        IMFILE *&ifp;
        unsigned &zero_after_ff;
    };
    getbithuff_t getbithuff;

    class nikbithuff_t {
    public:
        explicit nikbithuff_t(IMFILE *&i)
            : bitbuf(0), errors(0), vbits(0), ifp(i)
        {
        }
        void operator()() { bitbuf = vbits = 0; };
        unsigned operator()(int nbits, ushort *huff);
        unsigned errorCount() { return errors; }

    private:
        inline bool derror(bool condition)
        {
            if (UNLIKELY(condition)) {
                ++errors;
            }
            return condition;
        }
        unsigned bitbuf, errors;
        int vbits;
        IMFILE *&ifp;
    };
    nikbithuff_t nikbithuff;

    ushort *make_decoder_ref(const uchar **source);
    ushort *make_decoder(const uchar *source);
    void crw_init_tables(unsigned table, ushort *huff[2]);
    int canon_has_lowbits();
    void canon_load_raw();
    int ljpeg_start(struct jhead *jh, int info_only);
    void ljpeg_end(struct jhead *jh);
    int ljpeg_diff(ushort *huff);
    ushort *ljpeg_row(int jrow, struct jhead *jh);
    void lossless_jpeg_load_raw();
    void ljpeg_idct(struct jhead *jh);

    void canon_sraw_load_raw();
    void adobe_copy_pixel(unsigned row, unsigned col, ushort **rp);
    void lossless_dng_load_raw();
    void lossless_dnglj92_load_raw();
    void packed_dng_load_raw();
    void deflate_dng_load_raw();
    void init_fuji_compr(struct fuji_compressed_params *info);
    void fuji_fill_buffer(struct fuji_compressed_block *info);
    void init_fuji_block(struct fuji_compressed_block *info,
                         const struct fuji_compressed_params *params,
                         INT64 raw_offset, unsigned dsize);
    void copy_line_to_xtrans(struct fuji_compressed_block *info, int cur_line,
                             int cur_block, int cur_block_width);
    void copy_line_to_bayer(struct fuji_compressed_block *info, int cur_line,
                            int cur_block, int cur_block_width);
    void fuji_zerobits(struct fuji_compressed_block *info, int *count);
    void fuji_read_code(struct fuji_compressed_block *info, int *data,
                        int bits_to_read);
    int fuji_decode_sample_even(struct fuji_compressed_block *info,
                                const struct fuji_compressed_params *params,
                                ushort *line_buf, int pos,
                                struct int_pair *grads);
    int fuji_decode_sample_odd(struct fuji_compressed_block *info,
                               const struct fuji_compressed_params *params,
                               ushort *line_buf, int pos,
                               struct int_pair *grads);
    void fuji_decode_interpolation_even(int line_width, ushort *line_buf,
                                        int pos);
    void fuji_extend_generic(ushort *linebuf[_ltotal], int line_width,
                             int start, int end);
    void fuji_extend_red(ushort *linebuf[_ltotal], int line_width);
    void fuji_extend_green(ushort *linebuf[_ltotal], int line_width);
    void fuji_extend_blue(ushort *linebuf[_ltotal], int line_width);
    void xtrans_decode_block(struct fuji_compressed_block *info,
                             const struct fuji_compressed_params *params);
    void fuji_bayer_decode_block(struct fuji_compressed_block *info,
                                 const struct fuji_compressed_params *params);
    void fuji_decode_strip(const struct fuji_compressed_params *info_common,
                           int cur_block, INT64 raw_offset, unsigned dsize);
    void fuji_compressed_load_raw();
    void fuji_decode_loop(const struct fuji_compressed_params *common_info,
                          int count, INT64 *raw_block_offsets,
                          unsigned *block_sizes);
    void parse_fuji_compressed_header();
    void fuji_14bit_load_raw();
    void pentax_load_raw();
    void nikon_load_raw();
    int nikon_is_compressed();
    int nikon_e995();
    int nikon_e2100();
    void nikon_3700();
    int minolta_z2();
    void ppm_thumb();
    void ppm16_thumb();
    void layer_thumb();
    void rollei_thumb();
    void rollei_load_raw();
    int raw(unsigned row, unsigned col);
    void phase_one_flat_field(int is_float, int nc);
    void phase_one_correct();
    void phase_one_load_raw();
    void nikon_yuv_load_raw();
    void kodak_c330_load_raw();
    void kodak_c603_load_raw();
    void samsung3_load_raw();
    void parse_qt(int end);

    // ph1_bithuff(int nbits, ushort *huff);
    class ph1_bithuff_t {
    public:
        ph1_bithuff_t(DCraw *p, IMFILE *i, short &o)
            : order(o), ifp(i), bitbuf(0), vbits(0)
        {
        }
        unsigned operator()(int nbits, ushort *huff);
        unsigned operator()(int nbits);
        unsigned operator()();
        ushort get2()
        {
            uchar str[2] = {0xff, 0xff};
            fread(str, 1, 2, ifp);
            if (order == 0x4949) { /* "II" means little-endian */
                return str[0] | str[1] << 8;
            } else { /* "MM" means big-endian */
                return str[0] << 8 | str[1];
            }
        }

    private:
        inline unsigned get4()
        {
            unsigned val = 0xffffff;
            uchar *str = (uchar *)&val;
            fread(str, 1, 4, ifp);
            if (order == 0x4949) {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
                return val;
#else
                return str[0] | str[1] << 8 | str[2] << 16 | str[3] << 24;
#endif
            } else {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
                return str[0] << 24 | str[1] << 16 | str[2] << 8 | str[3];
#else
                return val;
#endif
            }
        }

        short &order;
        IMFILE *const ifp;
        UINT64 bitbuf;
        int vbits;
    };

    void phase_one_load_raw_c();
    void hasselblad_correct();
    void parse_hasselblad_gain();
    void hasselblad_load_raw();
    void leaf_hdr_load_raw();
    void unpacked_load_raw();
    void sinar_4shot_load_raw();
    void imacon_full_load_raw();
    void packed_load_raw();
    void nokia_load_raw();

    class pana_bits_t {
    public:
        pana_bits_t(IMFILE *i, unsigned &u, unsigned enc)
            : ifp(i), load_flags(u), vbits(0), encoding(enc)
        {
        }
        unsigned operator()(int nbits, unsigned *bytes = nullptr);

    private:
        IMFILE *ifp;
        unsigned &load_flags;
        uchar buf[0x4000];
        int vbits;
        unsigned encoding;
    };

    void panasonicC6_load_raw();
    void panasonicC7_load_raw();

    void canon_rmf_load_raw();
    void panasonic_load_raw();
    void olympus_load_raw();
    void minolta_rd175_load_raw();
    void quicktake_100_load_raw();
    void kodak_radc_load_raw();
    void samsung_load_raw();
    void samsung2_load_raw();
    void kodak_jpeg_load_raw();
    void lossy_dng_load_raw();
    void kodak_dc120_load_raw();
    void eight_bit_load_raw();
    void kodak_yrgb_load_raw();
    void kodak_262_load_raw();
    int kodak_65000_decode(short *out, int bsize);
    void kodak_65000_load_raw();
    void kodak_ycbcr_load_raw();
    void kodak_rgb_load_raw();
    void kodak_thumb_load_raw();

    // sony_decrypt(unsigned *data, int len, int start, int key);
    class sony_decrypt_t {
    public:
        explicit sony_decrypt_t(): p(0) {}
        void operator()(unsigned *data, int len, int start, int key);

    private:
        unsigned pad[128], p;
    };
    sony_decrypt_t sony_decrypt;

    void sony_load_raw();
    void sony_arw_load_raw();
    void sony_arw2_load_raw();
    void sony_arq_load_raw(); // RT
    void smal_decode_segment(unsigned seg[2][2], int holes);
    void smal_v6_load_raw();

    int median4(int *p);
    void fill_holes(int holes);
    void smal_v9_load_raw();

    void foveon_decoder(unsigned size, unsigned code);
    void foveon_thumb();
    void foveon_sd_load_raw();
    void foveon_load_camf();
    void foveon_load_raw();
    void foveon_huff(ushort *huff);
    void foveon_dp_load_raw();
    const char *foveon_camf_param(const char *block, const char *param);
    void *foveon_camf_matrix(unsigned dim[3], const char *name);
    int foveon_fixed(void *ptr, int size, const char *name);
    float foveon_avg(short *pix, int range[2], float cfilt);
    short *foveon_make_curve(double max, double mul, double filt);
    void foveon_make_curves(short **curvep, float dq[3], float div[3],
                            float filt);
    int foveon_apply_curve(short *curve, int i);
    void foveon_interpolate();

    void gamma_curve(double pwr, double ts, int mode, int imax);
    void pseudoinverse(double (*in)[3], double (*out)[3], int size);
    void cam_xyz_coeff(float rgb_cam[3][4], double cam_xyz[4][3]);
    void pre_interpolate();
    void crop_masked_pixels();

    void tiff_get(unsigned base, unsigned *tag, unsigned *type, unsigned *len,
                  unsigned *save);
    void parse_thumb_note(int base, unsigned toff, unsigned tlen);
    int parse_tiff_ifd(int base);
    void parse_makernote(int base, int uptag);
    void get_timestamp(int reversed);
    void parse_exif(int base);
    void parse_gps(int base);
    void romm_coeff(float romm_cam[3][3]);
    void parse_mos(int offset);
    void linear_table(unsigned len);
    void parse_kodak_ifd(int base);

    void parse_minolta(int base);
    int parse_tiff(int base);
    void apply_tiff();
    void parse_external_jpeg();
    void ciff_block_1030();
    void parse_ciff(int offset, int length, int depth);
    void parse_rollei();
    void parse_sinar_ia();
    void parse_phase_one(int base);
    void parse_fuji(int offset);
    int parse_jpeg(int offset);
    void parse_riff();
    void parse_smal(int offset, int fsize);
    void parse_cine();
    char *foveon_gets(int offset, char *str, int len);
    void parse_foveon();

    void adobe_coeff(const char *make, const char *model);
    void simple_coeff(int index);
    short guess_byte_order(int words);
    float find_green(int bps, int bite, int off0, int off1);
    void identify();
    void jpeg_thumb() {} // not needed
    bool dcraw_coeff_overrides(const char make[], const char model[],
                               int iso_speed, short trans[12], int *black_level,
                               int *white_level);
    void shiftXtransMatrix(const int offsy, const int offsx)
    {
        char xtransTemp[6][6];
        for (int row = 0; row < 6; row++) {
            for (int col = 0; col < 6; col++) {
                xtransTemp[row][col] =
                    xtrans[(row + offsy) % 6][(col + offsx) % 6];
            }
        }
        for (int row = 0; row < 6; row++) {
            for (int col = 0; col < 6; col++) {
                xtrans[row][col] = xtransTemp[row][col];
            }
        }
    }

    void nikon_14bit_load_raw(); // ported from LibRaw

    //-----------------------------------------------------------------------------
    // Canon CR3 support ported from LibRaw
    //-----------------------------------------------------------------------------
    /* -*- C++ -*-
     *
     * Copyright (C) 2018-2019 Alexey Danilchenko
     * Copyright (C) 2019 Alex Tutubalin, LibRaw LLC
     *
       Canon CR3 file decoder

     LibRaw is free software; you can redistribute it and/or modify
     it under the terms of the one of two licenses as you choose:

    1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
       (See file LICENSE.LGPL provided in LibRaw distribution archive for
    details).

    2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
       (See file LICENSE.CDDL provided in LibRaw distribution archive for
    details).

    */
    void parse_canon_cr3();
    void selectCRXTrack(short maxTrack);
    int parseCR3(unsigned long long oAtomList, unsigned long long szAtomList,
                 short &nesting, char *AtomNameStack, short &nTrack,
                 short &TrackType);
    int crxDecodePlane(void *p, uint32_t planeNumber);
    void crxLoadDecodeLoop(void *img, int nPlanes);
    void crxConvertPlaneLineDf(void *p, int imageRow);
    void crxLoadFinalizeLoopE3(void *p, int planeHeight);
    void crxLoadRaw();
    int crxParseImageHeader(uchar *cmp1TagData, int nTrack);
    //-----------------------------------------------------------------------------
};

#endif // DCRAW_H
