// $Id: DeckNavigationToolBarButtons.cs 1658 2008-07-28 17:49:52Z lining $


using System;
using System.Diagnostics;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;

using UW.ClassroomPresenter.Model;
using UW.ClassroomPresenter.Model.Presentation;
using UW.ClassroomPresenter.Model.Network;
using UW.ClassroomPresenter.Model.Workspace;

namespace UW.ClassroomPresenter.Viewer.ToolBars {
    /// <summary>
    /// Represents the deck navigation buttons
    /// </summary>
    public class DeckNavigationToolBarButtons {
        /// <summary>
        /// The model that this UI component modifies
        /// </summary>
        protected readonly PresenterModel m_Model;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="model">The model that this class modifies</param>
        public DeckNavigationToolBarButtons(PresenterModel model) {
            this.m_Model = model;
        }

        /// <summary>
        /// Constructs all of the child buttons belonging to this class
        /// </summary>
        /// <param name="parent">The parent ToolStrip to place the buttons in</param>
        /// <param name="dispatcher">The event queue to use for events</param>
        public void MakeButtons(ToolStrip parent, ControlEventQueue dispatcher, bool fullscreen) {
            NavigationToolBarButton animback, animfwd, back, forward, linked, whiteboard, dup;
            ToolStripButton full, clock;

            animback = new BackwardAnimationToolBarButton(dispatcher, this.m_Model);
            animfwd = new ForwardAnimationToolBarButton(dispatcher, this.m_Model);
            back = new BackwardNavigationToolBarButton( dispatcher, this.m_Model );
            forward = new ForwardNavigationToolBarButton( dispatcher, this.m_Model );
            whiteboard = new WhiteboardToolBarButton( dispatcher, this.m_Model );
            dup = new DupToolBarButton(dispatcher, this.m_Model);
            linked = new LinkedNavigationToolBarButton( dispatcher, this.m_Model );
            full = (fullscreen ? (ToolStripButton)new ExitFullScreenButton(this.m_Model ) : new EnterFullScreenButton(this.m_Model));
            clock = new ClockButton(this.m_Model);

            // Add the buttons to the parent ToolStrip
            ToolStripSeparator sep0, sep1, sep2, sep3, sep4, sep5, sep6;

            parent.Items.Add(sep0 = new ToolStripSeparator());

            parent.Items.Add(dup);
            parent.Items.Add(sep1 = new ToolStripSeparator());

            parent.Items.Add(linked);
            parent.Items.Add(sep2 = new ToolStripSeparator());

            parent.Items.Add( whiteboard );
            parent.Items.Add(sep3 = new ToolStripSeparator());

            parent.Items.Add(back);
            parent.Items.Add(forward);
            parent.Items.Add(sep4 = new ToolStripSeparator());

            parent.Items.Add(animback);
            parent.Items.Add(animfwd);
            parent.Items.Add(sep5 = new ToolStripSeparator());

            parent.Items.Add(full);
            parent.Items.Add(clock);
            parent.Items.Add(sep6 = new ToolStripSeparator());
            
            EventQueue.PropertyEventDispatcher listener = new EventQueue.PropertyEventDispatcher(dispatcher, new PropertyEventHandler(
                (object o, PropertyEventArgs args) => {
                using (Synchronizer.Lock(this.m_Model.ViewerState.SyncRoot))
                {
                    int mask = (fullscreen ? this.m_Model.ViewerState.FullScreenButtons : this.m_Model.ViewerState.MainWindowButtons);
                    dup.Visible = (mask & (int)Model.Viewer.ViewerStateModel.ToolbarButtons.DuplicateSlide) != 0;
                    sep1.Visible = dup.Visible;
                    linked.Visible = (mask & (int)Model.Viewer.ViewerStateModel.ToolbarButtons.Linked) != 0;
                    sep2.Visible = linked.Visible;
                    whiteboard.Visible = (mask & (int)Model.Viewer.ViewerStateModel.ToolbarButtons.Whiteboard) != 0;
                    sep3.Visible = whiteboard.Visible;
                    back.Visible = forward.Visible = (mask & (int)Model.Viewer.ViewerStateModel.ToolbarButtons.UpDown) != 0;
                    sep4.Visible = back.Visible;
                    animback.Visible = animfwd.Visible = (mask & (int)Model.Viewer.ViewerStateModel.ToolbarButtons.LeftRight) != 0;
                    sep5.Visible = animback.Visible;
                    full.Visible = (mask & (int)Model.Viewer.ViewerStateModel.ToolbarButtons.FullScreen) != 0;
                    clock.Visible = (mask & (int)Model.Viewer.ViewerStateModel.ToolbarButtons.Clock) != 0;
                    sep6.Visible = full.Visible || clock.Visible;
                    sep0.Visible = sep1.Visible || sep2.Visible || sep3.Visible || sep4.Visible | sep5.Visible | sep6.Visible;
                }
            }));
            this.m_Model.ViewerState.Changed[fullscreen ? "FullScreenButtons" : "MainWindowButtons"].Add(listener.Dispatcher);
            listener.Dispatcher(this, null);
        }

        /// <summary>
        /// Constructs all of the child buttons belonging to this class
        /// </summary>
        /// <param name="main">The main ToolStrip to place the buttons in</param>
        /// <param name="extra">The extra ToolStrip to place the buttons in</param>
        /// <param name="dispatcher">The event queue to use for events</param>
        public void MakeButtons(ToolStrip main, ToolStrip extra, ControlEventQueue dispatcher) {
            bool fullscreen = false;
            //NavigationToolBarButton back, forward, linked, whiteboard;
            NavigationToolBarButton linked, whiteboard;

            // Create the back button
            //back = new BackwardNavigationToolBarButton( dispatcher, this.m_Model );
            //back.Image = UW.ClassroomPresenter.Properties.Resources.left;
            //back.AutoSize = true;

            // Create the forward button
            //forward = new ForwardNavigationToolBarButton( dispatcher, this.m_Model );
            //forward.Image = UW.ClassroomPresenter.Properties.Resources.right;
            //forward.AutoSize = true;

            // Create the whiteboard button
            whiteboard = new WhiteboardToolBarButton( dispatcher, this.m_Model );
            whiteboard.Image = UW.ClassroomPresenter.Properties.Resources.whiteboard;
            whiteboard.AutoSize = true;

            // Create the linked button
            linked = new LinkedNavigationToolBarButton( dispatcher, this.m_Model );
            linked.Image = UW.ClassroomPresenter.Properties.Resources.linked;
            linked.AutoSize = true;

            // Add the buttons to the parent ToolStrip
            extra.Items.Add( whiteboard );
            ToolStripSeparator sep;
            extra.Items.Add(sep = new ToolStripSeparator());
            //main.Items.Add(back);
            //extra.Items.Add(forward);
            //main.Items.Add(new ToolStripSeparator());
            //extra.Items.Add(new ToolStripSeparator());

            //If we are not in student mode, then linked wasn't drawn, and we don't want to add this ToolStripSeparator.
            main.Items.Add(linked);

            EventQueue.PropertyEventDispatcher listener = new EventQueue.PropertyEventDispatcher(dispatcher, new PropertyEventHandler(
(object o, PropertyEventArgs args) =>
{
    using (Synchronizer.Lock(this.m_Model.ViewerState.SyncRoot))
    {
        int mask = (fullscreen ? this.m_Model.ViewerState.FullScreenButtons : this.m_Model.ViewerState.MainWindowButtons);
        linked.Visible = (mask & (int)Model.Viewer.ViewerStateModel.ToolbarButtons.Linked) != 0;
        whiteboard.Visible = (mask & (int)Model.Viewer.ViewerStateModel.ToolbarButtons.Whiteboard) != 0; 
        sep.Visible = linked.Visible && whiteboard.Visible;
    }
}));
            this.m_Model.ViewerState.Changed[fullscreen ? "FullScreenButtons" : "MainWindowButtons"].Add(listener.Dispatcher);
            listener.Dispatcher(this, null);
        }

        #region NavigationToolBarButton

        protected abstract class NavigationToolBarButton : ToolStripButton {
            private readonly ControlEventQueue m_EventQueue;
            private readonly IDisposable m_CurrentDeckTraversalDispatcher;
            private readonly EventQueue.PropertyEventDispatcher m_TraversalChangedDispatcher;
            protected readonly PresenterModel m_Model;
            private DeckTraversalModel m_CurrentDeckTraversal;
            private bool m_Disposed;

            protected NavigationToolBarButton(ControlEventQueue dispatcher, PresenterModel model) {
                this.m_EventQueue = dispatcher;
                this.m_Model = model;
                this.AutoSize = true;
                
                // Listen to changes in the deck traversal
                this.m_TraversalChangedDispatcher = new EventQueue.PropertyEventDispatcher(this.m_EventQueue, new PropertyEventHandler(this.HandleTraversalChanged));
                // Don't call the event handler immediately (use Listen instead of ListenAndInitialize)
                // because it needs to be delayed until Initialize() below.
                this.m_CurrentDeckTraversalDispatcher =
                    this.m_Model.Workspace.CurrentDeckTraversal.Listen(dispatcher,
                    delegate(Property<DeckTraversalModel>.EventArgs args) {
                        this.CurrentDeckTraversal = args.New;
                    });
            }

            protected virtual void Initialize() {
                using (Synchronizer.Lock(this.m_Model.Workspace.CurrentDeckTraversal.SyncRoot)) {
                    this.CurrentDeckTraversal = this.m_Model.Workspace.CurrentDeckTraversal.Value;
                }
            }

            protected override void Dispose(bool disposing)
            {
                if (this.m_Disposed) return;
                try
                {
                    if (disposing)
                    {
                        this.m_CurrentDeckTraversalDispatcher.Dispose();
                        // Unregister event listeners via the CurrentDeckTraversal setter
                        this.CurrentDeckTraversal = null;
                    }
                }
                finally
                {
                    base.Dispose(disposing);
                }
                this.m_Disposed = true;
            }

            protected abstract string TraversalProperty { get; }

            protected virtual DeckTraversalModel CurrentDeckTraversal {
                get { return this.m_CurrentDeckTraversal; }
                set {
                    if(this.m_CurrentDeckTraversal != null) {
                        this.m_CurrentDeckTraversal.Changed[this.TraversalProperty].Remove(this.m_TraversalChangedDispatcher.Dispatcher);
                        if (this.TraversalProperty.Equals("AnimationLevel"))
                            this.m_CurrentDeckTraversal.Changed["Current"].Remove(this.m_TraversalChangedDispatcher.Dispatcher);
                    }

                    this.m_CurrentDeckTraversal = value;

                    if(this.m_CurrentDeckTraversal != null) {
                        this.m_CurrentDeckTraversal.Changed[this.TraversalProperty].Add(this.m_TraversalChangedDispatcher.Dispatcher);
                        if (this.TraversalProperty.Equals("AnimationLevel"))
                            this.m_CurrentDeckTraversal.Changed["Current"].Add(this.m_TraversalChangedDispatcher.Dispatcher);
                    }

                    this.m_TraversalChangedDispatcher.Dispatcher(this, null);
                }
            }

            protected abstract void HandleTraversalChanged(object sender, PropertyEventArgs args);
        }

        #endregion

        #region ForwardNavigationToolBarButton

        /// <summary>
        /// Button handling forward navigation in decks
        /// </summary>
        protected class ForwardNavigationToolBarButton : NavigationToolBarButton {

            public ForwardNavigationToolBarButton(ControlEventQueue dispatcher, PresenterModel model)
                : base(dispatcher, model)
            {
                this.ToolTipText = Strings.ForwardTooltipNextSlide;
                this.Image = UW.ClassroomPresenter.Properties.Resources.down;
                this.Initialize();
            }

            protected override string TraversalProperty { get { return "Next"; } }

            protected override void HandleTraversalChanged(object sender, PropertyEventArgs args) {
                if(this.CurrentDeckTraversal == null) {
                    this.Enabled = false;
                } else {
                    using (Synchronizer.Lock(this.CurrentDeckTraversal.SyncRoot)) {
                        using (Synchronizer.Lock(this.CurrentDeckTraversal.Deck.SyncRoot)) {
                            // Update the button state.  Enable the button if there is a "next" slide,
                            // OR if this is a non-remote whiteboard deck (to which we can add additional slides).
                            if (((this.CurrentDeckTraversal.Deck.Disposition & DeckDisposition.Whiteboard) != 0) && ((this.CurrentDeckTraversal.Deck.Disposition & DeckDisposition.Remote) == 0))
                                this.Enabled = true;
                            else 
                                this.Enabled = (this.CurrentDeckTraversal.Next != null);
                            if (this.Enabled == true && this.CurrentDeckTraversal.Next == null)
                                this.ToolTipText = Strings.ForwardTooltipAddBlank;
                            else
                                this.ToolTipText = Strings.ForwardTooltipNextSlide;
                        }
                    }
                }
            }
            
            protected override void OnClick( EventArgs args ) {
                using( Synchronizer.Lock( this ) ) {
                    if( this.CurrentDeckTraversal == null ) return;
                    using( Synchronizer.Lock( this.CurrentDeckTraversal.SyncRoot ) ) {
                        if( this.CurrentDeckTraversal.Next != null )
                            this.CurrentDeckTraversal.Current = this.CurrentDeckTraversal.Next;
                        else {
                            // Add a whiteboard slide if we are at the end of the deck
                            using (Synchronizer.Lock(this.CurrentDeckTraversal.Deck.SyncRoot)) {
                                if (((this.CurrentDeckTraversal.Deck.Disposition & DeckDisposition.Whiteboard) != 0) && ((this.CurrentDeckTraversal.Deck.Disposition & DeckDisposition.Remote) == 0)) {
                                    // Add the new slide
                                    SlideModel slide = new SlideModel(Guid.NewGuid(), new LocalId(), SlideDisposition.Empty, UW.ClassroomPresenter.Viewer.ViewerForm.DEFAULT_SLIDE_BOUNDS);
                                    this.CurrentDeckTraversal.Deck.InsertSlide(slide);
                                    using (Synchronizer.Lock(this.CurrentDeckTraversal.Deck.TableOfContents.SyncRoot)) {
                                        // Add the new table of contents entry
                                        TableOfContentsModel.Entry entry = new TableOfContentsModel.Entry(Guid.NewGuid(), this.CurrentDeckTraversal.Deck.TableOfContents, slide);
                                        this.CurrentDeckTraversal.Deck.TableOfContents.Entries.Add(entry);

                                        // Move to the slide
                                        this.CurrentDeckTraversal.Current = entry;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        #endregion

        #region BackwardNavigationToolBarButton

        /// <summary>
        /// Button handling backward navigation in decks
        /// </summary>
        protected class BackwardNavigationToolBarButton : NavigationToolBarButton {

            public BackwardNavigationToolBarButton( ControlEventQueue dispatcher, PresenterModel model )
                : base(dispatcher, model)
            {
                this.ToolTipText = Strings.BackwardTooltipPreviousSlide;
                this.Image = UW.ClassroomPresenter.Properties.Resources.up;
                this.Initialize();
            }

            protected override string TraversalProperty {
                get { return "Previous"; }
            }

            protected override void HandleTraversalChanged( object sender, PropertyEventArgs args ) {
                if(this.CurrentDeckTraversal == null) {
                    this.Enabled = false;
                }
                else {
                    using(Synchronizer.Lock(this.CurrentDeckTraversal.SyncRoot)) {
                        this.Enabled = (this.CurrentDeckTraversal.Previous != null);
                    }
                }
            }

            protected override void OnClick( EventArgs args ) {
                if( this.CurrentDeckTraversal == null ) return;
                using( Synchronizer.Lock( this.CurrentDeckTraversal.SyncRoot ) ) {
                    this.CurrentDeckTraversal.Current = this.CurrentDeckTraversal.Previous;
                }
                base.OnClick( args );
            }
        }

        #endregion

        #region BackwardAnimationToolBarButton

        /// <summary>
        /// Button handling backward animation in decks
        /// </summary>
        protected class BackwardAnimationToolBarButton : NavigationToolBarButton
        {
            public BackwardAnimationToolBarButton(ControlEventQueue dispatcher, PresenterModel model)
                : base(dispatcher, model)
            {
                this.ToolTipText = Strings.TooltipAnimationNext;
                this.Image = UW.ClassroomPresenter.Properties.Resources.back;
                this.Initialize();
            }

            protected override string TraversalProperty
            {
                get { return "AnimationLevel"; }
            }

            protected override void HandleTraversalChanged(object sender, PropertyEventArgs args)
            {
                if (this.CurrentDeckTraversal == null)
                {
                    this.Enabled = false;
                }
                else
                {
                    using (Synchronizer.Lock(this.CurrentDeckTraversal.SyncRoot))
                    {
                        this.Enabled = (this.CurrentDeckTraversal.Current != null) &&
                            (this.CurrentDeckTraversal.AnimationLevel > 0);
                    }
                }
            }

            protected override void OnClick(EventArgs args)
            {
                if (this.CurrentDeckTraversal == null) return;
                using (Synchronizer.Lock(this.CurrentDeckTraversal.SyncRoot))
                {
                    if (this.CurrentDeckTraversal.AnimationLevel > 0)
                        this.CurrentDeckTraversal.AnimationLevel--;
                }
                base.OnClick(args);
            }
        }

        #endregion

        #region ForwardAnimationToolBarButton

        protected class ForwardAnimationToolBarButton : NavigationToolBarButton
        {
            public ForwardAnimationToolBarButton(ControlEventQueue dispatcher, PresenterModel model)
                : base(dispatcher, model)
            {
                this.ToolTipText = Strings.TooltipAnimationNext;
                this.Image = UW.ClassroomPresenter.Properties.Resources.forward;
                this.Initialize();
            }

            /// <summary>
            /// This class listens to changes in the "AnimationLevel" property of the deck traversal
            /// </summary>
            protected override string TraversalProperty
            {
                get { return "AnimationLevel"; }
            }

            protected override void HandleTraversalChanged(object sender, PropertyEventArgs args)
            {
                if (this.CurrentDeckTraversal == null)
                {
                    this.Enabled = false;
                }
                else
                {
                    using (Synchronizer.Lock(this.CurrentDeckTraversal.SyncRoot))
                    {
                        this.Enabled = (this.CurrentDeckTraversal.Current != null) &&
                            this.CurrentDeckTraversal.Current.Slide.AnimationLayerCount > 0 &&
                            (this.CurrentDeckTraversal.AnimationLevel < 
                            this.CurrentDeckTraversal.Current.Slide.AnimationLayerCount);
                    }
                }
            }

            protected override void OnClick(EventArgs args)
            {
                if (this.CurrentDeckTraversal == null) return;
                using (Synchronizer.Lock(this.CurrentDeckTraversal.SyncRoot))
                {
                    if (this.CurrentDeckTraversal.AnimationLevel < 0)
                        this.CurrentDeckTraversal.AnimationLevel = 0;
                    else if (this.CurrentDeckTraversal.AnimationLevel < this.CurrentDeckTraversal.Current.Slide.AnimationLayerCount)
                        this.CurrentDeckTraversal.AnimationLevel++;    
                }
                base.OnClick(args);
            }
        }

        #endregion

        #region LinkedNavigationToolBarButton

        protected class LinkedNavigationToolBarButton : NavigationToolBarButton {
            private readonly EventQueue.PropertyEventDispatcher m_RoleChangedDispatcher;

            public LinkedNavigationToolBarButton(ControlEventQueue dispatcher, PresenterModel model)
                : base(dispatcher, model)
            {
                this.Image = UW.ClassroomPresenter.Properties.Resources.linked;

                // Listen for changes to the Role property
                this.m_RoleChangedDispatcher = new EventQueue.PropertyEventDispatcher(dispatcher, new PropertyEventHandler(this.HandleRoleChanged));
                this.m_Model.Participant.Changed["Role"].Add(this.m_RoleChangedDispatcher.Dispatcher);
                this.m_RoleChangedDispatcher.Dispatcher(this, null);

                this.Initialize();
            }

            /// <summary>
            /// Destroy resources, i.e. stop listening to model changes
            /// </summary>
            /// <param name="disposing">True if we are destroying all objects</param>
            protected override void Dispose(bool disposing) {
                if(disposing) {
                    this.m_Model.Participant.Changed["Role"].Remove(this.m_RoleChangedDispatcher.Dispatcher);
                }

                base.Dispose(disposing);
            }

            /// <summary>
            /// This class listens to changes in the "Mode" property of the deck traversal
            /// </summary>
            protected override string TraversalProperty {
                get { return "Mode"; }
            }

            /// <summary>
            /// Only update the current deck traversal if it is a linked deck traversal
            /// </summary>
            protected override DeckTraversalModel CurrentDeckTraversal {
                get { return base.CurrentDeckTraversal; }
                set { base.CurrentDeckTraversal = (value is LinkedDeckTraversalModel) ? value : null; }
            }

            /// <summary>
            /// Handle the deck traversal changing and the "Mode" property.
            /// </summary>
            /// <param name="sender">The event sender</param>
            /// <param name="args">The arguments</param>
            protected override void HandleTraversalChanged(object sender, PropertyEventArgs args) {
                if(this.CurrentDeckTraversal is LinkedDeckTraversalModel) {
                    using(Synchronizer.Lock(this.CurrentDeckTraversal.SyncRoot)) {
                        switch(((LinkedDeckTraversalModel) this.CurrentDeckTraversal).Mode) {
                            case LinkedDeckTraversalModel.DeckTraversalSelector.Linked: {
                                this.Checked = true;
                                this.ToolTipText = "Enable free navigation independent of the instructor";
                                this.Image = UW.ClassroomPresenter.Properties.Resources.linked;
                                break;
                            }
                            case LinkedDeckTraversalModel.DeckTraversalSelector.Unlinked:
                            default: {
                                this.Checked = false;
                                this.Image = UW.ClassroomPresenter.Properties.Resources.unlinked;
                                break;
                            }
                        }
                    }

                    this.Enabled = true;
                }
                else {
                    this.Checked = false;
                    this.Enabled = false;
                }

                this.ToolTipText = (this.Checked)
                    ? Strings.EnableNavigationIndInstructor
                    : Strings.DisableNavigationIndInstructor;
            }

            /// <summary>
            /// Handle the button being clicked
            /// </summary>
            /// <param name="args">The event arguments</param>
            protected override void OnClick(EventArgs args) {
                if (this.CurrentDeckTraversal is LinkedDeckTraversalModel) {                       
                    if (this.Checked) {
                        using (Synchronizer.Lock(this.CurrentDeckTraversal.SyncRoot)) {
                            ((LinkedDeckTraversalModel)this.CurrentDeckTraversal).Mode = LinkedDeckTraversalModel.DeckTraversalSelector.Unlinked;
                        }
                        using (Synchronizer.Lock(this.m_Model.Workspace.DeckTraversals.SyncRoot)) {
                            foreach (DeckTraversalModel deck in this.m_Model.Workspace.DeckTraversals) {
                                using (Synchronizer.Lock(deck.SyncRoot)) {
                                    if (deck is LinkedDeckTraversalModel) {
                                        ((LinkedDeckTraversalModel)deck).Mode = LinkedDeckTraversalModel.DeckTraversalSelector.Unlinked;
                                    }
                                }
                            }
                        }
                        using (Synchronizer.Lock(this.m_Model.Workspace.CurrentDeckTraversal.SyncRoot)) {
                            ((LinkedDeckTraversalModel)this.m_Model.Workspace.CurrentDeckTraversal.Value).Mode = LinkedDeckTraversalModel.DeckTraversalSelector.Unlinked;
                        }
                    } else {
                        using (Synchronizer.Lock(this.m_Model.Workspace.DeckTraversals.SyncRoot)) {
                            foreach (DeckTraversalModel deck in this.m_Model.Workspace.DeckTraversals) {
                                using (Synchronizer.Lock(deck.SyncRoot)) {
                                    if (deck is LinkedDeckTraversalModel) {
                                        ((LinkedDeckTraversalModel)deck).Mode = LinkedDeckTraversalModel.DeckTraversalSelector.Linked;
                                        using (Synchronizer.Lock(this.m_Model.Workspace.CurrentPresentation.Value.Owner.SyncRoot)) {
                                            using (Synchronizer.Lock(this.m_Model.Workspace.CurrentPresentation.Value.Owner.Role)) {
                                                using (Synchronizer.Lock(this.m_Model.Workspace.CurrentPresentation.Value.Owner.Role.SyncRoot)) {
                                                    DeckTraversalModel current = ((InstructorModel)(this.m_Model.Workspace.CurrentPresentation.Value.Owner.Role)).CurrentDeckTraversal;
                                                    if (current != null && deck.Id == current.Id) {
                                                        this.m_Model.Workspace.CurrentDeckTraversal.Value = deck;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                using( Synchronizer.Lock( this.CurrentDeckTraversal.SyncRoot ) ) {
                                    this.CurrentDeckTraversal = this.m_Model.Workspace.CurrentDeckTraversal.Value;
                                    ((LinkedDeckTraversalModel)this.m_Model.Workspace.CurrentDeckTraversal.Value).Mode = LinkedDeckTraversalModel.DeckTraversalSelector.Linked;
                                    ((LinkedDeckTraversalModel)this.CurrentDeckTraversal).Mode = LinkedDeckTraversalModel.DeckTraversalSelector.Linked;
                                }
                            }
                        }
  
                    }

                    this.HandleTraversalChanged(this, null);
                }
                base.OnClick(args);
            }

            /// <summary>
            /// Handle the role changing (only display when we are not an instructor)
            /// </summary>
            /// <param name="sender">The event sender</param>
            /// <param name="args">The arguments</param>
            private void HandleRoleChanged(object sender, PropertyEventArgs args) {
                using(Synchronizer.Lock(this.m_Model.Participant.SyncRoot)) {
                    this.Visible = !(this.m_Model.Participant.Role is InstructorModel);
                }
            }
        }

        #endregion

        #region WhiteboardToolBarButton

        /// <summary>
        /// Button handling the display of the whiteboard deck
        /// </summary>
        protected class WhiteboardToolBarButton : NavigationToolBarButton
        {
            private readonly EventQueue.PropertyEventDispatcher m_RoleChangedDispatcher;
            private DeckTraversalModel m_PreviousWhiteboard;
            private DeckTraversalModel m_PreviousNormal;

            public WhiteboardToolBarButton(ControlEventQueue dispatcher, PresenterModel model)
                : base(dispatcher, model)
            {
                this.Image = UW.ClassroomPresenter.Properties.Resources.whiteboard;

                 // Listen for changes to the Role property
                this.m_RoleChangedDispatcher = new EventQueue.PropertyEventDispatcher(dispatcher, new PropertyEventHandler(this.HandleRoleChanged));
                this.m_Model.Participant.Changed["Role"].Add(this.m_RoleChangedDispatcher.Dispatcher);
                this.m_RoleChangedDispatcher.Dispatcher(this, null);
                this.Initialize();
            }

            protected override void Dispose(bool disposing)
            {
                if (disposing)
                {
                    this.m_Model.Participant.Changed["Role"].Remove(this.m_RoleChangedDispatcher.Dispatcher);
                }

                base.Dispose(disposing);
            }

            protected override string TraversalProperty
            {
                get { return "Current"; }
            }

            protected override void HandleTraversalChanged(object sender, PropertyEventArgs args)
            {
                if (this.CurrentDeckTraversal == null)
                {
                    this.Checked = false;
                }
                else
                {
                    if ((this.CurrentDeckTraversal.Deck.Disposition & DeckDisposition.Whiteboard) != 0)
                    {
                        this.m_PreviousWhiteboard = this.CurrentDeckTraversal;
                        this.Checked = true;
                    }
                    else
                    {
                        this.m_PreviousNormal = this.CurrentDeckTraversal;
                        this.Checked = false;
                    }
                }

                this.ToolTipText = (this.Checked)
                    ? Strings.SwitchToNonWhiteboardDeck
                    : Strings.SwitchWhiteboardDeck;
            }


            protected override void OnClick(EventArgs args)
            {
                using (this.m_Model.Workspace.Lock())
                {
                    using( Synchronizer.Lock( this.m_Model.Participant.SyncRoot ) ) 
                    {
                        DeckTraversalModel traversal;

                        if (this.CurrentDeckTraversal == null || (this.CurrentDeckTraversal.Deck.Disposition & DeckDisposition.Whiteboard) == 0)
                        {
                            traversal = this.m_PreviousWhiteboard;

                            // Make sure the deck hasn't been closed.
                            if (!this.m_Model.Workspace.DeckTraversals.Contains(traversal))
                                traversal = this.m_PreviousWhiteboard = null;

                            // If there is no previous whiteboard traversal, find one or create one.
                            if (traversal == null)
                            {
                                // TODO: If the participant is an instructor, *only* search the whiteboards in the active presentation.
                                //   Then, when changing the current deck, also change the current presentation if necessary.

                                foreach (DeckTraversalModel candidate in this.m_Model.Workspace.DeckTraversals)
                                {
                                    if ((candidate.Deck.Disposition & DeckDisposition.Whiteboard) != 0)
                                    {
                                        traversal = candidate;
                                        break;
                                    }
                                }

                                // If there is no existing whiteboard deck, create one.
                                if (traversal == null)
                                {
                                    // Change the name of whiteboard according to the number of it
                                    string wbname = "WhiteBoard " + (++UW.ClassroomPresenter.Viewer.ViewerForm.white_board_num).ToString();
                                    // NOTE: This code is duplicated in DecksMenu.CreateBlankWhiteboardDeckMenuItem.
                                    DeckModel deck = new DeckModel(Guid.NewGuid(), DeckDisposition.Whiteboard, wbname);
                                    System.Drawing.Color? bg = null;
                                    if (this.CurrentDeckTraversal != null)
                                            bg = this.CurrentDeckTraversal.Deck.DeckBackgroundColor;
                                    using (Synchronizer.Lock(deck.SyncRoot))
                                    {
                                        if (bg.HasValue)
                                            deck.DeckBackgroundColor = bg.Value;

                                        SlideModel slide = new SlideModel(Guid.NewGuid(), new LocalId(), SlideDisposition.Empty, UW.ClassroomPresenter.Viewer.ViewerForm.DEFAULT_SLIDE_BOUNDS);

                                        deck.InsertSlide(slide);

                                        using (Synchronizer.Lock(deck.TableOfContents.SyncRoot))
                                        {
                                            TableOfContentsModel.Entry entry = new TableOfContentsModel.Entry(Guid.NewGuid(), deck.TableOfContents, slide);
                                            deck.TableOfContents.Entries.Add(entry);
                                        }
                                    }

                                    traversal = new SlideDeckTraversalModel(Guid.NewGuid(), deck);

                                    if (this.m_Model.Workspace.CurrentPresentation.Value != null)
                                    {
                                        using (Synchronizer.Lock((~this.m_Model.Workspace.CurrentPresentation).SyncRoot))
                                        {
                                            (~this.m_Model.Workspace.CurrentPresentation).DeckTraversals.Add(traversal);
                                        }
                                    }
                                    else
                                    {
                                        this.m_Model.Workspace.DeckTraversals.Add(traversal);
                                    }
                                }
                            }
                        }
                        else
                        {
                            traversal = this.m_PreviousNormal;

                            // Make sure the deck hasn't been closed.
                            if (!this.m_Model.Workspace.DeckTraversals.Contains(traversal))
                                traversal = this.m_PreviousNormal = null;

                            // Search the workspace for a non-whiteboard deck if we haven't encountered one before.
                            if (traversal == null)
                            {
                                // TODO: If the participant is an instructor, *only* search the whiteboards in the active presentation.
                                foreach (DeckTraversalModel candidate in this.m_Model.Workspace.DeckTraversals)
                                {
                                    if ((candidate.Deck.Disposition & DeckDisposition.Whiteboard) == 0)
                                    {
                                        traversal = candidate;
                                        break;
                                    }
                                }

                                // If we still haven't found a non-whiteboard deck, do nothing.
                                if (traversal == null)
                                    return;
                            }
                        }

                        // If the user's role is an InstructorModel, set the InstructorModel's CurrentDeckTraversal.
                        // The NetworkAssociationService will then use this to set the WorkspaceModel's CurrentDeckTraversal,
                        // and the new deck traversal will also be broadcast.
                        InstructorModel instructor = this.m_Model.Participant.Role as InstructorModel;
                        if (instructor != null)
                        {
                            using (Synchronizer.Lock(instructor.SyncRoot))
                            {
                                instructor.CurrentDeckTraversal = traversal;
                            }
                        }
                        else
                        {
                            // Otherwise, we must set the CurrentDeckTraversal on the WorkspaceModel directly.
                            using (this.m_Model.Workspace.Lock())
                            {
                                this.m_Model.Workspace.CurrentDeckTraversal.Value = traversal;
                            }
                        }
                    }
                }
                base.OnClick(args);
            }



            /// <summary>
            /// Handle the role changing (only display when we are an instructor)
            /// </summary>
            /// <param name="sender">The event sender</param>
            /// <param name="args">The arguments</param>
            private void HandleRoleChanged(object sender, PropertyEventArgs args)
            {
                using (Synchronizer.Lock(this.m_Model.Participant.SyncRoot))
                {
                    this.Visible = (this.m_Model.Participant.Role is InstructorModel);
                }
            }

        }

     
        #endregion

        protected class ExitFullScreenButton : ToolStripButton
        {
            PresenterModel m_Model;
            public ExitFullScreenButton(PresenterModel model)
            {
                this.m_Model = model;
                this.Image = UW.ClassroomPresenter.Properties.Resources.exitfullscreen;
            }
            protected override void OnClick(EventArgs e)
            {
                using (Synchronizer.Lock(this.m_Model.ViewerState.SyncRoot))
                {
                    this.m_Model.ViewerState.PrimaryMonitorFullScreen = false;
                }
                base.OnClick(e);
            }
        }

        protected class EnterFullScreenButton : ToolStripButton
        {
            PresenterModel m_Model;
            public EnterFullScreenButton(PresenterModel model)
            {
                this.m_Model = model;
                this.Image = UW.ClassroomPresenter.Properties.Resources.fullscreen;
            }
            protected override void OnClick(EventArgs e)
            {
                using (Synchronizer.Lock(this.m_Model.ViewerState.SyncRoot))
                {
                    this.m_Model.ViewerState.PrimaryMonitorFullScreen = true;
                }
                base.OnClick(e);
            }
        }

        protected class ClockButton : ToolStripButton
        {
            PresenterModel m_Model;
            bool m_Elapsed, m_Disposed;
            System.Windows.Forms.Timer m_Timer;

            public ClockButton(PresenterModel model)
            {
                this.m_Model = model;
                this.Font = new Font(this.Font.FontFamily, 14f, FontStyle.Bold);
                this.Text = "00:00:00";
                this.m_Timer = new System.Windows.Forms.Timer();
                this.m_Timer.Tick += new EventHandler(this.Tick);
                this.m_Timer.Interval = 1000; // 1 sec
            }

            protected override void OnPaint(PaintEventArgs e)
            {
                this.m_Timer.Start();
                base.OnPaint(e);
            }

            private void Tick(Object myObject, EventArgs myEventArgs)
            {
                this.m_Timer.Stop();
                if (this.m_Elapsed)
                {
                    TimeSpan elapsed = DateTime.Now.Subtract(this.m_Model.Started);
                    this.Text = String.Format("{0}h{1:00}m{2:00}s", Math.Floor(elapsed.TotalHours), elapsed.Minutes, elapsed.Seconds);
                }
                else
                {
                    this.Text = DateTime.Now.ToString("h:mm:ss", System.Globalization.CultureInfo.InvariantCulture);
                }
                this.Invalidate();
            }

            protected override void OnClick(EventArgs e)
            {
                this.m_Elapsed = !this.m_Elapsed;
                Tick(null, null);
                base.OnClick(e);
            }

            protected override void Dispose(bool disposing)
            {
                if (this.m_Disposed) return;
                try
                {
                    if (disposing)
                    {
                        this.m_Timer.Stop();
                        this.m_Timer.Dispose();
                    }
                }
                finally
                {
                    base.Dispose(disposing);
                }
                this.m_Disposed = true;
            }
        }
 
        
        #region DupToolBarButton

        /// <summary>
        /// Button handling slide duplication
        /// </summary>
        protected class DupToolBarButton : NavigationToolBarButton
        {

            public DupToolBarButton(ControlEventQueue dispatcher, PresenterModel model)
                : base(dispatcher, model)
            {
                this.ToolTipText = Strings.DuplicateSlide;
                this.Image = UW.ClassroomPresenter.Properties.Resources.dup;
                this.Initialize();
            }

            protected override string TraversalProperty { get { return "Current"; } }

            protected override void HandleTraversalChanged(object sender, PropertyEventArgs args)
            {
                if (this.CurrentDeckTraversal == null)
                {
                    this.Enabled = false;
                }
                else
                {
                    using (Synchronizer.Lock(this.CurrentDeckTraversal.SyncRoot))
                    {
                        this.Enabled = (this.CurrentDeckTraversal.Current != null);
                    }
                }
            }

            protected override void OnClick(EventArgs args)
            {
                if (this.CurrentDeckTraversal == null) return;
                DuplicateSlide();
                base.OnClick(args);
            }

            private void DuplicateSlide() {

                DeckTraversalModel traversal_ = this.CurrentDeckTraversal;

                int current_slide_index;
                using (Synchronizer.Lock(traversal_.SyncRoot)) {
                    if (traversal_.Current == null)
                        return;
                    current_slide_index = traversal_.AbsoluteCurrentSlideIndex - 1;
                }

                if (current_slide_index < 0)
                    return;

                DeckModel m_Deck = traversal_.Deck;
                
                // Copy slide
                SlideModel newSlide;
                using (Synchronizer.Lock(m_Deck.SyncRoot)) {
                    SlideModel currentSlide = m_Deck.TableOfContents.Entries[current_slide_index].Slide;
                    newSlide = CloneSlideAndSheets(currentSlide);
                    //remove remote disposition for student submission slide; The removal may be not necessary for non-student submission slide.
                    //If current role is non-instructor,the sheet models in slide should contain remote disposition 
                    if ((currentSlide.Disposition & SlideDisposition.StudentSubmission)!=0) {
                        newSlide.RemoveRemoteDisposition();
                    }
                    m_Deck.Dirty = true;
                    m_Deck.InsertSlide(newSlide);
                }

                ///Insert our current slide *before* the current index. This differs from the powerpoint
                ///UI, but is better for live presentations, where one wants to re-ink the current slide.
                using (Synchronizer.Lock(m_Deck.TableOfContents.SyncRoot)) {
                    TableOfContentsModel.Entry entry = new TableOfContentsModel.Entry(Guid.NewGuid(), m_Deck.TableOfContents, newSlide);
                    m_Deck.TableOfContents.Entries.Insert(current_slide_index /*+ 1*/, entry);
                }
            }

            protected static SlideModel CloneSlideAndSheets(SlideModel slide)
            {

                // Create the new slide to add
                SlideModel newSlide = new SlideModel(Guid.NewGuid(), new LocalId(), SlideDisposition.Empty, UW.ClassroomPresenter.Viewer.ViewerForm.DEFAULT_SLIDE_BOUNDS);
                newSlide.Origin = slide.Origin;

                // Update the fields of the slide
                using (Synchronizer.Lock(newSlide.SyncRoot))
                {
                    using (Synchronizer.Lock(slide.SyncRoot))
                    {
                        newSlide.Title = slide.Title;
                        newSlide.Bounds = slide.Bounds;
                        newSlide.Zoom = slide.Zoom;
                        newSlide.BackgroundColor = slide.BackgroundColor;
                        newSlide.BackgroundTemplate = slide.BackgroundTemplate;
                        newSlide.SubmissionSlideGuid = slide.SubmissionSlideGuid;
                        newSlide.SubmissionStyle = slide.SubmissionStyle;
                        newSlide.AnimationLayerCount = slide.AnimationLayerCount;

                        // Copy all of the content sheets.
                        // Because ContentSheets do not change, there is no 
                        // need to do a deep copy (special case for ImageSheetModels).
                        foreach (SheetModel s in slide.ContentSheets)
                        {
                            newSlide.ContentSheets.Add(s);
                        }

                        // Make a deep copy of all the ink sheets
                        foreach (SheetModel s in slide.AnnotationSheets)
                        {
                            SheetModel newSheet = UW.ClassroomPresenter.Model.Presentation.SheetModel.SheetDeepCopyHelper(s);
                            newSlide.AnnotationSheets.Add(newSheet);

                        }
                    }
                }

                return newSlide;

            }


        }

        #endregion

    }
}
