#include "ToolTip.h"

#include "AlphaField.h"
#include "Image.h"
#include "Screen.h"
#include "MouseEvent.h"
#include "Border.h"
#include "Font.h"
#include "Scroll.h"
#include "Text.h"
#include "TextField.h"

using namespace Framework;

// Contents of the ToolTip class from ToolTip.h
// Constructor
ToolTip::ToolTip(Screen* zScreen)
    : DrawableBackground(),
      size(0, 0),
      animationSpeed(250),
      warten(1),
      wartenCount(0),
      tval(0),
      mouseIn(0),
      alpha(0),
      visible(0),
      zeichnen(0),
      mouseIn2(0),
      bildschirm(zScreen),
      onShow(0),
      onHide(0)
{
    style = Style::MEIgnoreInside | Style::MEIgnoreParentInside
          | Style::MEIgnoreVisible | Style::MEIgnoreProcessed
          | Style::Allowed;
    members = new RCArray<Drawable>();
    bildschirm->addToolTip(dynamic_cast<ToolTip*>(this->getThis()));
    setMouseEvent(_ret1ME);
}

// Destructor
ToolTip::~ToolTip()
{
    members->release();
}

void ToolTip::doMouseEvent(MouseEvent& me, bool userRet)
{
    if (!visible) pos.x += me.mx, pos.y += me.my + 15;
    if (hasStyleNot(Style::Visible) || !me.insideParent || me.processed
        || me.mx < 0 || me.my < 0 || me.mx >= gr.x || me.my >= gr.y || !userRet)
    {
        mouseIn2 = 0;
        if (!mouseIn && visible)
        {
            if (onHide && visible) onHide(this);
            visible = 0;
        }
        bool processed = me.processed;
        me.processed |= hasStyleNot(Style::Visible);
        bool insideParent = me.insideParent;
        me.insideParent = 0;
        int rbr = 0;
        if (hasStyle(Style::Border) && border) rbr = border->getRWidth();
        me.mx -= rbr;
        me.my -= rbr;
        if (hasStyle(Style::VScroll) && vertikalScrollBar)
            me.my += vertikalScrollBar->getScroll();
        if (hasStyle(Style::HScroll) && horizontalScrollBar)
            me.mx += horizontalScrollBar->getScroll();
        if (me.id != ME_Enter && me.id != ME_Leaves)
        {
            for (int i = members->getEntryCount() - 1; i >= 0; i--)
                members->z(i)->doPublicMouseEvent(me);
        }
        me.mx += rbr;
        me.my += rbr;
        if (hasStyle(Style::VScroll) && vertikalScrollBar)
            me.my -= vertikalScrollBar->getScroll();
        if (hasStyle(Style::HScroll) && horizontalScrollBar)
            me.mx -= horizontalScrollBar->getScroll();
        me.insideParent = insideParent;
        if (hasStyleNot(Style::Visible)) me.processed = processed;
        return;
    }
    mouseIn2 = 1;
    if (visible)
    {
        int rbr = 0;
        if (hasStyle(Style::Border) && border) rbr = border->getRWidth();
        me.mx -= rbr;
        me.my -= rbr;
        if (hasStyle(Style::VScroll) && vertikalScrollBar)
            me.my += vertikalScrollBar->getScroll();
        if (hasStyle(Style::HScroll) && horizontalScrollBar)
            me.mx += horizontalScrollBar->getScroll();
        for (auto z : *members)
            z->doPublicMouseEvent(me);
        me.mx += rbr;
        me.my += rbr;
        if (hasStyle(Style::VScroll) && vertikalScrollBar)
            me.my -= vertikalScrollBar->getScroll();
        if (hasStyle(Style::HScroll) && horizontalScrollBar)
            me.mx -= horizontalScrollBar->getScroll();
    }
    if (visible) me.processed = 1;
    if (alpha) rend = 1;
}

// Adds a drawing to the tooltip
//  m: the new drawing
void ToolTip::addMember(Drawable* m)
{
    members->add(m);
}

void ToolTip::removeMember(Drawable* zM)
{
    int index = 0;
    for (auto i : *members)
    {
        if (i == zM)
        {
            members->remove(index);
            return;
        }
        index++;
    }
}

// Removes a drawing from the tooltip
//  i: the index of the drawing
void ToolTip::removeMember(int i)
{
    members->remove(i);
}

// sets a function that is called when the tooltip is shown
//  onShow: The function
void ToolTip::setShowEvent(std::function<void(ToolTip*)> onShow)
{
    this->onShow = onShow;
}

// sets a function that is called when the tooltip is no longer
// shown
//  onShow: The function
void ToolTip::setHideEvent(std::function<void(ToolTip*)> onHide)
{
    this->onHide = onHide;
}

void ToolTip::setWarten(double warten)
{
    this->warten = warten;
}

void ToolTip::setAnimationSpeed(double speed)
{
    animationSpeed = speed;
}

void ToolTip::setMouseIn(bool mouseIn)
{
    if (this->mouseIn != mouseIn) rend = 1;
    this->mouseIn = mouseIn;
    if (!mouseIn && !mouseIn2 && visible)
    {
        if (visible && onHide) onHide(this);
        visible = 0;
    }
}

void ToolTip::waitReset()
{
    wartenCount = 0;
}

void ToolTip::setDrawing()
{
    zeichnen = 1;
}

bool ToolTip::tick(double tickVal)
{
    for (auto z : *members)
    {
        size.x
            = MAX(size.x, z->getX() + z->getWidth() + 2 * getBorderWidth());
        size.y
            = MAX(size.y, z->getY() + z->getHeight() + 2 * getBorderWidth());
    }
    this->tval += tickVal * animationSpeed;
    int val = (int)this->tval;
    if (val < 1)
    {
        bool ret = rend;
        rend = 0;
        return ret;
    }
    this->tval -= val;
    if (!visible)
    {
        if (alpha)
        {
            if (alpha - val < 0)
                alpha = 0;
            else
                alpha = (unsigned char)(alpha - val);
            rend = 1;
        }
        if (mouseIn)
        {
            wartenCount += tickVal;
            if (wartenCount >= warten)
            {
                if (onShow) onShow(this);
                visible = 1;
                wartenCount = 0;
                alpha = 0xFF;
                DrawableBackground::setSize(0, 0);
            }
        }
        else
            wartenCount = 0;
    }
    else
    {
        if (getWidth() < size.x)
        {
            DrawableBackground::setSize(getWidth() + val, getHeight());
            if (getWidth() > size.x)
                DrawableBackground::setSize(size.x, getHeight());
            rend = 1;
        }
        if (getHeight() < size.y)
        {
            DrawableBackground::setSize(getWidth(), getHeight() + val);
            if (getHeight() > size.y)
                DrawableBackground::setSize(getWidth(), size.y);
            rend = 1;
        }
    }
    return DrawableBackground::tick(tickVal);
}

void ToolTip::render(Image& zRObj)
{
    if (alpha && (zeichnen || mouseIn2))
    {
        zRObj.setAlpha(alpha);
        setPosition(pos);
        if (getX() + getWidth() > zRObj.getWidth())
            setPosition(
                getX() - (getX() + getWidth() - zRObj.getWidth()), getY());
        if (getY() + getHeight() > zRObj.getHeight())
            setPosition(
                getX(), getY() - (getY() + getHeight() - zRObj.getHeight()));
        DrawableBackground::render(zRObj);
        Point p = pos;
        Point s = gr;
        if (hasStyle(DrawableBackground::Style::Border))
        {
            p += Point(getBorderWidth(), getBorderWidth());
            s -= Point(getBorderWidth(), getBorderWidth()) * 2;
        }
        if (!zRObj.setDrawOptions(p, s))
        {
            zRObj.releaseAlpha();
            zeichnen = 0;
            return;
        }
        bool vSc = hasStyle(Style::VScroll) && vertikalScrollBar;
        bool hSc = hasStyle(Style::HScroll) && horizontalScrollBar;
        zRObj.addScrollOffset(hSc ? horizontalScrollBar->getScroll() : 0,
            vSc ? vertikalScrollBar->getScroll() : 0);
        for (auto z : *members)
            z->render(zRObj);
        zRObj.releaseDrawOptions();
        zRObj.releaseAlpha();
        zeichnen = 0;
    }
}

// constant
Screen* ToolTip::zScreen() const
{
    return bildschirm;
}

// Returns a specific member (without increased reference counter)
//  i: the index of the member
Drawable* ToolTip::zMember(int i) const
{
    return members->z(i);
}

// Returns a specific member
//  i: the index of the member
Drawable* ToolTip::getMember(int i) const
{
    return members->get(i);
}

// Returns the number of drawings that belong to the tooltip
int ToolTip::getMemberCount() const
{
    return members->getEntryCount();
}

// Creates a complete copy of a tooltip
Drawable* ToolTip::duplicate() const
{
    ToolTip* ret = new ToolTip(bildschirm);
    ret->size = size;
    ret->animationSpeed = animationSpeed;
    ret->warten = warten;
    ret->wartenCount = wartenCount;
    ret->tval = tval;
    ret->mouseIn = mouseIn;
    ret->alpha = alpha;
    ret->visible = visible;
    ret->zeichnen = zeichnen;
    ret->mouseIn2 = mouseIn2;
    for (auto z : *members)
        ret->addMember(dynamic_cast<Drawable*>(z->getThis()));
    return ret;
}

//! Checks whether the tooltip is currently being shown
DLLEXPORT bool ToolTip::isVisible() const
{
    return visible;
}