/***********************************************************************************

    Copyright (C) 2007-2020 Ahmet Öztürk (aoz_2@yahoo.com)

    This file is part of Lifeograph.

    Lifeograph is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Lifeograph 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Lifeograph.  If not, see <http://www.gnu.org/licenses/>.

***********************************************************************************/


#include <cairomm/context.h>

#include "../strings.hpp"
#include "../diarydata.hpp"
#include "../app_window.hpp"
#include "widget_calendar.hpp"


using namespace LIFEO;

// WIDGET CALENDAR =================================================================================
WidgetCalendar::WidgetCalendar()
{
    set_size_request( 300, 200 );

    set_events( Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK |
                Gdk::POINTER_MOTION_MASK | Gdk::LEAVE_NOTIFY_MASK | Gdk::SCROLL_MASK );

    m_builder = Gtk::Builder::create();
    Lifeograph::load_gui( m_builder, Lifeograph::SHAREDIR + "/ui/calendar.ui" );

    Gtk::Button* B_copy;
    m_builder->get_widget( "Po_calendar_day", m_Po_day );
    m_builder->get_widget( "Bx_calendar_add", m_Bx_add );
    m_builder->get_widget( "B_copy", B_copy );

    m_Po_day->set_relative_to( *this );

    m_Po_day->signal_closed().connect( [ this ](){ m_flag_selection_locked = false; } );

    B_copy->signal_clicked().connect(
            [ this ]()
            {
                Gtk::Clipboard::get()->set_text( Date::format_string( m_day_selected ) );
            } );

    m_month.set_now();
    m_month.set_day( 1 );
}

void
WidgetCalendar::set_editable( bool flag_editable )
{
    m_flag_editable = flag_editable;

    if( flag_editable )
        drag_dest_set( { Lifeograph::p->DTE_text_plain } );
    else
        drag_dest_unset();
}

void
WidgetCalendar::show_Po( int cell_x, int cell_y )
{
    Gdk::Rectangle rect{ int( s_margin + m_col_width * cell_x ),
                         int( s_grid_start_y + m_row_height * cell_y ),
                         int( m_col_width ), int( m_row_height ) };

    m_flag_selection_locked = true;

    m_Bx_add->set_visible( m_flag_editable );

    m_Po_day->set_pointing_to( rect );
    m_Po_day->show();
}

void
WidgetCalendar::set_selected_day_from_coords( int x, int y )
{
    int cell_x = int( floor( ( x - s_margin ) / m_col_width ) );
    int cell_y = int( floor( ( y - s_grid_start_y ) / m_row_height ) );
    date_t begin_date{ m_month.m_date };

    if( cell_x < 0 || cell_y < 0 || cell_x > 6 || cell_y > 5 )
        begin_date = Date::NOT_SET;
    else
    {
        int cell_index = cell_y * 7 + cell_x;
        calculate_begin_date( begin_date );
        Date::forward_days( begin_date, cell_index );
    }

    if( Date::is_set( begin_date ) != Date::is_set( m_day_selected ) )
    {
        if( begin_date == Date::NOT_SET )
            drag_source_unset();
        else
            drag_source_set( { Lifeograph::p->DTE_text_plain } );
    }

    if( begin_date != m_day_selected )
    {
        m_day_selected = begin_date;
        update();
    }
}

bool
WidgetCalendar::on_scroll_event( GdkEventScroll* event )
{
    if( event->direction == GDK_SCROLL_UP )
    {
        m_month.backward_months( event->state & Gdk::CONTROL_MASK ? 12  : 1 );
        m_month.set_day( 1 ); // should always point to day 1
        update();
    }
    else
    if( event->direction == GDK_SCROLL_DOWN )
    {
        m_month.forward_months( event->state & Gdk::CONTROL_MASK ? 12  : 1 );
        update();
    }

    return true;
}

bool
WidgetCalendar::on_button_press_event( GdkEventButton* event )
{
    if( event->type == GDK_DOUBLE_BUTTON_PRESS )
    {
        PRINT_DEBUG( "double_click" );
        m_Sg_day_add.emit( m_day_selected, "" );
        return true;
    }
    switch( event->button )
    {
        case 1:
            m_Sg_day_selected.emit( m_day_selected );
            break;
        case 3:
            if( m_day_selected != Date::NOT_SET )
                show_Po( int( floor( ( event->x - s_margin ) / m_col_width ) ),
                         int( floor( ( event->y - s_grid_start_y ) / m_row_height ) ) );
            break;
    }

    return true;
}

//bool
//WidgetCalendar::on_button_release_event( GdkEventButton* event )
//{
//    if( event->button == 1 )
//
//    return true;
//}

bool
WidgetCalendar::on_motion_notify_event( GdkEventMotion* event )
{
    set_selected_day_from_coords( event->x, event->y );

    return Gtk::DrawingArea::on_motion_notify_event( event );
}

bool
WidgetCalendar::on_leave_notify_event( GdkEventCrossing* event )
{
    if( m_day_selected != Date::NOT_SET && ! m_flag_selection_locked )
    {
        m_day_selected = Date::NOT_SET;
        update();
        drag_source_unset();
    }

    return true;
}

void
WidgetCalendar::on_size_allocate( Gtk::Allocation& allocation )
{
    Gtk::Widget::on_size_allocate( allocation );
    resize( allocation.get_width(), allocation.get_height() );
}

bool
WidgetCalendar::on_draw( const Cairo::RefPtr< Cairo::Context >& cr )
{
    return( Diary::d->is_open() ? Calendar::draw( cr ) : false );
}

void
WidgetCalendar::on_drag_begin( const Glib::RefPtr< Gdk::DragContext >& ctx )
{
    m_flag_selection_locked = true;
    ctx->set_icon_name( "x-office-calendar-symbolic", 0, 0 );
}

void
WidgetCalendar::on_drag_data_get( const Glib::RefPtr< Gdk::DragContext >& ctx,
                                  Gtk::SelectionData& data, guint info, guint time )
{
    data.set_text( Date::format_string( m_day_selected ) );
}

void
WidgetCalendar::on_drag_end( const Glib::RefPtr< Gdk::DragContext >& ctx )
{
    Gtk::DrawingArea::on_drag_end( ctx );
    m_flag_selection_locked = false;
}

bool
WidgetCalendar::on_drag_motion( const Glib::RefPtr< Gdk::DragContext >& ctx,
                                int x, int y, guint time )
{
    if( m_flag_selection_locked == false && drag_dest_find_target( ctx ) != "NONE" )
    {
        set_selected_day_from_coords( x, y );

        if( m_day_selected != Date::NOT_SET )
        {
            ctx->drag_status( ctx->get_suggested_action(), time );
            return true;
        }
    }

    ctx->drag_status( Gdk::DragAction( 0 ), time );
    return false;
}

bool
WidgetCalendar::on_drag_drop( const Glib::RefPtr< Gdk::DragContext >& ctx,
                              int x, int y, guint time )
{
    if( m_day_selected != Date::NOT_SET && drag_dest_find_target( ctx ) == TE_TEXT_PLAIN )
    {
        drag_get_data( ctx, TE_TEXT_PLAIN, time );
        // below flag is needed to prevent double receiving of drag data, this is all we know:
        m_flag_expect_drag_data = true;
        return true;
    }

    ctx->drag_finish( false, false, time );
    return false;
}

void
WidgetCalendar::on_drag_data_received( const Glib::RefPtr<Gdk::DragContext>& ctx,
                                       int, int, const Gtk::SelectionData& sel_data,
                                       guint, guint time )
{
    if( m_flag_expect_drag_data && drag_dest_find_target( ctx ) == TE_TEXT_PLAIN )
    {
        m_flag_expect_drag_data = false;

        m_Sg_day_add.emit( m_day_selected, sel_data.get_text() );

        ctx->drag_finish( true, false, time );
    }
    else
        ctx->drag_finish( false, false, time );
}
