#ifndef __AMSCPPIMGLIB4_HPP__
#define __AMSCPPIMGLIB4_HPP__

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdint.h>

#include <vector>
#include <thread>

namespace ams
{

    class amsimage;
    class amsbitplane;

    static const bool amsimage_usethreads = 1; //use threads for handling image initialization/copying
    static const int amsimage_maxthreads = 20;  //max number of threads to use
    static const int amsimage_threadpsz = 5000; //when to break into threaded operation
    static const int amsimage_success = 0;
    static const int amsimage_failure = -1;
    


    struct amspixel
    {
    public:
        uint8_t R,G,B,A;
        
        amspixel();
        amspixel(uint8_t _R ,uint8_t _G, uint8_t _B, uint8_t _A);
        uint8_t& operator[](const int ind);
        const uint8_t& operator[](const int ind) const;
    };

    class amsimage
    {
        public:
        int Nx,Ny; //image extent
        //int Nc; //number of color-planes  (1,3,4)
        //Assume we're talking about an RGBA image internally, greyscale is what bitplane is for
        uint8_t *data; //[(x + width*y)*4]

        int& width;  //aliases to Nx,Ny
        int& height;

        amsimage();
        ~amsimage();
        amsimage(const amsimage& other);
        amsimage(amsimage&& other) noexcept;
        amsimage& operator=(const amsimage& other);
        amsimage& operator=(amsimage&& other) noexcept;

        int resize(int _Nx, int _Ny);

        amsimage subimage(int I0, int J0, int I1, int J1) const;
        amsimage transpose() const;
        amsimage rotcw() const;
        amsimage rotccw() const;
        amsimage flipx() const;
        amsimage flipy() const;

        amspixel get_pixel(int I,int J) const;
        int set_pixel(int I,int J,const amspixel pix);
        int set_pixel(int I,int J,uint8_t R, uint8_t G, uint8_t B, uint8_t A);
        uint8_t& operator[](int ind);
        const uint8_t& operator[](int ind) const;
        uint8_t& operator()(int Nc, int I, int J);
        const uint8_t& operator()(int Nc, int I, int J) const;




        void clear();
        void setall(amspixel color);
        int setallplane(int Ncp, uint8_t val);

        int get_colorplane(int Ncp, amsbitplane* bp);
        int set_colorplane(int Ncp, const amsbitplane* bp);
        
        //applies image with alpha blending
        int apply_image(int I0, int J0, amsimage *img);
        //applied color to any pixels where the bitplane's value is >= thresh
        int apply_bitplane_nz(int I0, int J0, amsbitplane* bp, amspixel color, uint8_t thresh);
        
        //rescales the image with linear interpolation
        int rescale(int nnx, int nny);
    };

    int read_image(const char *fname, amsimage* image);
    int write_image(const char *fname, amsimage* image);

    class amsbitplane
    {
        public:
        int Nx,Ny;
        uint8_t *data; //[x+width*y]

        amsbitplane();
        ~amsbitplane();
        amsbitplane(const amsbitplane& other);
        amsbitplane(amsbitplane&& other) noexcept;
        amsbitplane& operator=(const amsbitplane& other);
        amsbitplane& operator=(amsbitplane&& other) noexcept;

        int resize(int _Nx, int _Ny);
        void transpose();
        void rotcw();
        void rotccw();
        void flipx();
        void flipy();
        
        uint8_t get(int I, int J) const;
        int set(int I, int J, uint8_t val);

        uint8_t& at(int I, int J);
        uint8_t& operator()(const int I, const int J);
        const uint8_t& operator()(const int I, const int J) const;
        uint8_t& operator[](const long I);
        const uint8_t& operator[](const long I) const;
        

        void clear();

        //rescales the image with linear interpolation
        int rescale(int nnx, int nny);
    };


    //Tests//

    void amscppimglib_test1();

}; //end namespace ams

#include <amscppimglib4/amscppimglib4_tests.hpp>

#endif