/*
 * DISTRHO Plugin Toolkit (DPT)
 * Copyright (C) 2012-2013 Filipe Coelho <falktx@falktx.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * For a full copy of the license see the LGPL.txt file
 */

#include "../ImageKnob.hpp"

START_NAMESPACE_DGL

// -------------------------------------------------

ImageKnob::ImageKnob(Window* parent, const Image& image, Orientation orientation)
    : Widget(parent),
      fImage(image),
      fMinimum(0.0f),
      fMaximum(1.0f),
      fValue(0.5f),
      fOrientation(orientation),
      fDragging(false),
      fLastX(0),
      fLastY(0),
      fCallback(nullptr),
      fIsImgVertical(image.getHeight() > image.getWidth()),
      fImgLayerSize(fIsImgVertical ? image.getWidth() : image.getHeight()),
      fImgLayerCount(fIsImgVertical ? image.getHeight()/fImgLayerSize : image.getWidth()/fImgLayerSize),
      fKnobArea(0, 0, fImgLayerSize, fImgLayerSize)
{
    setSize(fImgLayerSize, fImgLayerSize);
}

ImageKnob::ImageKnob(const ImageKnob& imageKnob)
    : Widget(imageKnob.getParent()),
      fImage(imageKnob.fImage),
      fMinimum(imageKnob.fMinimum),
      fMaximum(imageKnob.fMaximum),
      fValue(imageKnob.fValue),
      fOrientation(imageKnob.fOrientation),
      fDragging(false),
      fLastX(0),
      fLastY(0),
      fCallback(imageKnob.fCallback),
      fIsImgVertical(imageKnob.fIsImgVertical),
      fImgLayerSize(imageKnob.fImgLayerSize),
      fImgLayerCount(imageKnob.fImgLayerCount),
      fKnobArea(imageKnob.fKnobArea)
{
    setSize(fImgLayerSize, fImgLayerSize);
}

float ImageKnob::getValue() const
{
    return fValue;
}

void ImageKnob::setOrientation(Orientation orientation)
{
    if (fOrientation == orientation)
        return;

    fOrientation = orientation;
}

void ImageKnob::setRange(float min, float max)
{
    if (fValue < min)
    {
        fValue = min;
        repaint();

        if (fCallback != nullptr)
            fCallback->imageKnobValueChanged(this, fValue);
    }
    else if (fValue > max)
    {
        fValue = max;
        repaint();

        if (fCallback != nullptr)
            fCallback->imageKnobValueChanged(this, fValue);
    }

    fMinimum = min;
    fMaximum = max;
}

void ImageKnob::setValue(float value, bool sendCallback)
{
    if (fValue == value)
        return;

    fValue = value;
    repaint();

    if (sendCallback && fCallback != nullptr)
        fCallback->imageKnobValueChanged(this, fValue);
}

void ImageKnob::setCallback(Callback* callback)
{
    fCallback = callback;
}

void ImageKnob::onDisplay()
{
    float normValue = (fValue - fMinimum) / (fMaximum - fMinimum);

    // FIXME: assuming GL_BGRA data (* 4)
    int layerDataSize   = fImgLayerSize * fImgLayerSize * 4;
    int imageDataSize   = layerDataSize * fImgLayerCount;
    int imageDataOffset = imageDataSize - layerDataSize - (layerDataSize * int(normValue * float(fImgLayerCount-1)));

    glRasterPos2i(getX(), getY()+getHeight());
    glDrawPixels(fImgLayerSize, fImgLayerSize, fImage.getFormat(), fImage.getType(), fImage.getRawData() + imageDataOffset);
}

bool ImageKnob::onMouse(int button, bool press, int x, int y)
{
    if (button != 1)
        return false;

    if (press)
    {
        if (! getArea().contains(x, y))
            return false;

        fDragging = true;
        fLastX = x;
        fLastY = y;

        if (fCallback != nullptr)
            fCallback->imageKnobDragStarted(this);

        return true;
    }
    else if (fDragging)
    {
        if (fCallback != nullptr)
            fCallback->imageKnobDragFinished(this);

        fDragging = false;
        return true;
    }

    return false;
}

bool ImageKnob::onMotion(int x, int y)
{
    if (! fDragging)
        return false;

    if (fOrientation == ImageKnob::Horizontal)
    {
        int movX = x - fLastX;

        if (movX != 0)
        {
            float d     = (getModifiers() & DGL_MODIFIER_SHIFT) ? 2000.0f : 200.0f;
            float value = fValue + (float(fMaximum - fMinimum) / d * float(movX));

            if (value < fMinimum)
                value = fMinimum;
            else if (value > fMaximum)
                value = fMaximum;

            setValue(value, true);
        }
    }
    else if (fOrientation == ImageKnob::Vertical)
    {
        int movY = fLastY - y;

        if (movY != 0)
        {
            float d     = (getModifiers() & DGL_MODIFIER_SHIFT) ? 2000.0f : 200.0f;
            float value = fValue + (float(fMaximum - fMinimum) / d * float(movY));

            if (value < fMinimum)
                value = fMinimum;
            else if (value > fMaximum)
                value = fMaximum;

            setValue(value, true);
        }
    }

    fLastX = x;
    fLastY = y;

    return true;
}

// -------------------------------------------------

END_NAMESPACE_DGL
