#include "graphic.h"

Graphic *_gpu;

Graphic::Graphic(HMI *_lcd, uint16_t w, uint16_t h)
{
    lcd = _lcd;
    lcd_width = w;
    lcd_height = h;
    _gpu = this;
}

void Graphic::begin()
{
    //������ʾ������
    set_rotation(0);

    //����ǰ����ɫ
    set_back_color(0);
    set_color(0xffff);

    
    set_text_auto_reline(0);
    _gpu->set_font(&GUI_Font16_ASCII);
    set_text_mode(GuiDrawMode::Normal);
};


/*********************************************************************
*
*       Graphic settings
*
**********************************************************************
*/

void Graphic::set_color(uint32_t color)
{
    this->color = (color);
}
void Graphic::set_back_color(uint32_t back_color)
{
    this->back_color = (back_color);    
}

void Graphic::set_cursor(int16_t x, int16_t y)
{
    this->cursor_x = x;
    this->cursor_y = y;
}
void Graphic::set_draw_mode(uint8_t mode)
{
    draw_mode = mode;
}

/*********************************************************************
*
*       Graphic graphic
*
**********************************************************************
*/
void Graphic::draw_circle(int16_t x0, int16_t y0, int16_t r)
{

    int16_t f = 1 - r;
    int16_t ddF_x = 1;
    int16_t ddF_y = -2 * r;
    int16_t x = 0;
    int16_t y = r;

    draw_pixel(x0, y0 + r);
    draw_pixel(x0, y0 - r);
    draw_pixel(x0 + r, y0  );
    draw_pixel(x0 - r, y0  );

    while (x < y)
    {
        if (f >= 0)
        {
            y--;
            ddF_y += 2;
            f += ddF_y;
        }
        x++;
        ddF_x += 2;
        f += ddF_x;

        draw_pixel(x0 + x, y0 + y);
        draw_pixel(x0 - x, y0 + y);
        draw_pixel(x0 + x, y0 - y);
        draw_pixel(x0 - x, y0 - y);
        draw_pixel(x0 + y, y0 + x);
        draw_pixel(x0 - y, y0 + x);
        draw_pixel(x0 + y, y0 - x);
        draw_pixel(x0 - y, y0 - x);
    }
}

void Graphic::draw_circle_helper( int16_t x0, int16_t y0,
                              int16_t r, uint8_t cornername)
{
    int16_t f     = 1 - r;
    int16_t ddF_x = 1;
    int16_t ddF_y = -2 * r;
    int16_t x     = 0;
    int16_t y     = r;

    while (x < y)
    {
        if (f >= 0)
        {
            y--;
            ddF_y += 2;
            f     += ddF_y;
        }
        x++;
        ddF_x += 2;
        f     += ddF_x;
        if (cornername & 0x4)
        {
            draw_pixel(x0 + x, y0 + y);
            draw_pixel(x0 + y, y0 + x);
        }
        if (cornername & 0x2)
        {
            draw_pixel(x0 + x, y0 - y);
            draw_pixel(x0 + y, y0 - x);
        }
        if (cornername & 0x8)
        {
            draw_pixel(x0 - y, y0 + x);
            draw_pixel(x0 - x, y0 + y);
        }
        if (cornername & 0x1)
        {
            draw_pixel(x0 - y, y0 - x);
            draw_pixel(x0 - x, y0 - y);
        }
    }
}
void Graphic::fill_circle(int16_t x0, int16_t y0, int16_t r)
{
    draw_v_line(x0, y0 - r, y0 + r + 1);
    fill_circle_helper(x0, y0, r, 3, -1);
}
// Used to do circles and roundrects
void Graphic::fill_circle_helper(int16_t x0, int16_t y0, int16_t r,
                             uint8_t cornername, int16_t delta)
{

    int16_t f     = 1 - r;
    int16_t ddF_x = 1;
    int16_t ddF_y = -2 * r;
    int16_t x     = 0;
    int16_t y     = r;

    while (x < y)
    {
        if (f >= 0)
        {
            y--;
            ddF_y += 2;
            f     += ddF_y;
        }
        x++;
        ddF_x += 2;
        f     += ddF_x;

        if (cornername & 0x1)
        {
            draw_v_line(x0 + x, y0 - y, y0 + y + 1 + delta);
            draw_v_line(x0 + y, y0 - x, y0 + x + 1 + delta);
        }
        if (cornername & 0x2)
        {
            draw_v_line(x0 - x, y0 - y, y0 + y + 1 + delta);
            draw_v_line(x0 - y, y0 - x, y0 + x + 1 + delta);
        }
    }
}





// Draw a rounded rectangle
void Graphic::draw_round_rect(int16_t x, int16_t y, int16_t w,
                          int16_t h, int16_t r)
{
    // smarter version
    draw_h_line(x + r, y, x + w - r + 1);   // Top
    draw_h_line(x + r, y + h - 1, x + w - r + 1); // Bottom
    draw_v_line(x, y + r, y + h - r + 1);   // Left
    draw_v_line(x + w - 1, y + r, y + h - r + 1); // Right
    // draw four corners
    draw_circle_helper(x + r, y + r, r, 1);
    draw_circle_helper(x + w - r - 1, y + r, r, 2);
    draw_circle_helper(x + w - r - 1, y + h - r - 1, r, 4);
    draw_circle_helper(x + r, y + h - r - 1, r, 8);
}

// Fill a rounded rectangle
void Graphic::fill_round_rect(int16_t x, int16_t y, int16_t w,
                          int16_t h, int16_t r)
{
    // smarter version
    fill_rect(x + r, y, x + w - r + 1, y + h);

    // draw four corners
    fill_circle_helper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1);
    fill_circle_helper(x + r, y + r, r, 2, h - 2 * r - 1);
}

// Draw a triangle
void Graphic::draw_triangle(int16_t x0, int16_t y0,
                        int16_t x1, int16_t y1, int16_t x2, int16_t y2)
{
    draw_line(x0, y0, x1, y1);
    draw_line(x1, y1, x2, y2);
    draw_line(x2, y2, x0, y0);
}

//// Fill a triangle
void Graphic::fill_triangle(int16_t x0, int16_t y0,
                        int16_t x1, int16_t y1, int16_t x2, int16_t y2)
{

    int16_t a, b, y, last;

    // Sort coordinates by Y order (y2 >= y1 >= y0)
    if (y0 > y1)
    {
        _swap_int16_t(y0, y1);
        _swap_int16_t(x0, x1);
    }
    if (y1 > y2)
    {
        _swap_int16_t(y2, y1);
        _swap_int16_t(x2, x1);
    }
    if (y0 > y1)
    {
        _swap_int16_t(y0, y1);
        _swap_int16_t(x0, x1);
    }

    if(y0 == y2)   // Handle awkward all-on-same-line case as its own thing
    {
        a = b = x0;
        if(x1 < a)      a = x1;
        else if(x1 > b) b = x1;
        if(x2 < a)      a = x2;
        else if(x2 > b) b = x2;
        draw_h_line(a, y0, b + 1);
        return;
    }

    int16_t
    dx01 = x1 - x0,
    dy01 = y1 - y0,
    dx02 = x2 - x0,
    dy02 = y2 - y0,
    dx12 = x2 - x1,
    dy12 = y2 - y1;
    int32_t
    sa   = 0,
    sb   = 0;

    // For upper part of triangle, find scanline crossings for segments
    // 0-1 and 0-2.  If y1=y2 (flat-bottomed triangle), the scanline y1
    // is included here (and second loop will be skipped, avoiding a /0
    // error there), otherwise scanline y1 is skipped here and handled
    // in the second loop...which also avoids a /0 error here if y0=y1
    // (flat-topped triangle).
    if(y1 == y2) last = y1;   // Include y1 scanline
    else         last = y1 - 1; // Skip it

    for(y = y0; y <= last; y++)
    {
        a   = x0 + sa / dy01;
        b   = x0 + sb / dy02;
        sa += dx01;
        sb += dx02;
        /* longhand:
        a = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
        b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
        */
        if(a > b) _swap_int16_t(a, b);
        draw_h_line(a, y, b + 1);
    }

    // For lower part of triangle, find scanline crossings for segments
    // 0-2 and 1-2.  This loop is skipped if y1=y2.
    sa = dx12 * (y - y1);
    sb = dx02 * (y - y0);
    for(; y <= y2; y++)
    {
        a   = x1 + sa / dy12;
        b   = x0 + sb / dy02;
        sa += dx12;
        sb += dx02;
        /* longhand:
        a = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
        b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
        */
        if(a > b) _swap_int16_t(a, b);
        draw_h_line(a, y, b + 1);
    }
}


//void Graphic::box(int16_t x, int16_t y, int16_t w, int16_t h, int16_t bc)
//{
//    draw_line(x, y, x + w, y, 0xEF7D);
//    draw_line(x + w - 1, y + 1, x + w - 1, y + 1 + h, 0x2965);
//    draw_line(x, y + h, x + w, y + h, 0x2965);
//    draw_line(x, y, x, y + h, 0xEF7D);
//    draw_line(x + 1, y + 1, x + 1 + w - 2, y + 1 + h - 2, bc);
//}
//void Graphic::box2(int16_t x, int16_t y, int16_t w, int16_t h, u8 mode)
//{
//    if (mode == 0)
//    {
//        draw_line(x, y, x + w, y, 0xEF7D);
//        draw_line(x + w - 1, y + 1, x + w - 1, y + 1 + h, 0x2965);
//        draw_line(x, y + h, x + w, y + h, 0x2965);
//        draw_line(x, y, x, y + h, 0xEF7D);
//    }
//    if (mode == 1)
//    {
//        draw_line(x, y, x + w, y, 0x2965);
//        draw_line(x + w - 1, y + 1, x + w - 1, y + 1 + h, 0xEF7D);
//        draw_line(x, y + h, x + w, y + h, 0xEF7D);
//        draw_line(x, y, x, y + h, 0x2965);
//    }
//    if (mode == 2)
//    {
//        draw_line(x, y, x + w, y, 0xffff);
//        draw_line(x + w - 1, y + 1, x + w - 1, y + 1 + h, 0xffff);
//        draw_line(x, y + h, x + w, y + h, 0xffff);
//        draw_line(x, y, x, y + h, 0xffff);
//    }
//}




void  Graphic::set_rotation(uint8_t value)
{
    limit(value,(uint8_t)0,(uint8_t)3);
    rotation = value;
    _width = lcd_width;
    _height = lcd_height;
    switch(rotation)
    {
    case 1:
    case 3:
        swap(&_width, &_height);
        break;
    default :
            break;

    }
}
int16_t Graphic::ro_x(int16_t x, int16_t y)
{

    switch(rotation)
    {
    case 0:
        return x;
    case 1:
        return _height - 1 - y;
    case 2:
        return _width - 1 - x;
    case 3:
        return  y;

    }
    return 0;
}
int16_t Graphic::ro_y(int16_t x, int16_t y)
{
    switch(rotation)
    {
    case 0:
        return y;
    case 1:
        return x;
    case 2:
        return _height - 1 - y ;
    case 3:
        return _width - 1 - x;

    }
    return 0;

}

void Graphic::draw_pixel()
{
    int16_t tempx, tempy;
    tempx = ro_x(this->cursor_x, this->cursor_y);
    tempy = ro_y(this->cursor_x, this->cursor_y);
    lcd->draw_pixel(tempx, tempy, this->color);
}
void Graphic::draw_pixel(uint32_t color)
{
    int16_t tempx, tempy;
    tempx = ro_x(this->cursor_x, this->cursor_y);
    tempy = ro_y(this->cursor_x, this->cursor_y);
    lcd->draw_pixel(tempx, tempy, color);
}
void Graphic::draw_pixel(int16_t x, int16_t y)
{
    int16_t tempx, tempy;
    tempx = ro_x(x, y);
    tempy = ro_y(x, y);
    lcd->draw_pixel(tempx, tempy, this->color);
}
void Graphic::draw_pixel(int16_t x, int16_t y, uint32_t color)
{
    int16_t tempx, tempy;
    tempx = ro_x(x, y);
    tempy = ro_y(x, y);
    lcd->draw_pixel(tempx, tempy, color);
}

void Graphic::draw_h_line(int16_t x0, int16_t y0, int16_t x1)
{
    draw_h_line(x0,y0,x1,this->color);
}
void Graphic::draw_h_line(int16_t x0, int16_t y0, int16_t x1,uint32_t color)
{
    int16_t tempx0, tempy0, tempx1, tempy1;
    tempx0 = ro_x(x0, y0);
    tempy0 = ro_y(x0, y0);
    tempx1 = ro_x(x1, y0);
    tempy1 = ro_y(x1, y0);
    switch(rotation)
    {
    case 0:
        lcd->draw_h_line(tempx0, tempy0, tempx1, color);
        break;
    case 1:
        lcd->draw_v_line(tempx0, tempy0, tempy1, color);
        break;
    case 2:
        lcd->draw_h_line(tempx1, tempy0, tempx0, color);
        break;
    case 3:
        lcd->draw_v_line(tempx0, tempy1, tempy0, color);
        break;

    }
}

void Graphic::draw_v_line(int16_t x0, int16_t y0, int16_t y1)
{
   draw_v_line(x0,y0,y1,this->color);
}
void Graphic::draw_v_line(int16_t x0, int16_t y0, int16_t y1,uint32_t color)
{
    int16_t tempx0, tempy0, tempx1, tempy1;
    tempx0 = ro_x(x0, y0);
    tempy0 = ro_y(x0, y0);
    tempx1 = ro_x(x0, y1);
    tempy1 = ro_y(x0, y1);
    switch(rotation)
    {
    case 0:
        lcd->draw_v_line(tempx0, tempy0, tempy1, color);
        break;
    case 1:
        lcd->draw_h_line(tempx1, tempy0, tempx0, color);
        break;
    case 2:
        lcd->draw_v_line(tempx0, tempy1, tempy0, color);
        break;
    case 3:
        lcd->draw_h_line(tempx0, tempy0, tempx1, color);
        break;

    }
    //    lcd->draw_v_line(x0,y0,y1,this->color);
}

void Graphic::draw_line(int16_t x0, int16_t y0, int16_t x1, int16_t y1)
{
    int16_t tempx0, tempy0, tempx1, tempy1;
    tempx0 = ro_x(x0, y0);
    tempy0 = ro_y(x0, y0);
    tempx1 = ro_x(x1, y1);
    tempy1 = ro_y(x1, y1);

    lcd->draw_line(tempx0, tempy0, tempx1, tempy1, this->color);
}
void Graphic::draw_line(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint32_t color)
{
    int16_t tempx0, tempy0, tempx1, tempy1;
    tempx0 = ro_x(x0, y0);
    tempy0 = ro_y(x0, y0);
    tempx1 = ro_x(x1, y1);
    tempy1 = ro_y(x1, y1);

    lcd->draw_line(tempx0, tempy0, tempx1, tempy1, color);
}
void Graphic::draw_rect(int16_t x0, int16_t y0, int16_t x1, int16_t y1)
{
    draw_h_line(x0, y0, x1);
    draw_h_line(x0, y1, x1);
    draw_v_line(x0, y0, y1);
    draw_v_line(x1, y0, y1);
    
}
void Graphic::draw_rect(int16_t x0, int16_t y0, int16_t x1, int16_t y1,uint32_t color)
{
    draw_h_line(x0, y0, x1, color);
    draw_h_line(x0, y1, x1, color);
    draw_v_line(x0, y0, y1, color);
    draw_v_line(x1, y0, y1, color);
    
}
void Graphic::fill_rect(int16_t x0, int16_t y0, int16_t x1, int16_t y1)
{
    int16_t tempx0, tempy0, tempx1, tempy1;
    tempx0 = ro_x(x0, y0);
    tempy0 = ro_y(x0, y0);
    tempx1 = ro_x(x1, y1);
    tempy1 = ro_y(x1, y1);

    lcd->fill_rect(min(tempx0, tempx1), min(tempy0, tempy1), max(tempx0, tempx1), max(tempy0, tempy1), this->color);
}
void Graphic::fill_rect(int16_t x0, int16_t y0, int16_t x1, int16_t y1,uint32_t color)
{
    int16_t tempx0, tempy0, tempx1, tempy1;
    tempx0 = ro_x(x0, y0);
    tempy0 = ro_y(x0, y0);
    tempx1 = ro_x(x1, y1);
    tempy1 = ro_y(x1, y1);

    lcd->fill_rect(min(tempx0, tempx1), min(tempy0, tempy1), max(tempx0, tempx1), max(tempy0, tempy1), color);
}
void Graphic::fill_screen(uint32_t color)
{
    lcd->fill_screen(color);
}
void Graphic::clear()
{
    fill_screen(0);
}
void Graphic::flush()
{
    lcd->flush();
}