/*
         * TODO: change current tour event and reattach animation
         *
         * Does nothing if not in authoring mode
         * If it is, then it checks to find the tour events that it's attached to, and, if it's DURING a tour event
         */
        void mediaTouchUp(object sender, EventArgs e)
        {
            if (!tourAuthoringOn)
                return;
            undoableActionPerformed();
            double scrubtime = authorTimerCountSpan.TotalSeconds;

            DockableItem dockItem = sender as DockableItem;
            Timeline tl;
            if (itemToTLDict.TryGetValue(dockItem, out tl))
            {
                BiDictionary<double, TourEvent> itemDict;
                IList<BiDictionary<double, TourEvent>> list = tourBiDictionary[tl];
                if (list.Count != 0)
                {
                    itemDict = list[0];
                    double startEventTime = System.Double.MaxValue;
                    bool needToAddEvent = true;
                    foreach (TourEvent tevent in itemDict.firstValues)
                    {
                        startEventTime = itemDict[tevent][0];
                        if (tevent.type == TourEvent.Type.zoomMedia)
                        {
                            ZoomMediaEvent ze = (ZoomMediaEvent)tevent;
                        }

                        if ((authorTimerCountSpan.TotalSeconds >= startEventTime && authorTimerCountSpan.TotalSeconds <= (startEventTime + tevent.duration)))
                        {
                            needToAddEvent = false;
                            scrubtime = startEventTime + tevent.duration;
                            switch (tevent.type)
                            {
                                case TourEvent.Type.fadeInMedia:
                                    FadeInMediaEvent fadeInMediaEvent = (FadeInMediaEvent)tevent;
                                    fadeInMediaEvent.fadeInMediaToScreenPointX = dockItem.ActualCenter.X;
                                    fadeInMediaEvent.fadeInMediaToScreenPointY = dockItem.ActualCenter.Y;
                                    fadeInMediaEvent.absoluteScale = dockItem.image.ActualWidth / dockItem.image.Source.Width;
                                    break;
                                case TourEvent.Type.fadeOutMedia:
                                    break;
                                case TourEvent.Type.zoomMedia:
                                    ZoomMediaEvent zoomMediaEvent = (ZoomMediaEvent)tevent;
                                    zoomMediaEvent.zoomMediaToScreenPointX = dockItem.ActualCenter.X;
                                    zoomMediaEvent.zoomMediaToScreenPointY = dockItem.ActualCenter.Y;
                                    zoomMediaEvent.absoluteScale = dockItem.image.ActualWidth / dockItem.image.Source.Width;
                                    break;
                                default:
                                    break;
                            }
                        }
                    }
                    if (needToAddEvent)
                    {
                        double time = authorTimerCountSpan.TotalSeconds - 1;
                        if (time < 0)
                        {
                            time = 0;
                        }
                        ZoomMediaEvent toAdd = new ZoomMediaEvent(dockItem, dockItem.image.ActualWidth / dockItem.image.Source.Width, dockItem.ActualCenter.X, dockItem.ActualCenter.Y, 1);
                        itemDict.Add(time, toAdd);
                    }
                }
            }
            tourAuthoringUI.refreshUI();
            this.StopAndReloadTourAuthoringUIFromDict(scrubtime - .01);
        }
        public void LoadDictFromXML(String xmlFileName)
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(xmlFileName);

            if (doc.HasChildNodes)
            {
                foreach (XmlNode tourNode in doc.ChildNodes)
                {
                    if (tourNode.Name == "TourStoryboard")
                    {
                        tourStoryboard = new TourStoryboard();
                        tourStoryboard.displayName = tourNode.Attributes.GetNamedItem("displayName").InnerText;
                        tourStoryboard.description = tourNode.Attributes.GetNamedItem("description").InnerText;
                        //// Time experiment
                        tourStoryboard.totalDuration = double.Parse(tourNode.Attributes.GetNamedItem("duration").InnerText);

                        dockableItemsLoaded = new List<DockableItem>();
                        msiItemsLoaded = new List<MultiScaleImage>();
                        tourBiDictionary = new BiDictionary<Timeline, BiDictionary<double, TourEvent>>();
                        undoStack = new Stack<BiDictionary<Timeline, BiDictionary<double, TourEvent>>>();
                        redoStack = new Stack<BiDictionary<Timeline, BiDictionary<double, TourEvent>>>();
                        itemToTLDict = new Dictionary<DockableItem, Timeline>();
                        msiToTLDict = new Dictionary<MultiScaleImage, Timeline>();

                        foreach (XmlNode TLNode in tourNode.ChildNodes)
                        {
                            if (TLNode.Name == "TourParallelTL")
                            {
                                if (TLNode.Attributes.GetNamedItem("type").InnerText == "artwork")
                                {
                                    String msi_file = TLNode.Attributes.GetNamedItem("file").InnerText;
                                    String msi_tour_path = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\Data\\Images\\DeepZoom\\" + msi_file + "\\dz.xml";
                                    artModeWin.msi_tour.SetImageSource(msi_tour_path);
                                    artModeWin.msi_tour_thumb.SetImageSource(msi_tour_path);

                                    msiItemsLoaded.Add(artModeWin.msi_tour);
                                    artModeWin.msi_tour.PreviewTouchDown += new EventHandler<TouchEventArgs>(msiTouchDown);
                                    artModeWin.msi_tour.PreviewMouseDown += new MouseButtonEventHandler(msiTouchDown);
                                    artModeWin.msi_tour.PreviewTouchMove += new EventHandler<TouchEventArgs>(msiTouchMoved);
                                    artModeWin.msi_tour.PreviewMouseMove += new MouseEventHandler(msiTouchMoved);
                                    artModeWin.msi_tour.PreviewTouchUp += new EventHandler<TouchEventArgs>(msiTouchUp);
                                    artModeWin.msi_tour.PreviewMouseUp += new MouseButtonEventHandler(msiTouchUp);
                                    artModeWin.msi_tour.PreviewMouseWheel += new MouseWheelEventHandler(msiMouseWheel);
                                    artModeWin.msi_tour.disableInertia();
                                    TourParallelTL msi_tour_TL = new TourParallelTL();

                                    msiToTLDict.Add(artModeWin.msi_tour, msi_tour_TL);

                                    msi_tour_TL.type = TourTLType.artwork;
                                    msi_tour_TL.displayName = TLNode.Attributes.GetNamedItem("displayName").InnerText;
                                    msi_tour_TL.file = TLNode.Attributes.GetNamedItem("file").InnerText;

                                    BiDictionary<double, TourEvent> msi_tour_TL_dict = new BiDictionary<double, TourEvent>();
                                    tourBiDictionary.Add(msi_tour_TL, msi_tour_TL_dict);

                                    foreach (XmlNode TourEventNode in TLNode.ChildNodes)
                                    {
                                        if (TourEventNode.Name == "TourEvent")
                                        {
                                            if (TourEventNode.Attributes.GetNamedItem("type").InnerText == "ZoomMSIEvent")
                                            {
                                                double scale = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("scale").InnerText);
                                                double toMSIPointX = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("toMSIPointX").InnerText);
                                                double toMSIPointY = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("toMSIPointY").InnerText);
                                                double duration = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("duration").InnerText);
                                                TourEvent zoomMSIEvent = new ZoomMSIEvent(artModeWin.msi_tour, scale, toMSIPointX, toMSIPointY, duration);

                                                double beginTime = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("beginTime").InnerText);
                                                msi_tour_TL_dict.Add(beginTime, zoomMSIEvent);
                                            }
                                        }
                                    }
                                }

                                else if (TLNode.Attributes.GetNamedItem("type").InnerText == "media")
                                {
                                    String media_file = TLNode.Attributes.GetNamedItem("file").InnerText;
                                    DockableItem dockItem = new DockableItem(artModeWin.MSIScatterView, artModeWin, artModeWin.Bar, System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\Data\\Images\\Metadata\\" + media_file);

                                    dockItem.PreviewMouseMove += new MouseEventHandler(mediaTouchDown);
                                    dockItem.PreviewTouchDown += new EventHandler<TouchEventArgs>(mediaTouchDown);
                                    dockItem.PreviewMouseDown += new MouseButtonEventHandler(mediaTouchDown);
                                    dockItem.PreviewTouchMove += new EventHandler<TouchEventArgs>(mediaTouchMoved);
                                    dockItem.PreviewMouseMove += new MouseEventHandler(mediaTouchMoved);
                                    dockItem.PreviewTouchUp += new EventHandler<TouchEventArgs>(mediaTouchUp);
                                    dockItem.PreviewMouseUp += new MouseButtonEventHandler(mediaTouchUp);

                                    dockableItemsLoaded.Add(dockItem);
                                    dockItem.Visibility = Visibility.Hidden;

                                    TourParallelTL dockItem_TL = new TourParallelTL();
                                    dockItem_TL.type = TourTLType.media;

                                    dockItem_TL.displayName = TLNode.Attributes.GetNamedItem("displayName").InnerText;
                                    dockItem_TL.file = TLNode.Attributes.GetNamedItem("file").InnerText;

                                    itemToTLDict.Add(dockItem, dockItem_TL);

                                    BiDictionary<double, TourEvent> dockItem_TL_dict = new BiDictionary<double, TourEvent>();
                                    tourBiDictionary.Add(dockItem_TL, dockItem_TL_dict);

                                    foreach (XmlNode TourEventNode in TLNode.ChildNodes)
                                    {
                                        if (TourEventNode.Name == "TourEvent")
                                        {
                                            if (TourEventNode.Attributes.GetNamedItem("type").InnerText == "FadeInMediaEvent")
                                            {
                                                double toScreenPointX = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("toScreenPointX").InnerText);
                                                double toScreenPointY = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("toScreenPointY").InnerText);
                                                double scale = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("scale").InnerText);
                                                double duration = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("duration").InnerText);

                                                TourEvent fadeInMediaEvent = new FadeInMediaEvent(dockItem, toScreenPointX, toScreenPointY, scale, duration);

                                                double beginTime = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("beginTime").InnerText);
                                                dockItem_TL_dict.Add(beginTime, fadeInMediaEvent);
                                            }

                                            else if (TourEventNode.Attributes.GetNamedItem("type").InnerText == "ZoomMediaEvent")
                                            {
                                                double scale = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("scale").InnerText);
                                                double toScreenPointX = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("toScreenPointX").InnerText);
                                                double toScreenPointY = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("toScreenPointY").InnerText);
                                                double duration = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("duration").InnerText);

                                                TourEvent zoomMediaEvent = new ZoomMediaEvent(dockItem, scale, toScreenPointX, toScreenPointY, duration);

                                                double beginTime = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("beginTime").InnerText);
                                                dockItem_TL_dict.Add(beginTime, zoomMediaEvent);
                                            }

                                            else if (TourEventNode.Attributes.GetNamedItem("type").InnerText == "FadeOutMediaEvent")
                                            {
                                                double duration = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("duration").InnerText);

                                                TourEvent fadeOutMediaEvent = new FadeOutMediaEvent(dockItem, duration);

                                                double beginTime = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("beginTime").InnerText);
                                                dockItem_TL_dict.Add(beginTime, fadeOutMediaEvent);
                                            }
                                        }
                                    }
                                }

                                else if (TLNode.Attributes.GetNamedItem("type").InnerText == "highlight")
                                {
                                    String media_file = TLNode.Attributes.GetNamedItem("file").InnerText;
                                    SurfaceInkCanvas sic = new SurfaceInkCanvas();
                                    inkCanvases.Add(sic);
                                    sic.Width = 1920;
                                    sic.Height = 1080;
                                    sic.DefaultDrawingAttributes.Width = 50;
                                    sic.DefaultDrawingAttributes.Height = 50;
                                    sic.UsesTouchShape = false;
                                    sic.Background = Brushes.Transparent;
                                    sic.Opacity = 0.7;
                                    sic.Visibility = Visibility.Visible;
                                    Canvas.SetZIndex(sic, 50);
                                    loadInkCanvas(sic, System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "/" + media_file);
                                    artModeWin.ImageArea.Children.Add(sic);

                                    TourParallelTL tourtl = new TourParallelTL();
                                    tourtl.type = TourTLType.highlight;
                                    tourtl.inkCanvas = sic;
                                    tourtl.displayName = TLNode.Attributes.GetNamedItem("displayName").InnerText;
                                    tourtl.file = TLNode.Attributes.GetNamedItem("file").InnerText;
                                    BiDictionary<double, TourEvent> tldict = new BiDictionary<double, TourEvent>();
                                    tourBiDictionary.Add(tourtl, tldict);

                                    foreach (XmlNode TourEventNode in TLNode.ChildNodes)
                                    {
                                        if (TourEventNode.Name == "TourEvent")
                                        {
                                            if (TourEventNode.Attributes.GetNamedItem("type").InnerText == "FadeInHighlightEvent")
                                            {
                                                double opacity = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("opacity").InnerText);
                                                double duration = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("duration").InnerText);

                                                TourEvent fadeInHighlightEvent = new FadeInHighlightEvent(sic, duration, opacity);

                                                double beginTime = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("beginTime").InnerText);
                                                tldict.Add(beginTime, fadeInHighlightEvent);
                                            }

                                            else if (TourEventNode.Attributes.GetNamedItem("type").InnerText == "FadeOutHighlightEvent")
                                            {
                                                double opacity = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("opacity").InnerText);
                                                double duration = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("duration").InnerText);

                                                TourEvent fadeOutHighlightEvent = new FadeOutHighlightEvent(sic, duration, opacity);

                                                double beginTime = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("beginTime").InnerText);
                                                tldict.Add(beginTime, fadeOutHighlightEvent);
                                            }
                                        }
                                    }
                                }

                                else if (TLNode.Attributes.GetNamedItem("type").InnerText == "path")
                                {
                                    String media_file = TLNode.Attributes.GetNamedItem("file").InnerText;
                                    SurfaceInkCanvas sic = new SurfaceInkCanvas();
                                    inkCanvases.Add(sic);
                                    sic.Width = 1920;
                                    sic.Height = 1080;
                                    sic.UsesTouchShape = true;
                                    sic.DefaultDrawingAttributes.FitToCurve = true;
                                    sic.Background = Brushes.Transparent;
                                    sic.Opacity = 0;
                                    sic.Visibility = Visibility.Visible;
                                    Canvas.SetZIndex(sic, 50);
                                    loadInkCanvas(sic, System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "/" + media_file);
                                    artModeWin.ImageArea.Children.Add(sic);

                                    TourParallelTL tourtl = new TourParallelTL();
                                    tourtl.type = TourTLType.path;
                                    tourtl.inkCanvas = sic;
                                    tourtl.displayName = TLNode.Attributes.GetNamedItem("displayName").InnerText;
                                    tourtl.file = TLNode.Attributes.GetNamedItem("file").InnerText;
                                    BiDictionary<double, TourEvent> tldict = new BiDictionary<double, TourEvent>();
                                    tourBiDictionary.Add(tourtl, tldict);

                                    foreach (XmlNode TourEventNode in TLNode.ChildNodes)
                                    {
                                        if (TourEventNode.Name == "TourEvent")
                                        {
                                            if (TourEventNode.Attributes.GetNamedItem("type").InnerText == "FadeInPathEvent")
                                            {
                                                double duration = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("duration").InnerText);

                                                TourEvent fadeInPathEvent = new FadeInPathEvent(sic, duration);

                                                double beginTime = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("beginTime").InnerText);
                                                tldict.Add(beginTime, fadeInPathEvent);
                                            }

                                            else if (TourEventNode.Attributes.GetNamedItem("type").InnerText == "FadeOutPathEvent")
                                            {
                                                double duration = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("duration").InnerText);

                                                TourEvent fadeOutPathEvent = new FadeOutPathEvent(sic, duration);

                                                double beginTime = Convert.ToDouble(TourEventNode.Attributes.GetNamedItem("beginTime").InnerText);
                                                tldict.Add(beginTime, fadeOutPathEvent);
                                            }
                                        }
                                    }
                                }
                            }
                            else if (TLNode.Name == "TourMediaTL")
                            {
                                String audio_file = TLNode.Attributes.GetNamedItem("file").InnerText;
                                TourMediaTL tourAudio_TL = new TourMediaTL(new Uri(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\Data\\Tour\\Audio\\" + audio_file, UriKind.Absolute));
                                tourAudio_TL.type = TourTLType.audio;
                                tourAudio_TL.displayName = TLNode.Attributes.GetNamedItem("displayName").InnerText;
                                tourAudio_TL.file = TLNode.Attributes.GetNamedItem("file").InnerText;
                                if (TLNode.Attributes.GetNamedItem("beginTime") != null)
                                    tourAudio_TL.BeginTime = TimeSpan.FromSeconds(Convert.ToDouble(TLNode.Attributes.GetNamedItem("beginTime").InnerText));

                                if (TLNode.Attributes.GetNamedItem("duration") != null)
                                    tourAudio_TL.Duration = TimeSpan.FromSeconds(Convert.ToDouble(TLNode.Attributes.GetNamedItem("duration").InnerText));
                                else
                                {
                                    TagLib.File audio_file_tags = TagLib.File.Create(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\Data\\Tour\\Audio\\" + audio_file);
                                    tourAudio_TL.Duration = audio_file_tags.Properties.Duration;
                                }

                                BiDictionary<double, TourEvent> tourAudio_TL_dict = new BiDictionary<double, TourEvent>(); // dummy TL_dict -- tourAudio_timeline obviously doesn't store any TourEvents
                                tourBiDictionary.Add(tourAudio_TL, tourAudio_TL_dict);

                                tourAudio_element = new MediaElement();
                                tourAudio_element.Volume = 0.99;

                                tourAudio_element.LoadedBehavior = MediaState.Manual;
                                tourAudio_element.UnloadedBehavior = MediaState.Manual;

                                Storyboard.SetTarget(tourAudio_TL, tourAudio_element);
                                tourStoryboard.SlipBehavior = SlipBehavior.Slip;

                                // took me quite a while to figure out that WPF really can't determine the duration of an MP3 until it's actually loaded (i.e. playing), and then it took me a little longer to finally find and accept this open-source library...argh

                            }
                        }
                    }
                }
            }
        }
 /// <summary>
 /// Add new zoom tour event
 /// </summary>
 /// <param name="duration"></param>
 /// <param name="media"></param>
 /// <param name="x"></param>
 /// <param name="y"></param>
 /// <param name="scale"></param>
 private void addZoomTourEvent(DockableItem media, double scale, double x, double y, double start, double duration)
 {
     ZoomMediaEvent toAdd = new ZoomMediaEvent(media, scale, x, y, duration);
     Timeline tl;
     if (itemToTLDict.TryGetValue(media, out tl))
     {
         BiDictionary<double, TourEvent> itemDict;
         IList<BiDictionary<double, TourEvent>> itemDictList;
         if (tourBiDictionary.TryGetValue(tl, out itemDictList))
         {
             itemDict = itemDictList[0];
             itemDict.Add(start, toAdd);
         }
     }
 }