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

UserFace::UserFace()
    : Gtk::DrawingArea()
    , m_stImage()
    , m_currentSt(StateImage::STATE_NORMAL) {
    add_events(Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK
              | Gdk::BUTTON_RELEASE_MASK
              | Gdk::LEAVE_NOTIFY_MASK);

    m_sigEventIter = signal_event().connect(sigc::mem_fun(*this, &UserFace::onEventReceived));
    m_sigDrawIter = signal_draw().connect(sigc::mem_fun(*this, &UserFace::onUserFaceRealDraw));

    set_can_focus(true);
}

UserFace::~UserFace() {
    m_sigDrawIter.disconnect();
    m_sigEventIter.disconnect();
}

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

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

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

    int width = get_allocated_width();
    int height = get_allocated_height();
    int radius = MIN(width, height) / 2;

    if (0 >= width || 0 >= height || currentRes.empty() || 0 >= radius) {
        return false;
    }

    cr->arc(width / 2, height / 2, radius, 0, 2 * G_PI);
    cr->clip();

    Glib::RefPtr<Gdk::Pixbuf> facebuf
        = Gdk::Pixbuf::create_from_file(currentRes, radius * 2, radius * 2);

    Gdk::Cairo::set_source_pixbuf(cr,
                                  facebuf,
                                  width / 2 - radius,
                                  height / 2 - radius);
    cr->paint();
    if (needHover) {
        Cairo::RefPtr<Cairo::RadialGradient> pat = Cairo::RadialGradient::create (width / 2 , height / 2, radius,
                                   width / 2 ,  height / 2, radius * 2 / 3);
        pat->add_color_stop_rgba (0, 1, 1, 1, 0.5);
        pat->add_color_stop_rgba (1, 1, 1, 1, 0.35);
        cr->set_source(pat);
        cr->arc(width / 2, height / 2, radius, 0, 2*G_PI);
        cr->fill();
    }

    Gtk::StateFlags stateflg = get_state_flags();
    if (0 != (stateflg & Gtk::STATE_FLAG_FOCUSED)) {
        cr->set_source_rgba(1.0f, 1.0f, 1.0f, 0.4f);
        cr->save();
        cr->arc(width / 2, height / 2, radius - 3, 0, 2*G_PI);
        cr->fill();
        cr->restore();

        cr->save();
        cr->set_line_width(2);
        cr->arc(width / 2, height / 2, radius, 0, 2*G_PI);
        cr->stroke();
        cr->restore();

        cr->save();
        cr->set_source_rgba(0.0f, 0.0f, 0.0f, 0.3f);
        cr->set_line_width(1);
        cr->arc(width / 2, height / 2, radius - 2, 0, 2*G_PI);
        cr->stroke();
        cr->restore();
    }
    return false;
}

bool UserFace::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 UserFace::checkPointInside(int x, int y) {
    int width = get_allocated_width();
    int height = get_allocated_height();
    int radius = MIN(width, height) / 2;
    int centerx = width / 2;
    int centery = height / 2;

    x = x - centerx;
    y = y - centery;

    int pointlen = std::sqrt((x * x + y * y));
    if (radius >= pointlen) {
        return true;
    }
    return false;
}