#include "Border.h"

#include "Image.h"
#include "Point.h"
#include "Scroll.h"
#include "Text.h"
#include "ToolTip.h"

using namespace Framework;

// Contents of the Border class from Border.h
// Constructor
Border::Border()
    : Drawable(),
      thickness(1),
      color(0xFF000000),
      alpha(0),
      breaks(0)
{}

// Destructor
Border::~Border() {}

// non-constant
void Border::setBorderWidth(int br) // sets the width of the border
{
    this->thickness = br;
    rend = 1;
}

// when this flag is set, the border is drawn dashed
//  br: 1 -> dashed, 0 -> solid
void Border::setBreaks(bool br, int brOff, int brLength, int lineLength)
{
    breaks = br;
    breakOffset = brOff;
    breakLength = brLength;
    this->lineLength = lineLength;
    rend = 1;
}

void Border::setAlpha(
    bool a) // Sets whether the alpha value of the color should be considered
{
    alpha = a;
    rend = 1;
}

void Border::setColor(int f) // Sets the color of the border
{
    color = f;
    rend = 1;
}

int Border::getColor() const // Returns the color of the border
{
    return color;
}

bool Border::hasAlpha()
    const // Returns whether the alpha value of the color is considered
{
    return alpha;
}

int Border::getRWidth() const // Returns the width of the border
{
    return thickness;
}

// Returns 1 if the border is drawn dashed
bool Border::hasBreaks() const
{
    return breaks;
}

// starting point of the first line segment
int Border::getBreakOffset() const
{
    return breakOffset;
}

// length of a gap
int Border::getBreakLength() const
{
    return breakLength;
}

// length of a line
int Border::getLineLength() const
{
    return lineLength;
}

// Contents of the LBorder class from Border.h
// Constructor
LBorder::LBorder()
    : Border()
{}

// Destructor
LBorder::~LBorder() {}

void LBorder::render(Image& Obj) // Draws the border into the render object
{
    Drawable::render(Obj);
    int x = 0;
    int y = 0;
    int b = gr.x - 1;
    int h = gr.y - 1;
    if (!Obj.setDrawOptions(pos.x, pos.y, gr.x, gr.y)) return;
    if (!breaks)
    {
        if (alpha)
        {
            for (int i = 0; i < thickness; ++i)
            {
                Obj.drawLineHAlpha(x + i + 1, y + i, gr.x - i * 2 - 1, color);
                Obj.drawLineVAlpha(b - i, y + i + 1, gr.y - i * 2 - 2, color);
                Obj.drawLineHAlpha(x + i + 1, h - i, gr.x - i * 2 - 1, color);
                Obj.drawLineVAlpha(x + i, y + i, gr.y - i * 2, color);
            }
        }
        else
        {
            for (int i = 0; i < thickness; ++i)
            {
                Obj.drawLineH(x + i + 1, y + i, gr.x - i * 2 - 1, color);
                Obj.drawLineV(b - i, y + i + 1, gr.y - i * 2 - 2, color);
                Obj.drawLineH(x + i + 1, h - i, gr.x - i * 2 - 1, color);
                Obj.drawLineV(x + i, y + i, gr.y - i * 2, color);
            }
        }
    }
    else
    {
        if (alpha)
        {
            for (int i = 0; i < thickness; ++i)
            {
                for (int x = breakOffset; x < gr.x;
                    x += lineLength + breakLength)
                    Obj.drawLineHAlpha(x + i + 1,
                        y + i,
                        lineLength
                            - (x + i + 1 + lineLength > gr.x - i - 1
                                    ? gr.x - i - 1 - (x + i + 1 + lineLength)
                                    : 0),
                        color);
                for (int x = gr.x - breakOffset - lineLength; x >= 0;
                    x -= lineLength + breakLength)
                    Obj.drawLineHAlpha(MAX(x + i + 1, i + 1),
                        h - i,
                        lineLength
                            - (x + i + 1 < i + 1 ? i + 1 - (x + i + 1) : 0),
                        color);
                for (int y = breakOffset; y < gr.y;
                    y += lineLength + breakLength)
                    Obj.drawLineVAlpha(b - i,
                        y + i + 1,
                        lineLength
                            - (y + i + 1 + lineLength > gr.y - i - 1
                                    ? gr.y - i - 1 - (y + i + 1 + lineLength)
                                    : 0),
                        color);
                for (int y = gr.y - breakOffset - lineLength; y >= 0;
                    y -= lineLength + breakLength)
                    Obj.drawLineVAlpha(x + i,
                        MAX(y + i, i),
                        lineLength - (y + i < i ? i - (y + i) : 0),
                        color);
            }
        }
        else
        {
            for (int i = 0; i < thickness; ++i)
            {
                for (int x = breakOffset; x < gr.x;
                    x += lineLength + breakLength)
                    Obj.drawLineH(x + i + 1,
                        y + i,
                        lineLength
                            - (x + i + 1 + lineLength > gr.x - i - 1
                                    ? gr.x - i - 1 - (x + i + 1 + lineLength)
                                    : 0),
                        color);
                for (int x = gr.x - breakOffset - lineLength; x >= 0;
                    x -= lineLength + breakLength)
                    Obj.drawLineH(MAX(x + i + 1, i + 1),
                        h - i,
                        lineLength
                            - (x + i + 1 < i + 1 ? i + 1 - (x + i + 1) : 0),
                        color);
                for (int y = breakOffset; y < gr.y;
                    y += lineLength + breakLength)
                    Obj.drawLineV(b - i,
                        y + i + 1,
                        lineLength
                            - (y + i + 1 + lineLength > gr.y - i - 1
                                    ? gr.y - i - 1 - (y + i + 1 + lineLength)
                                    : 0),
                        color);
                for (int y = gr.y - breakOffset - lineLength; y >= 0;
                    y -= lineLength + breakLength)
                    Obj.drawLineV(x + i,
                        MAX(y + i, i),
                        lineLength - (y + i < i ? i - (y + i) : 0),
                        color);
            }
        }
    }
    Obj.releaseDrawOptions();
}

Drawable* LBorder::duplicate() const // Copies the drawing
{
    Border* obj = new LBorder();
    obj->setPosition(pos);
    obj->setSize(gr);
    obj->setMouseEventParameter(makParam);
    obj->setKeyboardEventParameter(takParam);
    obj->setMouseEvent(mak);
    obj->setKeyboardEvent(tak);
    if (toolTip) obj->setToolTipZ((ToolTip*)toolTip->duplicate());
    obj->setBreaks(breaks);
    obj->setAlpha(alpha);
    obj->setColor(color);
    obj->setBorderWidth(thickness);
    return obj;
}

// Contents of the Border3D class from Border.h
// Constructor
Border3D::Border3D()
    : Border()
{
    alpha = 1;
    color = 0x70FFFFFF;
    thickness = 5;
}

// Destructor
Border3D::~Border3D() {}

void Border3D::render(Image& Obj) // Draws the border into the render object
{
    Drawable::render(Obj);
    int x = 0;
    int y = 0;
    int b = gr.x - 1;
    int h = gr.y - 1;
    int fcomp = (color & 0xFF000000) | (~color & 0x00FFFFFF);
    if (!Obj.setDrawOptions(pos.x, pos.y, gr.x, gr.y)) return;
    if (!breaks)
    {
        if (alpha)
        {
            for (int i = 0; i < thickness; ++i)
            {
                Obj.drawLineHAlpha(x + i + 1, y + i, gr.x - i * 2 - 1, color);
                Obj.drawLineVAlpha(b - i, y + i + 1, gr.y - i * 2 - 2, color);
                Obj.drawLineHAlpha(x + i + 1, h - i, gr.x - i * 2 - 1, fcomp);
                Obj.drawLineVAlpha(x + i, y + i, gr.y - i * 2, fcomp);
            }
        }
        else
        {
            for (int i = 0; i < thickness; ++i)
            {
                Obj.drawLineH(x + i + 1, y + i, gr.x - i * 2 - 1, color);
                Obj.drawLineV(b - i, y + i + 1, gr.y - i * 2 - 2, color);
                Obj.drawLineH(x + i + 1, h - i, gr.x - i * 2 - 1, fcomp);
                Obj.drawLineV(x + i, y + i, gr.y - i * 2, fcomp);
            }
        }
    }
    else
    {
        if (alpha)
        {
            for (int i = 0; i < thickness; ++i)
            {
                for (int x = breakOffset; x < gr.x;
                    x += lineLength + breakLength)
                    Obj.drawLineHAlpha(x + i + 1,
                        y + i,
                        lineLength
                            - (x + i + 1 + lineLength > gr.x - i - 1
                                    ? gr.x - i - 1 - (x + i + 1 + lineLength)
                                    : 0),
                        color);
                for (int x = gr.x - breakOffset - lineLength; x >= 0;
                    x -= lineLength + breakLength)
                    Obj.drawLineHAlpha(MAX(x + i + 1, i + 1),
                        h - i,
                        lineLength
                            - (x + i + 1 < i + 1 ? i + 1 - (x + i + 1) : 0),
                        fcomp);
                for (int y = breakOffset; y < gr.y;
                    y += lineLength + breakLength)
                    Obj.drawLineVAlpha(b - i,
                        y + i + 1,
                        lineLength
                            - (y + i + 1 + lineLength > gr.y - i - 1
                                    ? gr.y - i - 1 - (y + i + 1 + lineLength)
                                    : 0),
                        color);
                for (int y = gr.y - breakOffset - lineLength; y >= 0;
                    y -= lineLength + breakLength)
                    Obj.drawLineVAlpha(x + i,
                        MAX(y + i, i),
                        lineLength - (y + i < i ? i - (y + i) : 0),
                        fcomp);
            }
        }
        else
        {
            for (int i = 0; i < thickness; ++i)
            {
                for (int x = breakOffset; x < gr.x;
                    x += lineLength + breakLength)
                    Obj.drawLineH(x + i + 1,
                        y + i,
                        lineLength
                            - (x + i + 1 + lineLength > gr.x - i - 1
                                    ? gr.x - i - 1 - (x + i + 1 + lineLength)
                                    : 0),
                        color);
                for (int x = gr.x - breakOffset - lineLength; x >= 0;
                    x -= lineLength + breakLength)
                    Obj.drawLineH(MAX(x + i + 1, i + 1),
                        h - i,
                        lineLength
                            - (x + i + 1 < i + 1 ? i + 1 - (x + i + 1) : 0),
                        fcomp);
                for (int y = breakOffset; y < gr.y;
                    y += lineLength + breakLength)
                    Obj.drawLineV(b - i,
                        y + i + 1,
                        lineLength
                            - (y + i + 1 + lineLength > gr.y - i - 1
                                    ? gr.y - i - 1 - (y + i + 1 + lineLength)
                                    : 0),
                        color);
                for (int y = gr.y - breakOffset - lineLength; y >= 0;
                    y -= lineLength + breakLength)
                    Obj.drawLineV(x + i,
                        MAX(y + i, i),
                        lineLength - (y + i < i ? i - (y + i) : 0),
                        fcomp);
            }
        }
    }
    Obj.releaseDrawOptions();
}

Drawable* Border3D::duplicate() const // Copies the drawing
{
    Border* obj = new Border3D();
    obj->setPosition(pos);
    obj->setSize(gr);
    obj->setMouseEventParameter(makParam);
    obj->setKeyboardEventParameter(takParam);
    obj->setMouseEvent(mak);
    obj->setKeyboardEvent(tak);
    if (toolTip) obj->setToolTipZ((ToolTip*)toolTip->duplicate());
    obj->setBreaks(breaks);
    obj->setAlpha(alpha);
    obj->setColor(color);
    obj->setBorderWidth(thickness);
    return obj;
}