#ifndef Image_H
#define Image_H

#include "Array.h"
#include "Point.h"
#include "Drawing.h"

namespace Framework
{
    class Image; //! from this file
#ifdef WIN32
    class VScrollBar;    //! Scroll.h
    class HScrollBar;    //! Scroll.h
    class Border;        //! Border.h
    struct MouseEvent; //! MouseEvent.h
    class Text;          //! Text.h
#endif

    //! Manages an image as an array of pixel colors in A8R8G8B8 format
    //! that can be drawn to
    class Image : public virtual ReferenceCounter
    {
    private:
        int* fc;
        bool delFc;
        Point size;
        Point* drawOff;
        Point* dPosA;
        Point* dSizeA;
        int doa;
        unsigned char* alpha;
        int alphaCount;
        bool rend;
        bool alpha3D;

        //! private
        inline void alphaPixelP(int x, int y, int f);
        inline void alphaPixelP3D(int x, int y, int f);
        inline void alphaPixelP(int& fc, int f);
        inline void alphaPixelP3D(int& fc, int f);
        inline void alphaPixelAssozP(int& fc, int f);
        inline char getOutCode(Point p) const;
        void drawFlatTriangle(
            int y1, int y2, float m1, float b1, float m2, float b2, int color);
        void drawFlatTriangleTexture(int y1,
            int y2,
            double m1,
            double b1,
            double m2,
            double b2,
            double tx1,
            double ty1,
            double tx2,
            double ty2,
            double tx_1o,
            double ty_1o,
            double tx_2o,
            double ty_2o,
            double txf,
            double tyf,
            const Image& textur);
        void drawFlatTriangleAlpha(
            int y1, int y2, float m1, float b1, float m2, float b2, int color);
        void drawFlatTriangleTextureAlpha(int y1,
            int y2,
            double m1,
            double b1,
            double m2,
            double b2,
            double tx1,
            double ty1,
            double tx2,
            double ty2,
            double tx_1o,
            double ty_1o,
            double tx_2o,
            double ty_2o,
            double txf,
            double tyf,
            const Image& textur);
        void drawLineHTexture(Vec2<double> p,
            double length,
            Vec2<double> ta,
            Vec2<double> tb,
            double txo,
            double tyo,
            const Image& textur);
        void drawLineHTextureAlpha(Vec2<double> p,
            double length,
            Vec2<double> ta,
            Vec2<double> tb,
            double txo,
            double tyo,
            const Image& textur);

    public:
        //! Constructor
        //! \param options 1 if the image should create buffers for drawing,
        //! such as for temporary drawing area boundaries and
        //! transparency filters
        //!  Uses about 50 kb more memory per image
        DLLEXPORT Image(bool options = 0);
        //! Destructor
        DLLEXPORT ~Image();
        //! Checks whether a rectangle is fully or partially within the drawing
        //! area.
        //!  return 0 if the rectangle is not in the drawing area, 1 otherwise
        DLLEXPORT bool isAreaDrawable(int x, int y, int width, int height);
        //! If this flag is set, when alpha blending and the previous color
        //! is 0, the new color is only copied with its alpha value.
        //! This is useful for use in the 3D screen, where the drawn image
        //! is later displayed using alpha blending. The flag is set
        //! automatically in the 3D screen
        DLLEXPORT void setAlpha3D(bool erlaubt);
        //! Sets the minimum transparency of everything drawn next,
        //! until the corresponding releaseAlpha() call is made.
        DLLEXPORT void setAlpha(unsigned char alpha);
        //! Removes the last alpha limit set with setAlpha()
        DLLEXPORT void releaseAlpha();
        //! Sets a pointer to the pixels to draw into
        //! \param buffer An array with A8R8G8B8 color values
        //! \param deleteBuffer 1 if the array should be deleted by the image
        //! \param Width The width in pixels
        //! \param height The height in pixels
        DLLEXPORT void setPixelBuffer(
            int* buffer, bool deleteBuffer, int Width, int height);
        //! Creates a new image
        //! \param Width The width of the image in pixels
        //! \param height The height of the image in pixels
        //! \param fillColor The initial color all pixels are set to
        DLLEXPORT void newImage(int Width, int height, int fillColor);
        //! Blends a color onto a specific pixel using alpha blending.
        //! Does not respect the drawing area boundary or scroll offset
        //! \param x The X coordinate of the pixel
        //! \param y The Y coordinate of the pixel
        //! \param f The color in A8R8G8B8 format to be blended
        DLLEXPORT void alphaPixel2D(int x, int y, int f);
        DLLEXPORT void alphaPixel3D(int x, int y, int f);
        //! Blends a color onto a specific pixel using alpha blending.
        //! Does not respect the drawing area boundary or scroll offset
        //! \param i The index of the pixel in the pixel array
        //! \param f The color in A8R8G8B8 format to be blended
        DLLEXPORT void alphaPixel2D(int i, int f);
        DLLEXPORT void alphaPixel3D(int i, int f);
        //! Blends a color onto a specific pixel using alpha blending
        //! \param x The X coordinate of the pixel
        //! \param y The Y coordinate of the pixel
        //! \param f The color in A8R8G8B8 format to be blended
        DLLEXPORT void alphaPixelDP2D(int x, int y, int f);
        DLLEXPORT void alphaPixelDP3D(int x, int y, int f);
        //! Blends a color onto a specific pixel using alpha blending
        //! \param i The index of the pixel in the pixel array
        //! \param f The color in A8R8G8B8 format to be blended
        DLLEXPORT void alphaPixelDP2D(int i, int f);
        DLLEXPORT void alphaPixelDP3D(int i, int f);
        //! Sets the color of a specific pixel
        //! \param x The X coordinate of the pixel
        //! \param y The Y coordinate of the pixel
        //! \param f The new color in A8R8G8B8 format
        DLLEXPORT void setPixelDP(int x, int y, int f);
        //! Sets the color of a specific pixel
        //! \param i The index of the pixel in the pixel array
        //! \param f The new color in A8R8G8B8 format
        DLLEXPORT void setPixelDP(int i, int f);
        //! Sets the color of all pixels in the image
        //! \param f The new color
        DLLEXPORT void setColor(int f);
        //! Sets the colors of all pixels in a rectangle
        //! \param x The X coordinate
        //! \param y The Y coordinate
        //! \param b The width of the rectangle
        //! \param h The height of the rectangle
        //! \param fc The color in A8R8G8B8 format
        DLLEXPORT void fillRegion(int x, int y, int b, int h, int fc);
        //! Blends a color with alpha blending into a rectangle
        //! \param x The X coordinate
        //! \param y The Y coordinate
        //! \param b The width of the rectangle
        //! \param h The height of the rectangle
        //! \param fc The color in A8R8G8B8 format
        DLLEXPORT void alphaRegion(int x, int y, int b, int h, int fc);
        //! Draws a horizontal line
        //! \param x The X coordinate of the starting point
        //! \param y The Y coordinate of the starting point
        //! \param length The length of the line
        //! \param fc The color in A8R8G8B8 format
        DLLEXPORT void drawLineH(int x, int y, int length, int fc);
        //! Draws a vertical line
        //! \param x The X coordinate of the starting point
        //! \param y The Y coordinate of the starting point
        //! \param length The length of the line
        //! \param fc The color in A8R8G8B8 format
        DLLEXPORT void drawLineV(int x, int y, int length, int fc);
        //! Draws a horizontal line with alpha blending
        //! \param x The X coordinate of the starting point
        //! \param y The Y coordinate of the starting point
        //! \param length The length of the line
        //! \param fc The color in A8R8G8B8 format
        DLLEXPORT void drawLineHAlpha(int x, int y, int length, int fc);
        //! Draws a vertical line with alpha blending
        //! \param x The X coordinate of the starting point
        //! \param y The Y coordinate of the starting point
        //! \param length The length of the line
        //! \param fc The color in A8R8G8B8 format
        DLLEXPORT void drawLineVAlpha(int x, int y, int length, int fc);
        //! Draws a bordered line
        //! \param a The starting point of the line
        //! \param b The ending point of the line
        //! \param bc The border color in A8R8G8B8 format
        //! \param fc The color in A8R8G8B8 format
        DLLEXPORT void drawLineBordered(Point a, Point b, int bc, int fc);
        //! Draws a bordered line with alpha blending
        //! \param a The starting point of the line
        //! \param b The ending point of the line
        //! \param bc The border color in A8R8G8B8 format
        //! \param fc The color in A8R8G8B8 format
        DLLEXPORT void drawLineBorderedAlpha(Point a, Point b, int bc, int fc);
        //! Draws a line
        //! \param a The starting point of the line
        //! \param b The ending point of the line
        //! \param fc The color in A8R8G8B8 format
        DLLEXPORT void drawLine(Point a, Point b, int fc);
        //! Draws a line with alpha blending
        //! \param a The starting point of the line
        //! \param b The ending point of the line
        //! \param fc The color in A8R8G8B8 format
        DLLEXPORT void drawLineAlpha(Point a, Point b, int fc);
        //! Fills a circle with a color. (Unfinished)
        //! \param xOff The X coordinate of the circle center
        //! \param yOff The Y coordinate of the circle center
        //! \param r The radius of the circle in pixels
        //! \param fc The color in A8R8G8B8 format
        DLLEXPORT void fillCircle(int xOff, int yOff, int r, int fc);
        //! Draws the outline of a circle
        //! \param xOff The X coordinate of the circle center
        //! \param yOff The Y coordinate of the circle center
        //! \param r The radius of the circle in pixels
        //! \param fc The color in A8R8G8B8 format
        DLLEXPORT void drawCircle(int xOff, int yOff, int r, int fc);
        //! Draws the outline of a circle with alpha blending
        //! \param xOff The X coordinate of the circle center
        //! \param yOff The Y coordinate of the circle center
        //! \param r The radius of the circle in pixels
        //! \param fc The color in A8R8G8B8 format
        DLLEXPORT void drawCircleAlpha(int xOff, int yOff, int r, int fc);
        //! Draws an image into a specific area without scaling
        //! \param x The X coordinate of the top left corner of the target area
        //! \param y The Y coordinate of the top left corner of the target area
        //! \param br The width of the target area
        //! \param hi The height of the target area
        //! \param zImage The image to be drawn
        DLLEXPORT void drawImage(
            int x, int y, int br, int hi, const Image& zImage);
        //! Draws an image into a specific area without scaling with alpha
        //! blending
        //! \param x The X coordinate of the top left corner of the target area
        //! \param y The Y coordinate of the top left corner of the target area
        //! \param br The width of the target area
        //! \param hi The height of the target area
        //! \param zImage The image to be drawn
        DLLEXPORT void alphaImage(
            int x, int y, int br, int hi, const Image& zImage);
        //! Draws an image into a specific area without scaling with associative
        //! alpha blending
        //! \param x The X coordinate of the top left corner of the target area
        //! \param y The Y coordinate of the top left corner of the target area
        //! \param br The width of the target area
        //! \param hi The height of the target area
        //! \param zImage The image to be drawn
        DLLEXPORT void alphaImageAssoc(
            int x, int y, int br, int hi, const Image& zImage);
        //! Draws an image rotated 90 degrees clockwise into a specific area
        //! without scaling
        //! \param x The X coordinate of the top left corner of the target area
        //! \param y The Y coordinate of the top left corner of the target area
        //! \param br The width of the target area
        //! \param hi The height of the target area
        //! \param zImage The image to be drawn
        DLLEXPORT void drawImage90(
            int x, int y, int br, int hi, const Image& zImage);
        //! Draws an image rotated 90 degrees clockwise into a specific area
        //! without scaling with alpha blending
        //! \param x The X coordinate of the top left corner of the target area
        //! \param y The Y coordinate of the top left corner of the target area
        //! \param br The width of the target area
        //! \param hi The height of the target area
        //! \param zImage The image to be drawn
        DLLEXPORT void alphaImage90(
            int x, int y, int br, int hi, const Image& zImage);
        //! Draws an image rotated 180 degrees into a specific area
        //! without scaling
        //! \param x The X coordinate of the top left corner of the target area
        //! \param y The Y coordinate of the top left corner of the target area
        //! \param br The width of the target area
        //! \param hi The height of the target area
        //! \param zImage The image to be drawn
        DLLEXPORT void drawImage180(
            int x, int y, int br, int hi, const Image& zImage);
        //! Draws an image rotated 180 degrees into a specific area
        //! without scaling with alpha blending
        //! \param x The X coordinate of the top left corner of the target area
        //! \param y The Y coordinate of the top left corner of the target area
        //! \param br The width of the target area
        //! \param hi The height of the target area
        //! \param zImage The image to be drawn
        DLLEXPORT void alphaImage180(
            int x, int y, int br, int hi, const Image& zImage);
        //! Draws an image rotated 270 degrees clockwise into a specific area
        //! without scaling
        //! \param x The X coordinate of the top left corner of the target area
        //! \param y The Y coordinate of the top left corner of the target area
        //! \param br The width of the target area
        //! \param hi The height of the target area
        //! \param zImage The image to be drawn
        DLLEXPORT void drawImage270(
            int x, int y, int br, int hi, const Image& zImage);
        //! Draws an image rotated 270 degrees clockwise into a specific area
        //! without scaling with alpha blending
        //! \param x The X coordinate of the top left corner of the target area
        //! \param y The Y coordinate of the top left corner of the target area
        //! \param br The width of the target area
        //! \param hi The height of the target area
        //! \param zImage The image to be drawn
        DLLEXPORT void alphaImage270(
            int x, int y, int br, int hi, const Image& zImage);
        //! Draws an image into a specific area with scaling
        //! \param x The X coordinate of the top left corner of the target area
        //! \param y The Y coordinate of the top left corner of the target area
        //! \param br The width of the target area
        //! \param hi The height of the target area
        //! \param zImage The image to be drawn
        DLLEXPORT void drawImageScaled(
            int x, int y, int br, int hi, const Image& zImage);
        //! Draws an image into a specific area with scaling and alpha blending
        //! \param x The X coordinate of the top left corner of the target area
        //! \param y The Y coordinate of the top left corner of the target area
        //! \param br The width of the target area
        //! \param hi The height of the target area
        //! \param zImage The image to be drawn
        DLLEXPORT void alphaImageScaled(
            int x, int y, int br, int hi, const Image& zImage);
        //! Fills a triangle with a specific color
        //! \param a A corner of the triangle
        //! \param b A corner of the triangle
        //! \param c A corner of the triangle
        //! \param color The color in A8R8G8B8 format
        DLLEXPORT void drawTriangle(Point a, Point b, Point c, int color);
        //! Fills a triangle with a specific texture
        //! \param a A corner of the triangle
        //! \param b A corner of the triangle
        //! \param c A corner of the triangle
        //! \param ta The coordinates of a in the texture
        //! \param tb The coordinates of b in the texture
        //! \param tc The coordinates of c in the texture
        //! \param textur The image to use as texture
        DLLEXPORT void drawTriangleTexture(Point a,
            Point b,
            Point c,
            Point ta,
            Point tb,
            Point tc,
            const Image& textur);
        //! Fills a triangle with a specific color with alpha blending
        //! \param a A corner of the triangle
        //! \param b A corner of the triangle
        //! \param c A corner of the triangle
        //! \param color The color in A8R8G8B8 format
        DLLEXPORT void drawTriangleAlpha(Point a, Point b, Point c, int color);
        //! Fills a triangle with a specific texture with alpha blending
        //! \param a A corner of the triangle
        //! \param b A corner of the triangle
        //! \param c A corner of the triangle
        //! \param ta The coordinates of a in the texture
        //! \param tb The coordinates of b in the texture
        //! \param tc The coordinates of c in the texture
        //! \param textur The image to use as texture
        DLLEXPORT void drawTriangleTextureAlpha(Point a,
            Point b,
            Point c,
            Point ta,
            Point tb,
            Point tc,
            const Image& textur);
        //! Replaces a specific color with transparency
        DLLEXPORT void replaceColorWithAlpha(int color);
        //! Restricts the drawing area until the next call of
        //! releaseDrawOptions(). Everything outside the area is automatically
        //! ignored. If the drawing area extends beyond an existing drawing area,
        //! it is automatically clipped to the existing one.
        //! \param pos The coordinates of the top left corner of the drawing area
        //! \param gr The size of the drawing area
        //! \return 1 if the new drawing area is fully or partially within the
        //! existing drawing area. If 0 is returned, the drawing area was not set
        //! and releaseDrawOptions() does not need to be called
        DLLEXPORT bool setDrawOptions(const Point& pos, const Point& gr);
        //! Restricts the drawing area until the next call of
        //! releaseDrawOptions(). Everything outside the area is automatically
        //! ignored. If the drawing area extends beyond an existing drawing area,
        //! it is automatically clipped to the existing one.
        //! \param x The X coordinate of the top left corner of the drawing area
        //! \param y The Y coordinate of the top left corner of the drawing area
        //! \param br The width of the drawing area
        //! \param hi The height of the drawing area
        //! \return 1 if the new drawing area is fully or partially within the
        //! existing drawing area. If 0 is returned, the drawing area was not set
        //! and releaseDrawOptions() does not need to be called
        DLLEXPORT bool setDrawOptions(int x, int y, int br, int hi);
        //! Restricts the drawing area until the next call of
        //! releaseDrawOptions(). Everything outside the area is automatically
        //! ignored. The drawing area may extend beyond an existing drawing area.
        //! \param pos The coordinates of the top left corner of the drawing area
        //! \param gr The size of the drawing area
        //! \return 1 if the new drawing area is fully or partially within the
        //! image. If 0 is returned, the drawing area was not set and
        //! releaseDrawOptions() does not need to be called
        DLLEXPORT bool setDrawOptionsErzwingen(
            const Point& pos, const Point& gr);
        //! Restricts the drawing area until the next call of
        //! releaseDrawOptions(). Everything outside the area is automatically
        //! ignored. The drawing area may extend beyond an existing drawing area.
        //! \param x The X coordinate of the top left corner of the drawing area
        //! \param y The Y coordinate of the top left corner of the drawing area
        //! \param br The width of the drawing area
        //! \param hi The height of the drawing area
        //! \return 1 if the new drawing area is fully or partially within the
        //! image. If 0 is returned, the drawing area was not set and
        //! releaseDrawOptions() does not need to be called
        DLLEXPORT bool setDrawOptionsErzwingen(int x, int y, int br, int hi);
        //! Resets draw options to image size
        DLLEXPORT void setDrawOptionsReset();
        //! Sets coordinates that are subtracted from positions in drawing
        //! functions. A drawing area should have been set with setDrawOptions
        //! beforehand. The values are reset with the call of
        //! releaseDrawOptions().
        //! \param xOff A value subtracted from all X coordinates
        //! \param yOff A value subtracted from all Y coordinates
        DLLEXPORT void addScrollOffset(int xOff, int yOff);
        //! Removes the last set drawing area restriction and all scroll offsets
        //! set since then. Restores the previous drawing area and its
        //! associated scroll offsets
        DLLEXPORT void releaseDrawOptions();
        //! Returns whether the image has changed since the last call of this
        //! function
        DLLEXPORT bool getNeedRender();
        //! Returns the array of A8R8G8B8 pixel color values.
        //! The index of a pixel is calculated as x + y * image width
        DLLEXPORT int* getBuffer() const;
        //! Returns the A8R8G8B8 color value of a pixel
        //! \param x The X coordinate of the pixel
        //! \param y The Y coordinate of the pixel
        DLLEXPORT int getPixel(int x, int y) const;
        //! Returns the size of the image
        DLLEXPORT const Point& getSize() const;
        //! Returns the width of the image
        DLLEXPORT int getWidth() const;
        //! Returns the height of the image
        DLLEXPORT int getHeight() const;
        //! Returns the current minimum transparency value
        DLLEXPORT unsigned char getAlpha() const;
        //! Returns the coordinates of the top left corner of the current
        //! drawing area
        DLLEXPORT const Point& getDrawPos() const;
        //! Returns the size of the current drawing area
        DLLEXPORT const Point& getDrawGr() const;
        //! Returns the coordinates added to all positions before drawing
        DLLEXPORT const Point& getDrawOff() const;
        //! If this flag is set, when alpha blending and the previous color
        //! is 0, the new color is only copied with its alpha value.
        //! This is useful for use in the 3D screen, where the drawn image
        //! is later displayed using alpha blending. The flag is set
        //! automatically in the 3D screen
        DLLEXPORT bool hasAlpha3D() const;
        //! Calculates the average color of all pixels in the image
        DLLEXPORT int getAverageColor() const;
    };

    //! A 2D GUI Framework drawing that displays an image.
    class ImageView : public DrawableBackground
    {
    public:
        class Style : public DrawableBackground::Style
        {
        public:
            //! If this flag is set, alpha blending is used when drawing
            //! the image
            static const __int64 Alpha = 0x1000;
            //! If this flag is set, the image is scaled to the drawing area
            static const __int64 Scaled = 0x2000;

            //! The normal style of an image drawing consisting of HScroll,
            //! Visible, Allowed, Border, VScroll
            static const __int64 normal
                = HScroll | Visible | Allowed | Border | VScroll;
        };

    private:
        Image* bild;

    protected:
        //! Processes a mouse event. Called automatically by the framework.
        //! \param me The event
        DLLEXPORT void doMouseEvent(MouseEvent& me, bool userRet) override;

    public:
        //! Constructor
        DLLEXPORT ImageView();
        //! Destructor
        DLLEXPORT virtual ~ImageView();
        //! Sets a pointer to the image to be displayed
        //! \param b The image
        DLLEXPORT void setImageZ(Image* b);
        //! Sets the image to be displayed. The image content is copied
        //! \param b The image
        DLLEXPORT void setImage(Image* b);
        //! Updates the drawing background
        //! \param tickVal The elapsed time in seconds since the last call
        //! of this function
        //! \return 1 if the image needs to be redrawn. 0 otherwise
        DLLEXPORT bool tick(double tickVal) override;
        //! Draws the drawing into a specific image
        //! \param zRObj The image to draw into
        DLLEXPORT void render(Image& zRObj) override;
        //! Returns the displayed image
        DLLEXPORT Image* getImage() const;
        //! Returns the displayed image without increased reference counter
        DLLEXPORT Image* zImage() const;
        //! Copies the complete drawing so it can be modified without
        //! affecting the original
        DLLEXPORT Drawable* duplicate() const override;
    };

#ifdef WIN32
    //! Loads an image from a .bmp, .jpg, .gif or .png file
    //! \param pfad The path to the image file
    //! \param zError A pointer to a Text object where a possible error is
    //! returned
    //! \return The loaded image
    DLLEXPORT Image* loadImage(const char* pfad, Text* zError);
#endif
} // namespace Framework
#endif