/// <summary>
 /// Initializes a new instance of the <see cref="Paint.PictureStateManager"/> class.		
 /// <param name='filenameResolver' Determines the filename for all files saved/loaded />
 /// <param name='pictureIOManager' Handles the saving of the imageStateData />
 /// <param name='renderTargetHandler' Handles the rendering of the appropriate render target when undo/redoing a change />
 /// <param name='imageStateData' Current save point, max undo/redo count etc />
 /// </summary>
 public PictureStateManager(IFilenameResolver filenameResolver, IPictureIOManager pictureIOManager, IRenderTargertHandler renderTargetHandler, ImageStateData imageStateData)
 {
     this.canvasRecorder = new CanvasRecorder();
     this.renderTargetHandler = renderTargetHandler;
     this.filenameResolver = filenameResolver;
     this.pictureIOManager = pictureIOManager;
     this.ImageStateData = imageStateData;
 }
        /// <summary>
        /// Initializes a new instance of the <see cref="Paint.CanvasPlaybackApp"/> class.
        /// </summary>
        /// <param name='canvasPlayback'>Canvas Playback data</param>
        /// <param name='imageStateData'>ImageSaveData</param>
        /// <param name='toolboxLayoutDefinition'>Layout of the toolbox</param>
        /// <param name='deviceScale'>The device scale/resolution. 1 = normal.  2 = retina.</param>
        public CanvasPlaybackApp(
			ICanvasPlayback canvasPlayback, 
			ImageStateData imageStateData, 
			ToolboxLayoutDefinition toolboxLayoutDefinition,
			int deviceScale)
            : base(imageStateData, toolboxLayoutDefinition, deviceScale)
        {
            this.canvasPlayback = canvasPlayback;
            this.calculatePlaybackSpeed = new CalculatePlaybackSpeed();
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="Paint.PaintApp"/> class.
        /// Instantiate our GraphicsDeviceManager and establish where the content folder is
        /// </summary>
        /// <param name='pictureIOManager'>Picture IO Manager</param>
        /// <param name='filenameResolver'>Filename Resolver</param>
        /// <param name='imageStateData'>ImageSaveData</param>
        /// <param name='saveBusyMessageDisplay'>Class for dsplaying the 'Busy - saving' message</param>
        /// <param name='toolboxLayoutDefinition'>Layout of the toolbox</param>
        /// <param name='deviceScale'>The device scale/resolution. 1 = normal.  2 = retina.</param>
        public PaintApp(
			IPictureIOManager pictureIOManager, 
			IFilenameResolver filenameResolver, 
			ImageStateData imageStateData,
			IUIBusyMessage saveBusyMessageDisplay,
			ToolboxLayoutDefinition toolboxLayoutDefinition,
			int deviceScale)
            : base(imageStateData, toolboxLayoutDefinition, deviceScale)
        {
            this.filenameResolver = filenameResolver;
            this.pictureIOManager = pictureIOManager;
            this.saveBusyMessageDisplay = saveBusyMessageDisplay;
        }
 /// <summary>
 /// Sets the orientation of the device based on the image dimensions.
 /// </summary>
 /// <param name='imageStateData'>
 /// Image state data.
 /// </param>
 private void SetOrientationForImage(ImageStateData imageStateData)
 {
     if (imageStateData.Height > imageStateData.Width)
     {
         this.SetOrientation(PictureOrientation.Portrait);
     }
     else
     {
         this.SetOrientation(PictureOrientation.Landscape);
     }
 }
        /// <summary>
        /// Start painting a new image
        /// </summary>
        /// <param name='orientation'>
        /// The orientation of the new image
        /// </param>
        private void NewImage(PictureOrientation orientation)
        {
            // If the device is still mid turn then the reported width and height may be wrong - hence we are using
            // Math.Max and Math.Min to ensure we get the right size.  [Actually I've delayed the turning until we are
            // ready to display the app  (inside call to EditImage) so it wouldn't have turned yet anyway]
            ImageStateData imageStateData = null;

            int deviceWidth = (int)UIScreen.MainScreen.Bounds.Width * this.deviceScale;
            int deviceHeight = (int)UIScreen.MainScreen.Bounds.Height * this.deviceScale;

            if (orientation == PictureOrientation.Landscape)
            {
                imageStateData = new ImageStateData(Math.Max(deviceHeight, deviceWidth), Math.Min(deviceHeight, deviceWidth), UndoRedoBufferSize);
            }
            else
            {
                imageStateData = new ImageStateData(Math.Min(deviceHeight, deviceWidth), Math.Max(deviceHeight, deviceWidth), UndoRedoBufferSize);
            }

            this.EditImage(Guid.NewGuid(), imageStateData);
        }
        /// <summary>
        /// Edits a specific image.
        /// </summary>
        /// <param name='pictureId'>
        /// Unique ID referencing the specific image we want to edit
        /// </param>
        /// <param name='imageStateData'>
        /// Image state data for this image - if null then it will be read from disk (so should not be null for new images)
        /// </param>
        private void EditImage(Guid pictureId, ImageStateData imageStateData = null)
        {
            var filenameResolver = this.CreateFilenameResolver(pictureId);
            var pictureIOManager = new PictureIOManager(filenameResolver);
            pictureIOManager.CreateDirectoryStructure();

            if (imageStateData == null)
            {
                imageStateData = pictureIOManager.LoadImageStateData();
            }

            this.SetOrientationForImage(imageStateData);

            BusyMessageDisplay busyMessageDisplay = new BusyMessageDisplay("Saving", "Please wait...");

            // Simply instantiate the class derived from monogame:game and away we go...
            ToolboxLayoutDefinition layoutDefinition =
                imageStateData.Width > imageStateData.Height ?
                    this.toolboxLayoutManager.PaintLandscapeToolboxLayout :
                    this.toolboxLayoutManager.PaintPortraitToolboxLayout;

            this.paintApp = new PaintApp(pictureIOManager, filenameResolver, imageStateData, busyMessageDisplay, layoutDefinition, this.deviceScale);
            this.paintApp.Exiting += PaintAppExiting;

            this.paintApp.Run();
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="Paint.BaseGame"/> class.
        /// </summary>
        /// <param name='imageStateData'>ImageSaveData</param>
        /// <param name='toolboxLayoutDefinition'>Layout of the toolbox</param>
        /// <param name='deviceScale'>Layout of the toolbox</param>
        /// <param name='deviceScale'>The device scale/resolution. 1 = normal.  2 = retina.</param>
        public BaseGame(ImageStateData imageStateData, ToolboxLayoutDefinition toolboxLayoutDefinition, int deviceScale)
        {
            this.ImageStateData = imageStateData;
            this.ToolboxLayoutDefinition = toolboxLayoutDefinition;
            this.DeviceScale = deviceScale;

            this.GraphicsDeviceManager = new GraphicsDeviceManager(this);
            this.GraphicsDeviceManager.IsFullScreen = true;

            if (imageStateData.Width > imageStateData.Height)
            {
                this.GraphicsDeviceManager.SupportedOrientations =
                    DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight;
            }
            else
            {
                this.GraphicsDeviceManager.SupportedOrientations =
                    DisplayOrientation.Portrait | DisplayOrientation.PortraitUpsideDown;
            }

            this.Content.RootDirectory = "Content";
        }
        /// <summary>
        ///  Saves the imageStateData to disk
        /// </summary>
        /// <param name='filename'>File to save the image data</param>
        /// <param name='imageStateData'>Image state data.</param>
        public void SaveImageStateData(string filename, ImageStateData imageStateData)
        {
            // Next write out the information file
            var dataArray = new int[] {
                imageStateData.Width,
                imageStateData.Height,
                imageStateData.MaxUndoRedoCount,
                imageStateData.FirstSavePoint,
                imageStateData.LastSavePoint,
                imageStateData.CurrentSavePoint
            };

            using (var stream = File.Open(filename, FileMode.Create, FileAccess.Write))
            {
                foreach (int val in dataArray)
                {
                    stream.WriteByte((byte)val);
                    stream.WriteByte((byte)(val >> 8));
                    stream.WriteByte((byte)(val >> 16));
                    stream.WriteByte((byte)(val >> 24));
                }
            }
        }
        /// <summary>
        /// Saves all the undoRedoRenderTargets to disk and the imageStateData
        /// </summary>
        /// <param name='imageStateData'>Image state data.</param>
        /// <param name='masterImageRenderTarget' Master image render target/>
        /// <param name='undoRedoRenderTargets'>Sequence of images representing the undo/redo chain</param>
        /// <param name='bottomMarginToCutOff'>Because the toolbox will always take up some space we will cut off the bottom section (toolbox height)
        /// when saving the master image so that there is no annoying white space at the bottom</param>
        public void SaveData(ImageStateData imageStateData, RenderTarget2D masterImageRenderTarget, RenderTarget2D[] undoRedoRenderTargets, int bottomMarginToCutOff)
        {
            this.SaveImageStateData(this.filenameResolver.MasterImageInfoFilename, imageStateData);

            int end = imageStateData.FirstSavePoint == 0 ? imageStateData.LastSavePoint : imageStateData.MaxUndoRedoCount - 1;

            for (int count = 0; count <= end; count++)
            {
                var renderTarget = undoRedoRenderTargets[count];

                // Save the render target to disk
                renderTarget.SaveAsPng(
                    this.filenameResolver.ImageSavePointFilename(count),
                    renderTarget.Width,
                    renderTarget.Height);

                // copy the working canvas recorder file into the master folder.
                var masterCanvasRecorderFile = this.filenameResolver.MasterCanvasRecorderFilename(count);

                if (File.Exists(masterCanvasRecorderFile))
                {
                    File.Delete(masterCanvasRecorderFile);
                }

                File.Move(this.filenameResolver.WorkingCanvasRecorderFilename(count), masterCanvasRecorderFile);
            }

            // Save the Master image as a JPG as it has no alpha channel - which is ideal for displaying on the home
            // home screen where we don't want the background image showing through
            masterImageRenderTarget.SaveAsJpeg(
                this.filenameResolver.MasterImageFilename,
                masterImageRenderTarget.Width,
                masterImageRenderTarget.Height - bottomMarginToCutOff);

            File.Delete(this.filenameResolver.WorkingImageInfoFilename);
        }