using Xamarin.Forms;

/// <summary>
/// The namespace for the Gallery application.
/// </summary>
namespace Gallery
{
    /// <summary>
    /// Creates a grid of images.
    /// GridPage inherits from ContentPage.
    /// </summary>
    public class GridPage : ContentPage
    {
        private int _numberOfImages = 10;
        private int _numberOfColumns = 8;
        private int _numberOfRows;
        // When the application is launched the first image will be selected
        private int _selectedImage = 0;

        /// <summary>
        /// Loads images and arranges them in a Grid.
        /// The user can select an image of their choice and this will open an
        /// image viewer on that image.
        /// </summary>
        public GridPage()
        {
            // Remove the back button
            NavigationPage.SetHasBackButton(this, false);
            Title = "Gallery";

            BackgroundColor = Color.White;

            // Create a grid with no spacing between the items
            Grid grid = new Grid
            {
                ColumnSpacing = 0,
                RowSpacing = 0,
            };

            // Add columns to the grid
            for (int column = 0; column < _numberOfColumns; column++)
            {
                grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
            }

            // Add rows to the grid
            _numberOfRows = (_numberOfImages / _numberOfColumns) + 1;
            for (int row = 0; row < _numberOfRows; row++)
            {
                grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
            }

            // Open the ImageViewer when an image is tapped (for mobile)
            TapGestureRecognizer tapGestureRecognizer = new TapGestureRecognizer();

            // Because the images are named 0-9 + .jpg we can strip the extension to
            // determine where to open the ImageViewer
            tapGestureRecognizer.Tapped += (s, e) =>
            {
                // Get the filename of the image which was tapped
                string filename = ((s as Image).Source as FileImageSource).File;
                // The characters in the file extension
                char[] extension = { '.', 'j', 'p', 'g' };
                // Remove the extension
                string number = filename.TrimEnd(extension);
                // Get the image number as an integer
                int imageNumber = int.Parse(number);
                // Open the image viewer on the image with that number
                Navigation.PushAsync(new ImageViewer(_numberOfImages, imageNumber), false);
            };

            // Conveniently the images are named 0-9 + .jpg so it is easy to get filenames
            for (int i = 0; i < _numberOfImages; i++)
            {
                // Construct the filename of the image to retrieve
                string filename = i.ToString() + ".jpg";
                Image image = new Image
                {
                    // These images are in the res folder
                    Source = filename,
                    Aspect = Aspect.AspectFill,
                    HorizontalOptions = LayoutOptions.CenterAndExpand,
                    VerticalOptions = LayoutOptions.CenterAndExpand
                };
                // Add a tap gesture recognizer to determine when an image is tapped
                image.GestureRecognizers.Add(tapGestureRecognizer);
                // Add the retrieved image to the grid in the correct position
                grid.Children.Add(image, i % _numberOfColumns, i / _numberOfColumns);
            }

            // Grid's images should be sized according to the device dimensions
            grid.PropertyChanged += (s, e) =>
            {
                // Images should be sized when the grid width changes
                if (e.PropertyName == "Width")
                {
                    // The images should use teh available space equally
                    var size = (grid.Width) / _numberOfColumns;
                    foreach (Image image in grid.Children)
                    {
                        image.WidthRequest = size;
                        image.HeightRequest = size;
                    }
                }
            };

            Button previousButton = new Button
            {
                HorizontalOptions = LayoutOptions.FillAndExpand,
                VerticalOptions = LayoutOptions.End,
                Text = "Previous"
            };

            // Callback to be invoked when the previous button is clicked
            previousButton.Clicked += (s, e) =>
            {
                // Restore the opacity of the currently selected image
                (grid.Children[_selectedImage] as Image).Opacity = 1.0;

                // Calculate the new selected image and consider if it is on image 0
                _selectedImage = _selectedImage - 1 < 0 ? _numberOfImages - 1 : _selectedImage - 1;

                // Modify the opacity of the newly selected image
                (grid.Children[_selectedImage] as Image).Opacity = 0.35;
            };

            Button nextButton = new Button
            {
                HorizontalOptions = LayoutOptions.FillAndExpand,
                VerticalOptions = LayoutOptions.End,
                Text = "Next"
            };

            // Callback to be invoked when the next button is clicked
            nextButton.Clicked += (s, e) =>
            {
                // Restore the opacity of the currently selected image
                (grid.Children[_selectedImage] as Image).Opacity = 1.0;

                // Calculate the new selected image and consider if it is on the last image
                _selectedImage = _selectedImage + 1 > _numberOfImages - 1 ? 0 : _selectedImage + 1;

                // Modify the opacity of the newly selected image
                (grid.Children[_selectedImage] as Image).Opacity = 0.35;
            };

            // Add the previous and next buttons into a StackLayout
            StackLayout navButtonStack = new StackLayout
            {
                Orientation = StackOrientation.Horizontal,
                HorizontalOptions = LayoutOptions.FillAndExpand,
                VerticalOptions = LayoutOptions.FillAndExpand,
                Children =
                {
                    previousButton, nextButton
                }
            };

            // Button to select the image to view
            Button okButton = new Button
            {
                HorizontalOptions = LayoutOptions.FillAndExpand,
                VerticalOptions = LayoutOptions.End,
                Text = "Select image"
            };

            // Callback to be invoked when the Select Image button is clicked
            okButton.Clicked += (s, e) =>
            {
                // Open the image viewer on the image with the selected image
                Navigation.PushAsync(new ImageViewer(_numberOfImages, _selectedImage), false);
            };

            // Get the initial image so that it can be marked as selected with opacity
            (grid.Children[_selectedImage] as Image).Opacity = 0.35;

            var content = new StackLayout
            {
                HorizontalOptions = LayoutOptions.FillAndExpand,
                VerticalOptions = LayoutOptions.FillAndExpand,
                Children =
                {
                    grid,
                }
            };

            // On non-mobile devices, the user cannot use touch, so buttons are required
            // We can used device idiom to determine what device the application
            // is running on
            if (Device.Idiom != TargetIdiom.Phone)
            {
                content.Children.Add(navButtonStack);
                content.Children.Add(okButton);
            }

            // Build the page
            this.Content = content;

            // Set the focus on the next button
            // This is likely to be the first operation that the user does
            // when the Gallery grid opens
            // This is especially useful on a TV when the user has to use a remote
            // because this button will already be focused
            Appearing += (s, e) =>
            {
                nextButton.Focus();
            };
        }
    }
}