#include "scrollBar.h"
#include "graphics.hpp"
#include <iostream>
#include <math.h>    // round()

using namespace genv;

int scrollBar::cnt;

enum pressed_type { none, scrollHandle, increase, decrease };

/// ------------------------------------------ Események ----------------------------------------------------------
void scrollBar::onClick(int posX, int posY, char button)
{
    if (alignment == horizontal)
    {
        // Csökkent
        if (x < posX && posX < x + thickness)
        {
            setValue(value-1);
            mouseDown = decrease;
            waitCounter = 0;
        }
        // Növel
        else if (x + width - thickness < posX && posX < x + width)
        {
            setValue(value+1);
            mouseDown = increase;
            waitCounter = 0;
        }
        // Csuszkára kattint
        else if (x + thickness + scrollHandlePosition < posX && posX < x + thickness + scrollHandlePosition + scrollHandleLength)
        {
            mouseDown = scrollHandle;
            prevPos = posX;
            prevVal = value;
        }
        // Csuszka elé kattint
        else if (x + thickness < posX && posX < x + thickness + scrollHandlePosition)
            setValue(value - numRange/5);
        // Csuszka után kattint
        else if (x + thickness + scrollHandlePosition + scrollHandleLength < posX && posX < x + width - thickness)
            setValue(value + numRange/5);
    }
    else if (alignment == vertical)
    {
        // Csökkent
        if (y < posY && posY < y + thickness)
        {
            setValue(value-1);
            mouseDown = decrease;
            waitCounter = 0;
        }
        // Növel
        else if (y + height - thickness < posY && posY < y + height)
        {
            setValue(value+1);
            mouseDown = increase;
            waitCounter = 0;
        }
        // Csuszkára kattint
        else if (y + thickness + scrollHandlePosition < posY && posY < y + thickness + scrollHandlePosition + scrollHandleLength)
        {
            mouseDown = scrollHandle;
            prevPos = posY;
            prevVal = value;
        }
        // Csuszka elé kattint
        else if (y + thickness < posY && posY < y + thickness + scrollHandlePosition)
            setValue(value - numRange/5);
        // Csuszka után kattint
        else if (y + thickness + scrollHandlePosition + scrollHandleLength < posY && posY < y + height - thickness)
            setValue(value + numRange/5);
    }
}

void scrollBar::onMouseUp(int posX, int posY, char button)
{
    mouseDown = none;
}

void scrollBar::onMouseMove(int posX, int posY)
{
    if (mouseDown == scrollHandle)
    {
        if (alignment == horizontal)
            setValue( prevVal + round( (posX - prevPos) / (graphicRange - scrollHandleLength) * (numRange - valueWidth) ) );
        else
            setValue( prevVal + round( (posY - prevPos) / (graphicRange - scrollHandleLength) * (numRange - valueWidth) ) );
    }
}

void scrollBar::onKeyPress(char keyCode)
{
    if (alignment == horizontal)
    {
        switch (keyCode)
        {
        case (char)key_left :
            setValue(value - 1);
            keyDown = decrease;
            waitCounter = 0;
            break;
        case (char)key_right :
            setValue(value + 1);
            keyDown = increase;
            waitCounter = 0;
            break;
        case (char)key_pgup :
            setValue(minValue);
            break;
        case (char)key_pgdn :
            setValue(maxValue);
            break;
        }
    }
    else
    {
        switch (keyCode)
        {
        case (char)key_up :
            setValue(value - 1);
            keyDown = decrease;
            waitCounter = 0;
            break;
        case (char)key_down :
            setValue(value + 1);
            keyDown = increase;
            waitCounter = 0;
            break;
        case (char)key_pgup :
            setValue(minValue);
            break;
        case (char)key_pgdn :
            setValue(maxValue);
            break;
        }
    }
}

void scrollBar::onKeyUp(char keyCode)
{
    keyDown = none;
}

void scrollBar::onTick()
{
    if (waitCounter*t < waitInterval)
        waitCounter++;
    else
    {
        if (mouseDown == increase || keyDown == increase) setValue(value + 1);
        else if (mouseDown == decrease || keyDown == decrease) setValue(value - 1);
    }
}
/// ----------------------------------------------------------------------------------------------------------------


/// ---------------------------------------- Konstruktorok ----------------------------------------------------------
scrollBar::scrollBar(int x0, int y0, unsigned int length, scrollAlign align, int minV, int maxV) : widget(x0, y0, 0, 0)
{
    cnt++;
    alignment = align;
    setRange(minV, maxV);
    value = minValue;
    tabStop = true;
    keyDown = none;
    mouseDown = none;

    valueWidth = 0;

    numRange = maxValue - minValue;
    graphicRange = length - 2 * thickness;
    scrollHandleLength = graphicRange / numRange;
    if (scrollHandleLength < thickness / 2) scrollHandleLength = thickness / 2;
    scrollHandlePosition = (graphicRange - scrollHandleLength) * ((value - minValue) / numRange);

    if (align == horizontal)
    {
        height = thickness;
        width = length;
    }
    else
    {
        width = thickness;
        height = length;
    }
}
/// ----------------------------------------------------------------------------------------------------------------


/// --------------------------------------------- Get/Set ----------------------------------------------------------
void scrollBar::setValue(int newValue)
{
    if (newValue < minValue) value = minValue;
    else if (maxValue < newValue + valueWidth) value = maxValue -  valueWidth;
    else value = newValue;

    scrollHandlePosition = (graphicRange - scrollHandleLength) * (value - minValue) / (numRange - valueWidth);
}

int scrollBar::getValue() const
    { return value; }

bool scrollBar::setRange(int minV, int maxV)
{
    if (minV > maxV)
    {
        minValue = 0;
        maxValue = 100;
        std::cerr << "Incorrect range! (minValue is greater or equal to maxValue) : scrollbar" + convert(cnt) + " - setRange" << std::endl;
        return false;
    }

    minValue = minV;
    maxValue = maxV;

    numRange = maxValue - minValue;
    if (numRange == 0) numRange = 1;

    // scrollHandleLength hosszának a meghatározása
    if (valueWidth == 0) scrollHandleLength = graphicRange/numRange;
    else scrollHandleLength = graphicRange/numRange * valueWidth;
    // Ha túl rövid lenne...
    if (scrollHandleLength < thickness/2) scrollHandleLength = thickness / 2;

    // scrollHandle helyének meghatározása
    scrollHandlePosition = (graphicRange - scrollHandleLength) * (value - minValue) / (numRange - valueWidth);

    return true;
}

int scrollBar::getMinValue() const
    { return minValue; }

int scrollBar::getMaxValue() const
    { return maxValue; }

void scrollBar::setValueWidth(unsigned int newValue)
{
    if (newValue == 0) valueWidth = 1;
    else if (numRange < newValue) valueWidth = numRange;
    else valueWidth = newValue;

    if (maxValue < value + valueWidth) value = minValue;

    scrollHandleLength = graphicRange/numRange * valueWidth;
    if (scrollHandleLength < thickness / 2) scrollHandleLength = thickness / 2;
}

unsigned int scrollBar::getValueWidth() const
    { return valueWidth; }

unsigned int scrollBar::getNumRange() const
    { return numRange; }
/// ----------------------------------------------------------------------------------------------------------------


/// --------------------------------- Mûködéshez szükséges függvények ------------------------------------------------
enum directions { left, up, right, down };

void drawArrow(int x, int y, int thickness, directions d)
{
    colorize(Black);

    switch (d)
    {
    case left :
        gout << move_to(x + thickness*2/3, y + thickness/3) << line(-thickness/3, thickness/3) << line(thickness/3, thickness/3);
        break;
    case up :
        gout << move_to(x + thickness/3, y + thickness*2/3) << line(thickness/3, -thickness/3) << line(thickness/3, thickness/3);
        break;
    case right :
        gout << move_to(x + thickness/3, y + thickness/3) << line(thickness/3, thickness/3) << line(-thickness/3, thickness/3);
        break;
    case down :
        gout << move_to(x + thickness/3, y + thickness/3) << line(thickness/3, thickness/3) << line(thickness/3, -thickness/3);
        break;
    }
}

void scrollBar::draw() const
{
    if (!visible) return;

    if (X < x + width) std::cerr << "Object is out of window! (X < x + width) - scrollBar" + convert(cnt) + " - scrollBar::draw" << std::endl;
    else if (Y < y + height) std::cerr << "Object is out of window! (Y < y + height) - scrollBar" + convert(cnt) + " - scrollBar::draw" << std::endl;

    if (focus) colorize(ActiveColor);
    else colorize(Gray);
    gout << move_to(x,y) << box(width, height);

if (alignment == horizontal)
    {
        colorize(ButtonColor);
            gout << move_to(x + thickness + scrollHandlePosition, y) << box(scrollHandleLength, thickness);

            gout << move_to(x, y) << box(thickness, thickness);
            gout << move_to(x + width - thickness, y) << box(thickness, thickness);

        rectange(x, y, thickness, thickness);
        rectange(x + width - thickness, y, thickness, thickness);
        drawArrow(x, y, thickness, left);
        drawArrow(x + width - thickness, y, thickness, right);

        rectange(x + thickness + scrollHandlePosition, y, scrollHandleLength, thickness);
    }
    else if (alignment == vertical)
    {
        colorize(ButtonColor);
            gout << move_to(x, y + thickness + scrollHandlePosition) << box(thickness, scrollHandleLength);

            gout << move_to(x, y) << box(thickness, thickness);
            gout << move_to(x, y + height - thickness) << box(thickness, thickness);

        rectange(x, y, thickness, thickness);
        rectange(x, y + height - thickness, thickness, thickness);
        drawArrow(x, y, thickness, up);
        drawArrow(x, y + height - thickness, thickness, down);

        rectange(x, y + thickness + scrollHandlePosition, thickness, scrollHandleLength);
    }
}
/// ----------------------------------------------------------------------------------------------------------------

