#include "keyframeview.h"

#include "project/effect.h"
#include "ui/collapsiblewidget.h"
#include "panels/panels.h"
#include "panels/effectcontrols.h"
#include "project/clip.h"
#include "panels/timeline.h"
#include "ui/timelineheader.h"
#include "project/undo.h"
#include "panels/viewer.h"
#include "ui/viewerwidget.h"
#include "project/sequence.h"
#include "ui_effectcontrols.h"

#include <QLabel>
#include <QMouseEvent>
#include <QtMath>
#include <QMenu>

#define KEYFRAME_SIZE 6
#define KEYFRAME_POINT_COUNT 4

long KeyframeView::adjust_row_keyframe(EffectRow* row, long time) {
	return time-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in);
}

KeyframeView::KeyframeView(QWidget *parent) :
	QWidget(parent),
    visible_in(0),
    visible_out(0),
	mousedown(false),
	dragging(false),
	keys_selected(false),
	select_rect(false),	
	x_scroll(0),
	y_scroll(0)
{
	setFocusPolicy(Qt::ClickFocus);
	setMouseTracking(true);

    setContextMenuPolicy(Qt::CustomContextMenu);
    connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu(const QPoint&)));
}

void KeyframeView::show_context_menu(const QPoint& pos) {
    if (selected_rows.size() > 0) {
        QMenu menu(this);
        QAction* linear = menu.addAction("Linear");
        linear->setData(KEYFRAME_TYPE_LINEAR);
        QAction* smooth = menu.addAction("Smooth");
        smooth->setData(KEYFRAME_TYPE_SMOOTH);
        /*QAction* bezier = menu.addAction("Bezier");
        bezier->setData(KEYFRAME_TYPE_BEZIER);*/
        connect(&menu, SIGNAL(triggered(QAction*)), this, SLOT(menu_set_key_type(QAction*)));
        menu.exec(mapToGlobal(pos));
    }
}

void KeyframeView::menu_set_key_type(QAction* a) {
    ComboAction* ca = new ComboAction();
    for (int i=0;i<selected_rows.size();i++) {
        ca->append(new SetInt(&selected_rows.at(i)->keyframe_types[selected_keyframes.at(i)], a->data().toInt()));
    }
    undo_stack.push(ca);
    update();
}

void KeyframeView::paintEvent(QPaintEvent*) {
	QPainter p(this);

	rowY.clear();
	rows.clear();

	if (panel_effect_controls->selected_clips.size() > 0) {
		visible_in = LONG_MAX;
		visible_out = 0;

		for (int j=0;j<panel_effect_controls->selected_clips.size();j++) {
			Clip* c = sequence->clips.at(panel_effect_controls->selected_clips.at(j));
			visible_in = qMin(visible_in, c->timeline_in);
			visible_out = qMax(visible_out, c->timeline_out);
		}

		for (int j=0;j<panel_effect_controls->selected_clips.size();j++) {
			Clip* c = sequence->clips.at(panel_effect_controls->selected_clips.at(j));
			for (int i=0;i<c->effects.size();i++) {
				Effect* e = c->effects.at(i);
				if (e->container->is_expanded()) {
					for (int j=0;j<e->row_count();j++) {
						EffectRow* row = e->row(j);

						QLabel* label = row->label;
						QWidget* contents = e->container->contents;

						int keyframe_y = label->y() + (label->height()>>1) + mapFrom(panel_effect_controls, contents->mapTo(panel_effect_controls, contents->pos())).y() - e->container->title_bar->height()/* - y_scroll*/;
						for (int k=0;k<row->keyframe_times.size();k++) {
							bool keyframe_selected = keyframeIsSelected(row, k);
							long keyframe_frame = adjust_row_keyframe(row, row->keyframe_times.at(k));
							if (dragging && keyframe_selected) keyframe_frame += frame_diff;
                            draw_keyframe(p, row->keyframe_types.at(k), getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected);
						}

						rows.append(row);
						rowY.append(keyframe_y);
					}
				}
			}
		}

		int max_width = getScreenPointFromFrame(panel_effect_controls->zoom, visible_out - visible_in);
		if (max_width < width()) {
			p.fillRect(QRect(max_width, 0, width(), height()), QColor(0, 0, 0, 64));
		}
		panel_effect_controls->ui->horizontalScrollBar->setMaximum(qMax(max_width - width(), 0));
		header->set_visible_in(visible_in);

		int playhead_x = getScreenPointFromFrame(panel_effect_controls->zoom, sequence->playhead-visible_in) - x_scroll;
		if (dragging && panel_timeline->snapped) {
			p.setPen(Qt::white);
		} else {
			p.setPen(Qt::red);
		}
		p.drawLine(playhead_x, 0, playhead_x, height());
	}

	if (select_rect) {
		draw_selection_rectangle(p, QRect(rect_select_x, rect_select_y, rect_select_w, rect_select_h));
	}

    /*if (mouseover && mouseover_row < rowY.size()) {
		draw_keyframe(p, getScreenPointFromFrame(panel_effect_controls->zoom, mouseover_frame - visible_in), rowY.at(mouseover_row), true);
    }*/
}

bool KeyframeView::keyframeIsSelected(EffectRow *row, int keyframe) {
	for (int i=0;i<selected_rows.size();i++) {
		if (selected_rows.at(i) == row && selected_keyframes.at(i) == keyframe) {
			return true;
		}
	}
	return false;
}

void KeyframeView::delete_selected_keyframes() {
	KeyframeDelete* kd = new KeyframeDelete();
	bool del = false;
	for (int i=0;i<selected_keyframes.size();i++) {
		selected_rows.at(i)->delete_keyframe(kd, selected_keyframes.at(i));
		del = true;
	}
	if (del) {
		undo_stack.push(kd);

		selected_keyframes.clear();
		selected_rows.clear();
		update();
		panel_sequence_viewer->viewer_widget->update();
	} else {
		delete kd;
	}
}

void KeyframeView::set_x_scroll(int s) {
	x_scroll = s;
	update();
}

void KeyframeView::set_y_scroll(int s) {
	y_scroll = s;
	update();
}

void KeyframeView::draw_keyframe(QPainter &p, int type, int x, int y, bool darker) {
    int color = (darker) ? 100 : 160;
    p.setPen(QColor(0, 0, 0));
    p.setBrush(QColor(color, color, color));

    switch (type) {
    case KEYFRAME_TYPE_LINEAR:
    {
        QPoint points[KEYFRAME_POINT_COUNT] = {QPoint(x-KEYFRAME_SIZE, y), QPoint(x, y-KEYFRAME_SIZE), QPoint(x+KEYFRAME_SIZE, y), QPoint(x, y+KEYFRAME_SIZE)};
        p.drawPolygon(points, KEYFRAME_POINT_COUNT);
    }
        break;
    case KEYFRAME_TYPE_SMOOTH:
        p.drawEllipse(QPoint(x, y), KEYFRAME_SIZE, KEYFRAME_SIZE);
        break;
    }
}

void KeyframeView::mousePressEvent(QMouseEvent *event) {
    if (event->button() == Qt::LeftButton) {
        int row_index = -1;
        int keyframe_index = -1;
        long frame_diff = 0;
        int mouse_x = event->x() + x_scroll;
        int mouse_y = event->y();
        long frame_min = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x-KEYFRAME_SIZE);
        drag_frame_start = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x);
        long frame_max = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x+KEYFRAME_SIZE);
        for (int i=0;i<rowY.size();i++) {
            if (mouse_y > rowY.at(i)-KEYFRAME_SIZE-KEYFRAME_SIZE && mouse_y < rowY.at(i)+KEYFRAME_SIZE+KEYFRAME_SIZE) {
                EffectRow* row = rows.at(i);
                for (int j=0;j<row->keyframe_times.size();j++) {
                    long eval_keyframe_time = row->keyframe_times.at(j)-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in);
                    if (eval_keyframe_time >= frame_min && eval_keyframe_time <= frame_max) {
                        long eval_frame_diff = qAbs(eval_keyframe_time - drag_frame_start);
                        if (keyframe_index == -1 || eval_frame_diff < frame_diff) {
                            row_index = i;
                            keyframe_index = j;
                            frame_diff = eval_frame_diff;
                        }
                    }
                }
                break;
            }
        }
        bool already_selected = false;
        keys_selected = false;
        if (keyframe_index > -1) already_selected = keyframeIsSelected(rows.at(row_index), keyframe_index);
        if (!already_selected) {
            if (!(event->modifiers() & Qt::ShiftModifier)) {
                selected_rows.clear();
                selected_keyframes.clear();
            }
            if (keyframe_index > -1) {
                selected_rows.append(rows.at(row_index));
                selected_keyframes.append(keyframe_index);
            }
        }

        if (selected_rows.size() > 0) {
            keys_selected = true;
        } else {
            rect_select_x = event->x();
            rect_select_y = event->y();
        }

        update();
        mousedown = true;
    }
}

void KeyframeView::mouseMoveEvent(QMouseEvent* event) {
	if (mousedown) {
		int mouse_x = event->x() + x_scroll;
		if (keys_selected) {
			// move keyframes
			frame_diff = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x) - drag_frame_start;

			// snapping to playhead
			if (panel_timeline->snapping) {
				for (int i=0;i<selected_keyframes.size();i++) {
					EffectRow* row = selected_rows.at(i);
					Clip* c = row->parent_effect->parent_clip;
					long key_time = row->keyframe_times.at(selected_keyframes.at(i)) + frame_diff - c->clip_in + c->timeline_in;
					long key_eval = key_time;
					if (panel_timeline->snap_to_point(sequence->playhead, &key_eval)) {
						frame_diff += (key_eval - key_time);
						break;
					}
				}
			}

			// validate frame_diff (make sure no keyframes overlap each other)
			for (int i=0;i<selected_keyframes.size();i++) {
				EffectRow* row = selected_rows.at(i);
				long eval_key = row->keyframe_times.at(selected_keyframes.at(i));
				for (int j=0;j<row->keyframe_times.size();j++) {
					while (!keyframeIsSelected(row, j) && row->keyframe_times.at(j) == eval_key + frame_diff) {
						if (last_frame_diff > frame_diff) {
							frame_diff++;
							panel_timeline->snapped = false;
						} else {
							frame_diff--;
							panel_timeline->snapped = false;
						}
					}
				}
			}

			last_frame_diff = frame_diff;

			dragging = true;
		} else {
			// do a rect select
			rect_select_w = event->x() - rect_select_x;
			rect_select_h = event->y() - rect_select_y;

			int min_row = qMin(rect_select_y, event->y())-KEYFRAME_SIZE;
			int max_row = qMax(rect_select_y, event->y())+KEYFRAME_SIZE;

			long frame_start = getFrameFromScreenPoint(panel_effect_controls->zoom, rect_select_x+x_scroll);
			long frame_end = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x);
			long min_frame = qMin(frame_start, frame_end)-KEYFRAME_SIZE;
			long max_frame = qMax(frame_start, frame_end)+KEYFRAME_SIZE;

			for (int i=0;i<rowY.size();i++) {
				if (rowY.at(i) >= min_row && rowY.at(i) <= max_row) {
					EffectRow* row = rows.at(i);
					for (int j=0;j<row->keyframe_times.size();j++) {
						long keyframe_frame = adjust_row_keyframe(row, row->keyframe_times.at(j));
						if (!keyframeIsSelected(row, j) && keyframe_frame >= min_frame && keyframe_frame <= max_frame) {
							selected_rows.append(rows.at(i));
							selected_keyframes.append(j);
						}
					}
				}
			}

			select_rect = true;
		}
		update();
    }
}

void KeyframeView::mouseReleaseEvent(QMouseEvent*) {
	if (dragging && frame_diff != 0) {
		KeyframeMove* ka = new KeyframeMove();
		ka->movement = frame_diff;
		ka->keyframes = selected_keyframes;
		ka->rows = selected_rows;
		undo_stack.push(ka);
	}

	select_rect = false;
	dragging = false;
    mousedown = false;
	panel_timeline->snapped = false;
	update();
}
