/***************************************************************************
                          trackpanelcliprollfunction  -  description
                             -------------------
    begin                : Wed Aug 25 2004
    copyright            : (C) 2004 by Jason Wood
    email                : jasonwood@blueyonder.co.uk
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program 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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
#include "trackpanelcliprollfunction.h"

#include "kdebug.h"

#include <qnamespace.h>
#include <qstring.h>

#include "clipdrag.h"
#include "doctrackbase.h"
#include "kdenlive.h"
#include "ktimeline.h"
#include "krollcommand.h"
#include "kselectclipcommand.h"
#include "ktrackview.h"

#include "kmmtimeline.h"

#include <cmath>

// static
const uint TrackPanelClipRollFunction::s_resizeTolerance = 5;

TrackPanelClipRollFunction::TrackPanelClipRollFunction(Gui::KdenliveApp * app, Gui::KTimeLine * timeline, KdenliveDoc * document):
m_app(app),
m_timeline(timeline),
m_document(document),
m_clipUnderMouse(0), m_resizeState(None), m_rollCommand(0), m_snapToGrid(), m_clipBeforeMouse(0), m_clipAfterMouse(0)
{

}

TrackPanelClipRollFunction::~TrackPanelClipRollFunction()
{
}

//make sure there are two concurrent tracks before applying mouse
bool TrackPanelClipRollFunction::mouseApplies(Gui::KTrackPanel * panel,
    QMouseEvent * event) const
{
    bool result = false;
    //get current timescale and set minimum crop time
    double minDrag = getMinimumDrag();

    if (panel->hasDocumentTrackIndex()) {
	DocTrackBase *track =
	    m_document->track(panel->documentTrackIndex());
	if (track) {
		GenTime mouseTime((int)m_timeline->mapLocalToValue(event->x()),
		m_document->framesPerSecond());
	    DocClipRef *clip = track->getClipAt(mouseTime);
	    if (clip) {
		if (fabs(m_timeline->mapValueToLocal(clip->trackStart().
			    frames(m_document->framesPerSecond())) -
			event->x()) < s_resizeTolerance) {
			GenTime beforeTime((int)minDrag,
			m_document->framesPerSecond());
		    beforeTime = clip->trackStart() - beforeTime;
		    DocClipRef *clipBefore = track->getClipAt(beforeTime);
		    if (clipBefore) {
			result = true;
		    }
		}
		if (fabs(m_timeline->mapValueToLocal((clip->trackEnd()).
			    frames(m_document->framesPerSecond())) -
			event->x()) < s_resizeTolerance) {
			GenTime afterTime((int)minDrag,
			m_document->framesPerSecond());
		    afterTime = clip->trackEnd() + afterTime;
		    DocClipRef *clipAfter = track->getClipAt(afterTime);
		    if (clipAfter) {
			result = true;
		    }
		}
	    }
	}
    }

    return result;
}

QCursor TrackPanelClipRollFunction::getMouseCursor(Gui::KTrackPanel *
    panel, QMouseEvent * event)
{
    return QCursor(Qt::SplitHCursor);
}

bool TrackPanelClipRollFunction::mousePressed(Gui::KTrackPanel * panel,
    QMouseEvent * event)
{
    bool result = false;

    if (panel->hasDocumentTrackIndex()) {
	DocTrackBase *track =
	    m_document->track(panel->documentTrackIndex());

	if (track) {
		GenTime mouseTime((int)m_timeline->mapLocalToValue(event->x()),
		m_document->framesPerSecond());
	    m_clipUnderMouse = track->getClipAt(mouseTime);
	    if (m_clipUnderMouse) {
		if (fabs(m_timeline->mapValueToLocal(m_clipUnderMouse->
			    trackStart().frames(m_document->
				framesPerSecond())) - event->x()) <
		    s_resizeTolerance) {
		    m_resizeState = Start;
		}
		else if (fabs(m_timeline->mapValueToLocal((m_clipUnderMouse->
				trackEnd()).frames(m_document->
				framesPerSecond())) - event->x()) <
		    s_resizeTolerance) {
		    m_resizeState = End;
		}

		KMacroCommand *macroCommand = new KMacroCommand(i18n("Select Clip"));
		macroCommand->addCommand(Command::KSelectClipCommand::selectNone(m_document));
		//select both clips simultaneously

		GenTime beforeTime(s_resizeTolerance, m_document->framesPerSecond());
		if (m_resizeState == Start) {
	  	    macroCommand->addCommand(Command::KSelectClipCommand::selectClipAt(m_document, *track, m_clipUnderMouse->trackStart() + beforeTime));
	  	    macroCommand->addCommand(Command::KSelectClipCommand::selectClipAt(m_document, *track, m_clipUnderMouse->trackStart() - beforeTime));

		    m_clipBeforeMouse = track->getClipAt(GenTime(m_clipUnderMouse->trackStart() - beforeTime));
		}
		if (m_resizeState == End) {
	  	    macroCommand->addCommand(Command::KSelectClipCommand::selectClipAt(m_document, *track, m_clipUnderMouse->trackEnd() + beforeTime));
	  	    macroCommand->addCommand(Command::KSelectClipCommand::selectClipAt(m_document, *track, m_clipUnderMouse->trackEnd() - beforeTime));
		    m_clipAfterMouse = track->getClipAt(GenTime(m_clipUnderMouse->trackEnd() + beforeTime));

		}
		m_app->addCommand(macroCommand, true);
		m_snapToGrid.clearSnapList();
		if (m_timeline->snapToSeekTime())
		    m_snapToGrid.addToSnapList(m_timeline->seekPosition());
		m_snapToGrid.setSnapToFrame(m_timeline->snapToFrame());

		m_snapToGrid.addToSnapList(m_document->
		    getSnapTimes(m_timeline->snapToBorders(),
			m_timeline->snapToMarkers(), true, false));

		m_snapToGrid.setSnapTolerance(GenTime((int)(m_timeline->
			mapLocalToValue(Gui::KTimeLine::snapTolerance) -
				m_timeline->mapLocalToValue(0)),
			m_document->framesPerSecond()));

		QValueVector < GenTime > cursor;

		if (m_resizeState == Start) {
		    cursor.append(m_clipUnderMouse->trackStart());
		} else if (m_resizeState == End) {
		    cursor.append(m_clipUnderMouse->trackEnd());
		}
		m_snapToGrid.setCursorTimes(cursor);
		if (m_resizeState == Start) {
		    m_rollCommand =
			new Command::KRollCommand(m_document,
			*m_clipUnderMouse, *m_clipBeforeMouse);
		} else if (m_resizeState == End) {
		    m_rollCommand =
			new Command::KRollCommand(m_document,
			*m_clipUnderMouse, *m_clipAfterMouse);
		}

		result = true;
	    }
	}
    }

    return result;
}

bool TrackPanelClipRollFunction::mouseDoubleClicked(Gui::KTrackPanel * panel, QMouseEvent * event)
{
    return false;
}

bool TrackPanelClipRollFunction::mouseReleased(Gui::KTrackPanel * panel,
    QMouseEvent * event)
{
    if (!m_rollCommand) return false;
    bool result = false;

    if (m_resizeState == Start) m_rollCommand->setEndSize(*m_clipUnderMouse, *m_clipBeforeMouse);
    else m_rollCommand->setEndSize(*m_clipUnderMouse, *m_clipAfterMouse);

    m_app->addCommand(m_rollCommand, false);
    m_app->addCommand(Command::KSelectClipCommand::selectNone(m_document), true);
    m_document->indirectlyModified();
    m_rollCommand = 0;
    m_clipUnderMouse = 0;
    m_clipBeforeMouse = 0;
    result = true;
    return result;
}

bool TrackPanelClipRollFunction::mouseMoved(Gui::KTrackPanel * panel,
    QMouseEvent * event)
{
    bool result = false;

    if (panel->hasDocumentTrackIndex()) {
	DocTrackBase *track =
	    m_document->track(panel->documentTrackIndex());
	if (track) {
	    GenTime mouseTime =
		m_snapToGrid.getSnappedTime(m_timeline->
		timeUnderMouse(event->x()));

	    if (m_clipUnderMouse) {
		result = true;
		bool sizeLimit = true;
		if (m_resizeState == Start) {
		    if (m_clipBeforeMouse->clipType() == DocClipBase::IMAGE || m_clipBeforeMouse->clipType() == DocClipBase::COLOR || m_clipBeforeMouse->clipType() == DocClipBase::TEXT) sizeLimit = false;
		    if (mouseTime < m_clipUnderMouse->trackStart()) {
			//dragging left, mouse on beginning of second track
			if (m_clipBeforeMouse->cropDuration() >
			    GenTime(getMinimumDrag())
			    && m_clipUnderMouse->cropStartTime() >
			    GenTime(getMinimumDrag())) {
			    track->resizeClipTrackEnd(m_clipBeforeMouse,
				mouseTime);
			    emit signalClipCropEndChanged
				(m_clipBeforeMouse);

			    track->resizeClipTrackStart(m_clipUnderMouse,
				m_clipBeforeMouse->trackEnd());
			    emit signalClipCropStartChanged
				(m_clipUnderMouse);
			}
		    } else {
			//dragging right, mouse on beginning of second track
			if ((m_clipBeforeMouse->cropDuration() <=
			    m_clipBeforeMouse->duration() -
			    GenTime(getMinimumDrag()) || !sizeLimit)
			    && m_clipUnderMouse->cropDuration() >
			    GenTime(getMinimumDrag())) {
			    track->resizeClipTrackStart(m_clipUnderMouse,
				mouseTime);
			    emit signalClipCropStartChanged
				(m_clipUnderMouse);

			    track->resizeClipTrackEnd(m_clipBeforeMouse,
				m_clipUnderMouse->trackStart());
			    emit signalClipCropEndChanged
				(m_clipBeforeMouse);
			}
		    }
		} else if (m_resizeState == End) {
		    if (m_clipUnderMouse->clipType() == DocClipBase::IMAGE || m_clipUnderMouse->clipType() == DocClipBase::COLOR || m_clipUnderMouse->clipType() == DocClipBase::TEXT) sizeLimit = false;
		    GenTime cropDuration =
			mouseTime - m_clipUnderMouse->trackStart();
		    if (mouseTime > m_clipUnderMouse->trackEnd()) {
			//dragging right, mouse on end of first track
			if (m_clipAfterMouse->cropDuration() >
			    GenTime(getMinimumDrag())
			    && (m_clipUnderMouse->cropDuration() <=
			    m_clipUnderMouse->duration() -
			    GenTime(getMinimumDrag()) || !sizeLimit)) {
			    track->resizeClipTrackStart(m_clipAfterMouse,
				mouseTime);
			    emit signalClipCropStartChanged
				(m_clipAfterMouse);

			    track->resizeClipTrackEnd(m_clipUnderMouse,
				m_clipAfterMouse->trackStart());
			    emit signalClipCropEndChanged
				(m_clipUnderMouse);
			}
		    } else {
			//dragging left, mouse on end of first track
			if (m_clipAfterMouse->cropStartTime() >
			    GenTime(getMinimumDrag())
			    && m_clipUnderMouse->cropDuration() >
			    GenTime(getMinimumDrag())) {
			    track->resizeClipTrackEnd(m_clipUnderMouse,
				mouseTime);
			    emit signalClipCropEndChanged
				(m_clipUnderMouse);

			    track->resizeClipTrackStart(m_clipAfterMouse,
				m_clipUnderMouse->trackEnd());
			    emit signalClipCropStartChanged
				(m_clipAfterMouse);
			}
		    }
		} else {
		    kdError() <<
			"Unknown resize state reached in trackpanelrollfunction::mouseMoved()"
			<< endl;
		    kdError() << "(this message should never be seen!)" <<
			endl;
		}
	    }
	}
    }

    return result;
}

double TrackPanelClipRollFunction::getMinimumDrag() const
{
    int currentScale(m_app->getTimeScaleSliderText());
    double minimumDrag = 1.0;
    if (currentScale == 0) {
	minimumDrag = 0.05;
    } else if (currentScale == 1) {
	minimumDrag = 0.05;
    } else if (currentScale == 2) {
	minimumDrag = 0.05;
    } else if (currentScale == 3) {
	minimumDrag = 0.1;
    } else if (currentScale == 4) {
	minimumDrag = 0.5;
    } else if (currentScale == 5) {
	minimumDrag = 0.5;
    } else if (currentScale == 6) {
	minimumDrag = 1.0;
    } else if (currentScale == 7) {
	minimumDrag = 2.0;
    } else if (currentScale == 8) {
	minimumDrag = 5.0;
    } else if (currentScale == 9) {
	minimumDrag = 5.0;
    } else if (currentScale == 10) {
	minimumDrag = 10.0;
    } else if (currentScale == 11) {
	minimumDrag = 10.0;
    } else if (currentScale == 12) {
	minimumDrag = 20.0;
    } else if (currentScale == 13) {
	minimumDrag = 40.0;
    } else {
	minimumDrag = 1.0;
    }
    return minimumDrag;
}
