#include "ImageButton.h"
#include <gdkmm/general.h>
#include <iostream>

ImageButton::ImageButton()
    : Gtk::DrawingArea()
    , m_stImage()
    , m_currentSt(StateImage::STATE_NORMAL)
    , m_cornerRadius(0)
    , m_realCornerRd(0)
    , m_realWidth(0)
    , m_realHeight(0) {
    add_events(Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::LEAVE_NOTIFY_MASK);

    m_eventIter = signal_event().connect(sigc::mem_fun(*this, &ImageButton::onEventReceived));
    m_drawIter = signal_draw().connect(sigc::mem_fun(*this, &ImageButton::onImageButtonRealDraw));

    set_can_focus(true);
}

ImageButton::~ImageButton() {
    m_drawIter.disconnect();
    m_eventIter.disconnect();
}

void ImageButton::setStateImage(const StateImage& stImage) {
    m_stImage = stImage;
}

void ImageButton::setCornerRadius(uint32_t radius) {
    m_cornerRadius = radius;
}

sigc::signal<void> ImageButton::signalClicked() {
    return m_clickedSignal;
}

bool ImageButton::onImageButtonRealDraw(const ::Cairo::RefPtr< ::Cairo::Context>& cr) {
    Glib::ustring currentRes;
    StateImage::PathArray pathArray = m_stImage.getStateImages();
    currentRes = pathArray[m_currentSt];

    Gtk::StateFlags stateflg = get_state_flags();

    if (currentRes.empty()) {
        currentRes = pathArray[StateImage::STATE_NORMAL];
    }

    int width = get_allocated_width ();
    int height = get_allocated_height ();
    if (0 >= width || 0 >= height || currentRes.empty()) {
        return false;
    }
    double degrees = M_PI / 180.0;

    Glib::RefPtr<Gdk::Pixbuf> facebuf = Gdk::Pixbuf::create_from_file(currentRes, width, height);

    m_realWidth = facebuf->get_width ();
    m_realHeight = facebuf->get_height ();
    int posX = (width - m_realWidth) / 2;
    int posY = (height - m_realHeight) / 2;
    m_realCornerRd = MIN(m_cornerRadius, MIN(m_realWidth / 2, m_realHeight / 2));

    if (0 < m_realCornerRd) {
        cr->begin_new_sub_path ();
        cr->arc(posX + m_realWidth - m_realCornerRd, posY + m_realCornerRd, m_realCornerRd, -90 * degrees, 0 * degrees);
        cr->arc(posX + m_realWidth - m_realCornerRd, posY + m_realHeight - m_realCornerRd, m_realCornerRd, 0 * degrees, 90 * degrees);
        cr->arc(posX + m_realCornerRd, posY + m_realHeight - m_realCornerRd, m_realCornerRd, 90 * degrees, 180 * degrees);
        cr->arc(posX + m_realCornerRd, posY + m_realCornerRd, m_realCornerRd, 180 * degrees, 270 * degrees);
        cr->close_path ();
    } else {
        cr->rectangle(posX, posY, m_realWidth, m_realHeight);
    }
    cr->clip();

    Gdk::Cairo::set_source_pixbuf(cr, facebuf, posX, posY);
    cr->paint ();

    if (0 != (stateflg & Gtk::STATE_FLAG_FOCUSED)) {
        cr->save();
        cr->set_line_width(2.0f);
        cr->set_source_rgb(1.0f, 1.0f, 1.0f);
        if (0 < m_realCornerRd) {
            cr->arc(posX + m_realWidth - m_realCornerRd, posY + m_realCornerRd, m_realCornerRd, 0, 2 * M_PI);
        } else {
            cr->rectangle(posX, posY, m_realWidth, m_realHeight);
        }
        cr->stroke();
        cr->restore();
    }
    return FALSE;
}

bool ImageButton::onEventReceived(GdkEvent* event) {
    switch (event->type) {
    case GDK_MOTION_NOTIFY: {
        GdkEventMotion* motion = (GdkEventMotion*)event;
        if (!checkPointInside(motion->x, motion->y)) {
            m_currentSt = StateImage::STATE_NORMAL;
            break;
        }
        if (StateImage::STATE_PRESSED != m_currentSt) {
            m_currentSt = StateImage::STATE_HOVER;
        }
        break;
    }
    case GDK_BUTTON_PRESS: {
        GdkEventButton* btnEvent = (GdkEventButton*)event;
        if (!checkPointInside(btnEvent->x, btnEvent->y)) {
            m_currentSt = StateImage::STATE_NORMAL;
            break;
        }
        m_currentSt = StateImage::STATE_PRESSED;
        break;
    }
    case GDK_BUTTON_RELEASE: {
        GdkEventButton* btnEvent = (GdkEventButton*)event;
        if (checkPointInside(btnEvent->x, btnEvent->y)) {
            m_currentSt = StateImage::STATE_HOVER;
            break;
        }
        m_currentSt = StateImage::STATE_NORMAL;
        break;
    }
    case GDK_LEAVE_NOTIFY:
        m_currentSt = StateImage::STATE_NORMAL;
        break;
    default:
        return FALSE;
    }
    queue_draw();
    return FALSE;
}

bool ImageButton::checkPointInside(int x, int y) {
    int width = get_allocated_width ();
    int height = get_allocated_height ();
    int centerX = width / 2;
    int centerY = height / 2;

    x = std::abs(x - centerX);
    y = std::abs(y - centerY);

    if (0 > x || 0 > y || (m_realWidth / 2 < x) || (m_realHeight / 2 < y)) {
        return false;
    }

    if ((x >= 0 && x <= m_realWidth / 2 - m_realCornerRd)
        ||(y >= 0 && y <= m_realHeight / 2 - m_realCornerRd)) {
        return true;
    } else {
        x = x - (m_realWidth / 2 - m_realCornerRd);
        y = y - (m_realHeight / 2 - m_realCornerRd);
        int pointlen = std::sqrt((x * x + y * y));
        if (m_realCornerRd >= pointlen) {
            return true;
        }    
    }
    
    return false;
}