/*
 *  Graphics.h
 *
 */
#pragma once
#ifndef GRAPHICS_H_
#define GRAPHICS_H_

#include <vector>
#include <string>
#include <stddef.h>
#include "Point.h"
#include "Rectangle.h"

struct Color {
    union
    {
        unsigned int rgba;
        struct
        {
            unsigned char r;
            unsigned char g;
            unsigned char b;
            unsigned char a;
        };
        unsigned char v[4];
    };
    Color( unsigned int rgba = 0 )
        : rgba( rgba )
    { }
    Color( unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha = 255 )
        : r(red), g(green), b(blue), a(alpha)
    { }
    void swapRB()
    {
        unsigned char t = r;
        r = b;
        b = t;
    }
    Color swappedRB() const
    {
        return Color(b, g, r, a);
    }
};


class Image
{
public:
    virtual ~Image() { }
    virtual int getWidth() const = 0;
    virtual int getHeight() const = 0;

    virtual bool setBrightnessCorrection(int Brightness)
    {
        brightness_ = Brightness;
        return true;
    }

    virtual bool setContrastCorrection(int Contrast)
    {
        contrast_ = Contrast;
        return true;
    }

    virtual bool doCorrection() = 0;

    //static Image* createImageFromPNGData( const unsigned char* png );
    static Image* createImageFromRGBA( size_t w, size_t h, const unsigned char* data );

    int brightness() const { return brightness_; }
    int contrast() const { return contrast_; }
private:
    int brightness_;
    int contrast_;
};

template< class T >
class vector4
{
public:
    union {
        struct {
            T x, y, z, w;
        };
        T f[4];
    };

public:
    vector4( const vector4< T >& v )
    : x(v.x), y(v.y), z(v.z), w(v.w)
    {}

    vector4( T x = 0, T y = 0, T z = 0, T w = 0 )
    : x(x), y(y), z(z), w(w)
    {}

    vector4< T > wnorm()
    {
        if( fabs( w ) > 1e-5 )
            return vector4<T>( x / w, y / w, z / w, 1 );
        return *this;
    }

    vector4< T > operator * ( const T& d ) const
    {
        return vector4< T >( x * d, y * d, z * d, w * d );
    }

    vector4< T > operator / ( const T& d ) const
    {
        return vector4< T >( x / d, y / d, z / d, w / d );
    }

    vector4< T > operator + ( const vector4< T >& v ) const
    {
        return vector4< T >( x + v.x, y + v.y, z + v.z, w + v.w );
    }

    vector4< T > operator - ( const vector4< T >& v ) const
    {
        return vector4< T >( x - v.x, y - v.y, z - v.z, w - v.w );
    }

    static vector4< T > convex( const vector4< T >& u, const vector4< T >& v, const T& a )
    {
        const T a1 = 1 - a;
        return vector4< T >( u.x * a1 + v.x * a, u.y * a1 + v.y * a, u.z * a1 + v.z * a, u.w * a1 + v.w * a );
    }
};
/*
template< class T >
class matrix4x4
{
public:
    T m[4][4];
public:
    matrix4x4()
    {
        for( int i = 0; i < 4; i++ )
            for( int j = 0; j < 4; j++ )
                m[i][j] = i == j;
    }

    matrix4x4( T v )
    {
        for( int i = 0; i < 4; i++ )
            for( int j = 0; j < 4; j++ )
                m[i][j] = v;
    }

    matrix4x4( const matrix4x4< T >& mat )
    {
        for( int i = 0; i < 4; i++ )
            for( int j = 0; j < 4; j++ )
                m[i][j] = mat.m[i][j];
    }

    static matrix4x4< T > gen_projection( T near, T far, T left, T right, T bottom, T top )
    {
        matrix4x4< T > m;
        m.m[0][0] = 2 * near / ( right - left );
        m.m[0][1] = 0;
        m.m[0][2] = ( right + left ) / ( right - left );
        m.m[0][3] = 0;

        m.m[1][0] = 0;
        m.m[1][1] = 2 * near / ( top - bottom );
        m.m[1][2] = ( top + bottom ) / ( top - bottom );
        m.m[1][3] = 0;

        m.m[2][0] = 0;
        m.m[2][1] = 0;
        m.m[2][2] = -( far + near ) / ( far - near );
        m.m[2][3] = -2 * far * near / ( far - near );

        m.m[3][0] = 0;
        m.m[3][1] = 0;
        m.m[3][2] = -1;
        m.m[3][3] = 0;
        return m;
    }

    static matrix4x4< T > gen_translate( T x, T y, T z )
    {
        matrix4x4< T > m;
        m.m[0][3] = x;
        m.m[1][3] = y;
        m.m[2][3] = z;
        return m;
    }

    static matrix4x4< T > gen_translate( const vector4< T > v )
    {
        matrix4x4< T > m;
        m.m[0][3] = v.x;
        m.m[1][3] = v.y;
        m.m[2][3] = v.z;
        return m;
    }

    static matrix4x4< T > gen_scale( T x, T y, T z )
    {
        matrix4x4< T > m;
        m.m[0][0] = x;
        m.m[1][1] = y;
        m.m[2][2] = z;
        return m;
    }

    static matrix4x4< T > gen_scale( const vector4< T > v )
    {
        matrix4x4< T > m;
        m.m[0][0] = v.x;
        m.m[1][1] = v.y;
        m.m[2][2] = v.z;
        return m;
    }

    static matrix4x4< T > gen_rotate( T angle, T x, T y, T z )
    {
        T c = cos( angle );
        T s = sin( angle );
        T w = sqrt( x * x + y * y + z * z );
        x /= w; y /= w; z /= w;
        matrix4x4< T > m;
        m.m[0][0] = x * x * ( 1 - c ) + c;
        m.m[0][1] = x * y * ( 1 - c ) - z * s;
        m.m[0][2] = x * z * ( 1 - c ) + y * s;

        m.m[1][0] = y * x * ( 1 - c ) + z * s;
        m.m[1][1] = y * y * ( 1 - c ) + c;
        m.m[1][2] = y * z * ( 1 - c ) - x * s;

        m.m[2][0] = z * x * ( 1 - c ) - y * s;
        m.m[2][1] = z * y * ( 1 - c ) + x * s;
        m.m[2][2] = z * z * ( 1 - c ) + c;

        return m;
    }

    static matrix4x4< T > gen_rotate( T angle, const vector4< T > v )
    {
        return gen_rotate( angle, v.x, v.y, v.z );
    }

    matrix4x4< T >& projection( T near, T far, T left, T right, T bottom, T top )
    {
        matrix4x4< T > p = gen_projection( near, far, left, right, bottom, top );
        return *this *= p;
    }

    matrix4x4< T >& translate( T x, T y, T z )
    {
        matrix4x4< T > t = gen_translate( x, y, z );
        return *this *= t;
    }

    matrix4x4< T >& translate( const vector4< T >& v )
    {
        matrix4x4< T > t = gen_translate( v );
        return *this *= t;
    }

    matrix4x4< T >& scale( T x, T y, T z )
    {
        matrix4x4< T > t = gen_scale( x, y, z );
        return *this *= t;
    }

    matrix4x4< T >& scale( const vector4< T >& v )
    {
        matrix4x4< T > t = gen_scale( v );
        return *this *= t;
    }

    matrix4x4< T >& rotate( T angle, T x, T y, T z )
    {
        matrix4x4< T > r = gen_rotate( angle, x, y, z );
        return *this *= r;
    }

    matrix4x4< T >& rotate( T angle, const vector4< T >& v )
    {
        matrix4x4< T > r = gen_rotate( angle, v );
        return *this *= r;
    }

    matrix4x4< T > operator * ( const matrix4x4< T >& b ) const
    {
        matrix4x4< T > c( 0 );
        for( int i = 0; i <  4; i++ ) {
            for( int j = 0; j < 4; j++ ) {
                for( int k = 0; k < 4; k++ ) {
                    c.m[i][j] += m[i][k] * b.m[k][j];
                }
            }
        }
        return c;
    }

    matrix4x4< T >& operator *= ( const matrix4x4< T >& b )
    {
        return *this = *this * b;
    }

    vector4< T > operator * ( const vector4< T >& v ) const
    {
        vector4< T > vv;
        for( int i = 0; i < 4; i++ ) {
            vv.f[i] = 0;
            for( int j = 0; j < 4; j++ ) {
                vv.f[i] += m[i][j] * v.f[j];
            }
        }
        return vv;
    }
};

typedef vector4< float > vector4f;
typedef vector4< double > vector4d;
typedef matrix4x4< float > matrix4x4f;
typedef matrix4x4< double > matrix4x4d;
*/
class Graphics
{
public:
    enum FONT_STYLE { Regular = 0, Bold = 1, Italic = 2, BoldItalic = 3 };
    enum HORZ_ALIGN { Left, Center, Right };
    enum VERT_ALIGN { Top, Middle, BaseLine, Bottom };
    struct Transform2Matrix
    {
        Transform2Matrix( float a, float b, float c, float d, float x, float y )
            : a(a), b(b), c(c), d(d), x(x), y(y)
        {}

        union {
            struct {
                float a, b; // a b 0
                float c, d; // c d 0
                float x, y; // x y 1
            };
            float m[6];
        };
    };

public:
    virtual ~Graphics() { }
    virtual bool drawImage(const Image* img, int x, int y, int width, int height) = 0;
    virtual void drawLine(int x1, int y1, int x2, int y2) = 0;
    virtual void setColor(Color c) = 0;
    virtual void drawRect(int x, int y, int width, int height) = 0;
    virtual void fillRect(int x, int y, int width, int height) = 0;
    virtual void drawEllipse(int x, int y, int width, int height) = 0;
    virtual void fillEllipse(int x, int y, int width, int height) = 0;
    virtual void drawPolyline( const std::vector<int>& xCoords, const std::vector<int>& yCoords, int count ) = 0;
    virtual void drawPolyline( const std::vector<Point2>& points ) = 0;
    virtual void fillPolygon( const std::vector<int>& xCoords, const std::vector<int>& yCoords, int count ) = 0;
    virtual void fillPolygon( const std::vector<Point2>& points ) = 0;

    virtual float getStrokeWidth() const = 0;
    virtual void setStrokeWidth( float strokeWidth ) = 0;

    virtual void setFont( const char* fontName, float fontSize, FONT_STYLE fontStyle ) = 0;
    virtual float fontAscent() const = 0;
    virtual float fontDescent() const = 0;
    virtual Point2 measureText( const std::string& text ) = 0;
    virtual Rectangle2 drawString( const std::string& text, float x, float y, HORZ_ALIGN horzAlign = Left, VERT_ALIGN vertAlign = BaseLine ) = 0;

    virtual void pushState() = 0;
    virtual void popState() = 0;
    virtual void setTransform2Identity() = 0;
    virtual void setTransform2( Transform2Matrix matrix ) = 0;
    virtual Transform2Matrix getTransform2() = 0;
    virtual void multiplyTransform2( Transform2Matrix matrix ) = 0;
    virtual void scaleTransform2( float sx, float sy ) = 0;
    virtual void rotateTransform2( float angle ) = 0; // angle in degrees
    virtual void translateTransform2( float dx, float dy ) = 0;
};

class CursorCtrl
{
    CursorCtrl() {}
public:
    enum SHAPE { Arrow, Finger, Move, Moving };
    static void setShape( SHAPE Shape );
    //virtual ~CursorCtrl() {}
};


#endif
