using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using JD.Application.Utils;
using JD.Application.View.Misc;
using JD.Application.View.Timeline;
using JD.Audio.Annotations;
using JD.Audio.SoundPlayer;
using JD.Collections.Extensions;
using JD.Maths.Utils;
using JD.MessageService;
using JD.Timeline.Model.Clips;
using JD.Timeline.View.Actions.Refactor;
using JD.Timeline.View.ClipView;
using TeaBox;
using TeaBox.Gui;
using JD.Serialization;
using JD.WinFormExtensions;
using TeaBox.Core.Source.Gui;
using TeaBox.Core.Source.Gui.Misc;
using FrameConverter = JD.Core.FrameConverter;
namespace JD.Timeline.View.Actions
{
///
/// Provides the different menu actions to create a new clip.
///
public class AddNewClipActionHandler : TapeActionHandlerBase
{
protected virtual IEnumerable ClipTypes()
{
return Track != null ? Track.ClipTypes() : Enumerable.Empty();
}
public AddNewClipActionHandler()
{
ClipLimit = 500;
}
protected ClipViewControl CurrentControl
{
get
{
var view = CurrentView;
return view != null ? view.ClipView : null;
}
}
protected TapeTrack Track
{
get; set;
}
public override bool UpdateArray(ActionContext context, ActionArrayInfo info)
{
if (CurrentView == null || SelectedClips.Any())
{
return true;
}
Point currentLocation = context.Location;
if (currentLocation == Point.Empty)
{
currentLocation = Control.MousePosition;
}
var dropLocation = CurrentControl.PointToClient(currentLocation);
Track = CurrentControl.TrackFromPoint(dropLocation.Y);
if (Track != null)
{
foreach (var clipType in ClipTypes().AsSorted((x1, x2) => System.String.Compare(x1.Name, x2.Name, System.StringComparison.Ordinal)))
{
var action = new ActionInfoElement("Add " + clipType.GetStyle().DisplayName)
{
Icon = new ImageOrIcon(clipType.GetStyle().Icon),
State = clipType
};
info.Add(action);
}
}
return true;
}
public override void Execute(ActionExecutionContext context)
{
if (CurrentTape == null)
{
return;
}
var type = (System.Type)context.State;
// From the shortcut.
Clip newClip;
if (type == null)
{
newClip = InstantiateDefaultClip();
}
else
{
var dropLocation = CurrentControl.PointToClient(context.Location);
newClip = Instantiate(type, dropLocation);
}
if (newClip != null)
{
CurrentTape.Clips.Add(newClip);
var newWidget = CurrentControl.ClipWidgets.FirstOrDefault(x => x.Model == newClip);
newWidget.OnInserted();
}
}
private Clip InstantiateDefaultClip()
{
var trackWidget = CurrentView.ClipView.TrackView.ActiveTrack as TapeTrackWidget;
if (trackWidget != null)
{
var track = trackWidget.Track;
var clipType = track.ClipTypes().SingleOrDefault();
var curentBeat = CurrentControl.Timeline.ConvertTo(CurrentControl.Timeline.TimeCursor.Time, TimeUnit.Beat);
if (clipType == null)
{
// When the track is not of a fixed type
// we assume the user might want to create the same type of clip he just created before.
// so we find a type that is near the actual time cursor on the same track.
clipType = (from clip in track.Tape.Clips
where clip.Track == track
orderby Math.Abs(clip.StartBeat - curentBeat)
select clip.GetType()).FirstOrDefault();
}
if (clipType != null && ValidateCanAddClip(clipType))
{
curentBeat = CurrentTape.TryGetWave().UnitConverter.Snap(new BeatUnit(curentBeat), EditionOptions.CurrentSnappingUnit).Value;
var newClip = Clip.Instantiate(clipType);
newClip.InitializeDefaultValues();
newClip.StartFrame = FindBestStartFrame(track, FrameConverter.FrameFromTimeInteger(curentBeat));
newClip.TrackId = track.Id;
return newClip;
}
}
return null;
}
protected virtual Clip Instantiate(System.Type clipType, Point dropLocation)
{
var track = CurrentControl.TrackFromPoint(dropLocation.Y);
if (track != null && ValidateCanAddClip(clipType))
{
var newEvent = Clip.Instantiate(clipType);
newEvent.InitializeDefaultValues();
newEvent.StartFrame = FindBestStartFrame(track, (int) CurrentControl.FrameFromPoint(dropLocation.X));
newEvent.TrackId = track.Id;
if (newEvent.DefaultDuration > 0)
{
newEvent.Duration = newEvent.DefaultDuration;
}
else
{
newEvent.Duration = (int)CurrentControl.Timeline.FrameFromPixelDuration(200);
}
return newEvent;
}
return null;
}
///
/// Sets the starting frame to make sure it does not overlap an existing clip
///
private int FindBestStartFrame(TapeTrack track, int frame)
{
Clip collidingClip;
while (TryGetCollidingClip(track, frame, out collidingClip))
{
frame = collidingClip.StopFrame;
}
return frame;
}
private bool TryGetCollidingClip(TapeTrack track, int frame, out Clip collision)
{
collision = track.Clips().FirstOrDefault(x => frame.IsBetween(x.StartFrame, x.StopFrame - 1));
return collision != null;
}
protected int ClipLimit
{
get; set;
}
private bool ValidateCanAddClip(System.Type type)
{
var clipCount = CurrentControl.Tape.Clips.Count;
if (type != typeof(TapeReferenceClip) && clipCount > ClipLimit)
{
var dialog = new MessageDialog
{
MainIcon = MessageDialogIcon.Warning,
Content =
$"Clip limit reached ({clipCount} clips instead of 300). Are you sure you want to continue?"
};
var yes = dialog.AddButton("Add clip anyway");
yes.Content = "We suggest you start to reduce the clip count before adding new clips.";
var help = dialog.AddButton("Help");
var cancel = dialog.AddButton("Cancel");
cancel.IsDefault = true;
var result = dialog.ShowDialog();
if (result == help)
{
ClipLimitWarning.OpenHelpFile();
}
return result == yes;
}
return true;
}
}
}