#include "epd_gui.h"
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include "7inch5_epd.h"

IMAGE Image;
/******************************************************************************
function:	Create Image
parameter:
    image   :   Pointer to the image cache
    width   :   The width of the picture
    height  :   The height of the picture
    Color   :   Whether the picture is inverted
******************************************************************************/

void Image_Init(uint8_t *image, uint16_t width, uint16_t height, uint16_t Rotate, uint16_t Color)
{
    Image.img = NULL;
    Image.img = image;

    Image.mem_w = width;
    Image.mem_h = height;
    Image.color = Color;    
    Image.byte_w = (width % 8 == 0)? (width / 8 ): (width / 8 + 1);
    Image.byte_h = height;    
    //printf("widthByte = %d, heightByte = %d\r\n", Image.byte_w, Image.byte_h);
    //printf(" EPD_width / 8 = %d\r\n",  122 / 8);
   
    Image.rotate = Rotate;
    Image.mirror = MIRROR_NONE;
    
    if(Rotate == ROTATE_0 || Rotate == ROTATE_180) 
		{
        Image.w = width;
        Image.h = height;
    } 
		else 
		{
        Image.w = height;
        Image.h = width;
    }
}

/******************************************************************************
function:	Select Image
parameter:
    image   :   Pointer to the image cache
******************************************************************************/
void Gui_SelectImage(uint8_t *image)
{
    Image.img = image;
}

/******************************************************************************
function:	Select Image Rotate
parameter:
    Rotate   :   0,90,180,270
******************************************************************************/
void Gui_SetRotate(uint16_t rotate)
{
    if(rotate == ROTATE_0 || rotate == ROTATE_90 || rotate == ROTATE_180 || rotate == ROTATE_270) 
		{
        //Debug("Set image Rotate %d\r\n", Rotate);
        Image.rotate = rotate;
    } 
		else 
		{
        //Debug("rotate = 0, 90, 180, 270\r\n");
    }
}

/******************************************************************************
function:	Select Image mirror
parameter:
mirror   :       Not mirror,Horizontal mirror,Vertical mirror,Origin mirror
******************************************************************************/
void Gui_SetMirror(uint8_t mirror)
{
    if(mirror == MIRROR_NONE || mirror == MIRROR_HORIZONTAL || 
        mirror == MIRROR_VERTICAL || mirror == MIRROR_ORIGIN) 
		{
        //Debug("mirror image x:%s, y:%s\r\n",(mirror & 0x01)? "mirror":"none", ((mirror >> 1) & 0x01)? "mirror":"none");
        Image.mirror = mirror;
    } 
		else 
		{
        //Debug("mirror should be MIRROR_NONE, MIRROR_HORIZONTAL, \
        MIRROR_VERTICAL or MIRROR_ORIGIN\r\n");
    }    
}

/******************************************************************************
function:	Draw Pixels
parameter:
       x  :   At point X
       x  :   At point Y
    color :   Guied colors
******************************************************************************/
void Gui_SetPixel(uint16_t x, uint16_t y, uint16_t color)
{
	uint16_t xx, yy;
	uint32_t Addr;
	uint8_t Rdata;
    if(x > Image.w || y > Image.h)
		{
        //Debug("Exceeding display boundaries\r\n");
        return;
    }      
    
    switch(Image.rotate) 
		{
				case 0:
						xx = x;
						yy = y;  
						break;
				case 90:
						xx = Image.mem_w - y - 1;
						yy = x;
						break;
				case 180:
						xx = Image.mem_w - x - 1;
						yy = Image.mem_h - y - 1;
						break;
				case 270:
						xx = y;
						yy = Image.mem_h - x - 1;
						break;
				
				default:
						return;
    }
    
    switch(Image.mirror) 
		{
				case MIRROR_NONE:
						break;
				case MIRROR_HORIZONTAL:
						xx = Image.mem_w - xx - 1;
						break;
				case MIRROR_VERTICAL:
						yy = Image.mem_h - yy - 1;
						break;
				case MIRROR_ORIGIN:
						xx = Image.mem_w - xx - 1;
						yy = Image.mem_h - yy - 1;
						break;
				default:
						return;
    }

    if(xx > Image.mem_w || yy > Image.mem_h)
		{
        //Debug("Exceeding display boundaries\r\n");
        return;
    }
    
    Addr = xx / 8 + yy * Image.byte_w;
		color = color%4;
    Rdata = Image.img[Addr];
    if(color == BLACK)
        Image.img[Addr] = Rdata & ~(0x80 >> (xx % 8));
    else
        Image.img[Addr] = Rdata | (0x80 >> (xx % 8));
}

/******************************************************************************
function:	Clear the color of the picture
parameter:
Color   :   Guied colors
******************************************************************************/
void Gui_Clear(uint16_t color)
{
	  uint16_t x,y;
	  uint32_t addr;
    for (y = 0; y < Image.byte_h; y++) 
	  {
        for (x = 0; x < Image.byte_w; x++ ) 
		    {//8 pixel =  1 byte
            addr = x + y*Image.byte_w;
            Image.img[addr] = (color<<6)|(color<<4)|(color<<2)|color;
        }
    }
}

/******************************************************************************
function:	Clear the color of a window
parameter:
    x1 :   x starting point
    y1 :   Y starting point
    x2   :   x end point
    y2   :   y end point
******************************************************************************/
void Gui_ClearWindows(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color)
{
    uint16_t x, y;
    for (y = y1; y < y2; y++) 
	  {
        for (x = x1; x < x2; x++) 
			  {//8 pixel =  1 byte
            Gui_SetPixel(x, y, color);
        }
    }
}

/******************************************************************************
function:	Draw Point(x, y) Fill the color
parameter:
    x		:   The xpoint coordinate of the point
    y		:   The ypoint coordinate of the point
    color		:   Set color
    Dot_Pixel	:	point size
******************************************************************************/
void Gui_Draw_Point(uint16_t x, uint16_t y, uint16_t color, DOT_PIXEL Dot_Pixel, DOT_STYLE Dot_Style)
{
	  int16_t xd , yd;
    if (x > Image.w || y > Image.h) 
		{
        //Debug("Gui_Draw_Point Input exceeds the normal display range\r\n");
        return;
    }
    if (Dot_Style == AROUND) 
		{
        for (xd = 0; xd < 2 * Dot_Pixel - 1; xd++) 
		    {
            for (yd = 0; yd < 2 * Dot_Pixel - 1; yd++) 
					  {
                if(x + xd - Dot_Pixel < 0 || y + yd - Dot_Pixel < 0)
                    break;
                Gui_SetPixel(x + xd - Dot_Pixel, y + yd - Dot_Pixel, color);
            }
        }
    } 
		else 
		{
        for (xd = 0; xd <  Dot_Pixel; xd++) 
			  {
            for (yd = 0; yd <  Dot_Pixel; yd++) 
					  {
                Gui_SetPixel(x + xd - 1, y + yd - 1, color);
            }
        }
    }
}

/******************************************************************************
function:	Draw a line of arbitrary slope use Bresenham algorithm 
parameter:
    x1 ：Starting Xpoint point coordinates
    y1 ：Starting Ypoint point coordinates
    x2   ：End point Xpoint coordinate
    y2   ：End point Ypoint coordinate
    color  ：The color of the line segment
******************************************************************************/
void Gui_Draw_Line(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend,
                    uint16_t Color, DOT_PIXEL Dot_Pixel,LINE_STYLE Line_Style)
{    uint16_t Xpoint, Ypoint;
	   int dx, dy;
	   int XAddway,YAddway;
	   int Esp;
	   char Dotted_Len;
    if (Xstart > Image.w || Ystart > Image.h ||
        Xend > Image.w || Yend > Image.h) {
        //Debug("Paint_DrawLine Input exceeds the normal display range\r\n");
        return;
    }

    Xpoint = Xstart;
    Ypoint = Ystart;
    dx = (int)Xend - (int)Xstart >= 0 ? Xend - Xstart : Xstart - Xend;
    dy = (int)Yend - (int)Ystart <= 0 ? Yend - Ystart : Ystart - Yend;

    // Increment direction, 1 is positive, -1 is counter;
    XAddway = Xstart < Xend ? 1 : -1;
    YAddway = Ystart < Yend ? 1 : -1;

    //Cumulative error
    Esp = dx + dy;
    Dotted_Len = 0;

    for (;;) {
        Dotted_Len++;
        //Painted dotted line, 2 point is really virtual
        if (Line_Style == DOTTED && Dotted_Len % 3 == 0) {
            //Debug("LINE_DOTTED\r\n");
            Gui_Draw_Point(Xpoint, Ypoint, IMAGE_BACKGROUND, Dot_Pixel, DOT_STYLE_DFT);
            Dotted_Len = 0;
        } else {
            Gui_Draw_Point(Xpoint, Ypoint, Color, Dot_Pixel, DOT_STYLE_DFT);
        }
        if (2 * Esp >= dy) {
            if (Xpoint == Xend)
                break;
            Esp += dy;
            Xpoint += XAddway;
        }
        if (2 * Esp <= dx) {
            if (Ypoint == Yend)
                break;
            Esp += dx;
            Ypoint += YAddway;
        }
    }
}
/******************************************************************************
function:	Draw a rectangle
parameter:
    x1 ：Rectangular  Starting Xpoint point coordinates
    y1 ：Rectangular  Starting Ypoint point coordinates
    x2   ：Rectangular  End point Xpoint coordinate
    y2   ：Rectangular  End point Ypoint coordinate
    color  ：The color of the Rectangular segment
    filled : Whether it is filled--- 1 solid 0：empty
******************************************************************************/
void Gui_Draw_Rectangle(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend,
                         uint16_t Color, DRAW_FILL Filled, DOT_PIXEL Dot_Pixel)
{
	uint16_t Ypoint;
    if (Xstart > Image.w || Ystart > Image.h ||
        Xend > Image.w || Yend > Image.h) {
        //Debug("Input exceeds the normal display range\r\n");
        return;
    }

    if (Filled ) {
        for(Ypoint = Ystart; Ypoint < Yend; Ypoint++) {
            Gui_Draw_Line(Xstart, Ypoint, Xend, Ypoint, Color ,Dot_Pixel,SOLID);
        }
    } else {
        Gui_Draw_Line(Xstart, Ystart, Xend, Ystart, Color , Dot_Pixel,SOLID);
        Gui_Draw_Line(Xstart, Ystart, Xstart, Yend, Color , Dot_Pixel,SOLID);
        Gui_Draw_Line(Xend, Yend, Xend, Ystart, Color , Dot_Pixel,SOLID);
        Gui_Draw_Line(Xend, Yend, Xstart, Yend, Color , Dot_Pixel,SOLID);
    }
}


/******************************************************************************
function:	Use Bresenham  algorithm to draw a circle of the
            specified size at the specified position->
parameter:
    x  ：Center X coordinate
    y  ：Center Y coordinate
    radius    ：circle radius
    color     ：The color of the ：circle segment
    filled    : Whether it is filled: 1 filling 0：Do not
******************************************************************************/
void Gui_Draw_Circle(uint16_t x, uint16_t y, uint16_t radius, uint16_t color, DRAW_FILL fill , DOT_PIXEL Dot_Pixel)
{
    uint16_t dx=0,dy=radius;
    int d = 1-radius;
    uint16_t i;
    if(x>Image.w || y > Image.h )
    {
        printf("ERR:circle center point out of screen area!\r\n");
        return ;
    } 
    while(dy>dx)
    {
        if(fill==EMPTY)
        {
            Gui_Draw_Point(x+dx, y+dy, color, Dot_Pixel, AROUND);
            Gui_Draw_Point(x+dy, y+dx, color, Dot_Pixel, AROUND);
            Gui_Draw_Point(x-dx, y+dy, color, Dot_Pixel, AROUND);
            Gui_Draw_Point(x-dy, y+dx, color, Dot_Pixel, AROUND);
            Gui_Draw_Point(x-dx, y-dy, color, Dot_Pixel, AROUND);
            Gui_Draw_Point(x-dy, y-dx, color, Dot_Pixel, AROUND);
            Gui_Draw_Point(x+dx, y-dy, color, Dot_Pixel, AROUND);
            Gui_Draw_Point(x+dy, y-dx, color, Dot_Pixel, AROUND);
        }
        else if(fill==FULL)
        {
            for(i=dx;i<=dy;i++)
            {
                Gui_Draw_Point(x+dx, y+i, color, Dot_Pixel, AROUND);
                Gui_Draw_Point(x+i, y+dx, color, Dot_Pixel, AROUND);
                Gui_Draw_Point(x-dx, y+i, color, Dot_Pixel, AROUND);
                Gui_Draw_Point(x-i, y+dx, color, Dot_Pixel, AROUND);
                Gui_Draw_Point(x-dx, y-i, color, Dot_Pixel, AROUND);
                Gui_Draw_Point(x-i, y-dx, color, Dot_Pixel, AROUND);
                Gui_Draw_Point(x+dx, y-i, color, Dot_Pixel, AROUND);
                Gui_Draw_Point(x+i, y-dx, color, Dot_Pixel, AROUND);                
            }
        }
        if(d<0)
        {
            d += 2*dx+3;    
        }
        else
        {
            d += 2*(dx-dy)+5;
            dy--;
        }
        dx++;
    }
}
/******************************************************************************
function:	Display monochrome bitmap
parameter:
    image_buffer ：A picture data converted to a bitmap
info:
    Use a computer to convert the image into a corresponding array,
    and then embed the array directly into Imagedata.cpp as a .c file.
******************************************************************************/
void Gui_DrawBitMap(const unsigned char* image_buffer)
{
    uint16_t x, y;
    uint32_t Addr = 0;

    for (y = 0; y < Image.byte_h; y++) 
	  {
        for (x = 0; x < Image.byte_w; x++) 
			  {//8 pixel =  1 byte
            Addr = x + y * Image.byte_w;
            Image.img[Addr] = (unsigned char)image_buffer[Addr];
        }
    }
}
