using Microsoft.Win32;
using RBG_Server;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
// Not using Shapes.Path so specify Path = System.IO
using Path = System.IO.Path;
namespace RBG_Server_WPF
{
///
/// Interaction logic for ConnectionPage.xaml
///
public partial class ConnectionPage : Page
{
public ConnectionPage()
{
InitializeComponent();
}
// ************************************************************
// ****************** Visibility Toggles **********************
// ************************************************************
#region visibility toggles
private void HostButton_Click(object sender, RoutedEventArgs e)
{
// Draw host window
ServerGrid.Visibility = Visibility.Visible;
ServerSelectPanel.Visibility = Visibility.Collapsed;
}
private void ConnectButton_Click(object sender, RoutedEventArgs e)
{
// Draw client window
ClientGrid.Visibility = Visibility.Visible;
ServerSelectPanel.Visibility = Visibility.Collapsed;
}
private void BackButton_Click(object sender, RoutedEventArgs e)
{
// Hide host view, show connect view
ServerGrid.Visibility = Visibility.Collapsed;
ServerSelectPanel.Visibility = Visibility.Visible;
}
private void ClientBackButton_Click(object sender, RoutedEventArgs e)
{
ClientGrid.Visibility = Visibility.Collapsed;
ServerSelectPanel.Visibility = Visibility.Visible;
}
#endregion
// ************************************************************
// *************** Client Connection Logic *******************
// ************************************************************
#region client connection logic
TcpClient client;
///
/// Connect to server buttonm runs an asynchronous task that attempts to connect to a server,
/// and load the thumbnails from the server
///
private void ClientServerConnectButton_Click(object sender, RoutedEventArgs e)
{
// Run an async connection task
// This task establishes a connection, then
Task clientConnect = new(async () =>
{
client = new();
try
{
await client.ConnectAsync(ServerAddressConnectBox.Text, int.Parse(ServerPortConnectBox.Text));
if (client.Connected)
{
// Invoke the display to update
Dispatcher.Invoke(() =>
{
StatusTextBlock.Text = "Connected!";
StatusTextBlock.Foreground = new SolidColorBrush(Color.FromRgb(255, 255, 255));
});
// Run image downloading for the sprite
// This task is contained in the CommunicationHandler class
Progress progress = new Progress();
Task t = App.Context.GameCommunicationHandler.InitDataLoader(client.GetStream(), progress);
progress.ProgressChanged += Progress_ProgressChanged;
t.Start(); // t is asynchronous; we don't need to monitor/await progress as it fires its progress through the assigned handler
// We should, however, ensure that we don't make requests on client until progress is completed (which can be monitored via
// the Progress object)
}
else
{
Dispatcher.Invoke(() =>
{
StatusTextBlock.Text = "Connection Failed";
StatusTextBlock.Foreground = new SolidColorBrush(Color.FromRgb(255, 0, 0));
});
}
}
catch (Exception ex)
{
Dispatcher.Invoke(() =>
{
StatusTextBlock.Text = ex.Message[..(128 < ex.Message.Length ? 128 : ex.Message.Length)];
StatusTextBlock.Foreground = new SolidColorBrush(Color.FromRgb(255, 0, 0));
});
client?.Dispose();
}
});
}
///
/// Progress callback, that responds to changes in the state of the task
///
///
///
///
private void Progress_ProgressChanged(object sender, CommunicationHandler.ProgressData e)
{
// Load iamges as they arrive
if (e.CurrentActivity == CommunicationHandler.ProgressData.Activity.ImageDownloaded)
{
// Show the user the total number of images downloaded
Dispatcher.Invoke(() => StatusTextBlock.Text = $"Downloaded {e.Progress} of {App.Context.GameCommunicationHandler.ImageList.Count} images.");
// Attempt to retrieve the image data
if(App.Context.GameCommunicationHandler.ImageCollection.TryGetValue(e.Message, out CachedByteArray cachedByteArray))
{
BitmapImage image = new();
image.BeginInit();
image.StreamSource = new MemoryStream(cachedByteArray, false);
image.EndInit();
if (e.Message == App.Context.GameCommunicationHandler.BoardName)
{
// Set the board image to `image`
_ = Dispatcher.Invoke(() => BoardPreviewImage.Source = image);
}
else
{
// Create a new image object
Image spriteImage = new()
{
Source = image
};
// Image is a sprite, load it
LoadedSpriteGrid loadedSprite = null;
foreach (LoadedSpriteGrid item in ClientLoadedSprites.Children)
{
// If the image already exists, replace the current image with the new one
if (item.Equals(e.Message))
{
loadedSprite = item;
// Set the image to the new loaded image
Dispatcher.Invoke(() => loadedSprite.FrameImage = spriteImage);
break;
}
}
if (loadedSprite == null)
{
loadedSprite = new LoadedSpriteGrid(spriteImage, e.Message);
// Listen to any selection event
loadedSprite.MouseDown += SpriteImage_Tapped;
loadedSprite.TouchDown += SpriteImage_Tapped;
loadedSprite.StylusDown += SpriteImage_Tapped;
// Add the loaded image into the view
Dispatcher.Invoke(() => ClientLoadedSprites.Children.Add(loadedSprite));
}
}
}
}
}
LoadedSpriteGrid currentlySelected = null;
private void SpriteImage_Tapped(object sender, InputEventArgs e)
{
currentlySelected.frame.BorderThickness = new Thickness(0, 0, 0, 0);
currentlySelected = sender as LoadedSpriteGrid;
currentlySelected.frame.BorderThickness = new Thickness(3, 3, 3, 3);
// Finally, enable the button
CheckEnableVis();
}
///
/// Every check required to enable the 'Join Server' button
///
private void CheckEnableVis()
{
if (currentlySelected != null && PlayerNameBox.Text != "" && client.Connected)
ClientStartGameButton.IsEnabled = true;
else
ClientStartGameButton.IsEnabled = false;
}
///
/// Layout that comprises of a Border containing an Image
///
internal class LoadedSpriteGrid : Grid
{
string imageName;
public Border frame;
public Image FrameImage { get; set; }
public LoadedSpriteGrid(Image spriteImage, string imageName)
{
FrameImage = spriteImage;
frame = new();
frame.Child = FrameImage;
frame.BorderThickness = new Thickness(0, 0, 0, 0);
frame.BorderBrush = new SolidColorBrush(Color.FromArgb(255, 0, 100, 200));
Children.Add(frame);
this.imageName = imageName;
}
///
/// Gets if the two grids are referencing the same image (regardless of the loaded mip level)
///
///
///
public new bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
string otherName = ((LoadedSpriteGrid)obj).imageName.Replace(App.MIP_LOW, "").Replace(App.MIP_MEDIUM, "").Replace(App.MIP_HIGH, "").Replace(App.MIP_RAW, "");
string tName = imageName.Replace(App.MIP_LOW, "").Replace(App.MIP_MEDIUM, "").Replace(App.MIP_HIGH, "").Replace(App.MIP_RAW, "");
return tName.Equals(otherName);
}
}
private void PlayerNameBox_LostFocus(object sender, RoutedEventArgs e)
{
CheckEnableVis();
}
#endregion
// ************************************************************
// *************** Server Connection Logic *******************
// ************************************************************
#region server connection logic
private static readonly SolidColorBrush borderBrush = new(Color.FromRgb(255, 0, 0));
private void BrowseButton_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog ofd = new();
ofd.Filter = "Image Files|*.png;*.jpg";
if (ofd.ShowDialog() == true)
{
ImageSourceTextBox.Text = ofd.FileName;
UpdateImage().Start();
}
}
///
/// Update the image when the text box looses focus
///
///
///
private void ImageSourceTextBox_LostFocus(object sender, RoutedEventArgs e)
{
UpdateImage().Start();
}
private Task UpdateImage()
{
try
{
CommunicationHandler host = App.Context.GameCommunicationHandler;
using FileStream fs = File.OpenRead(ImageSourceTextBox.Text);
using MemoryStream ms = new();
fs.CopyTo(ms);
// Calc mips
ms.Position = 0;
App.Context.LoadStream(ms, Name, false);
ms.Position = 0;
BitmapImage gameBoardImage = new();
gameBoardImage.BeginInit();
gameBoardImage.StreamSource = ms;
gameBoardImage.EndInit();
PreviewImage.Source = gameBoardImage;
RedrawGrid();
}
catch (Exception e)
{
Console.WriteLine(e);
}
return Task.CompletedTask;
}
///
/// Draws the overlay grid, clearing existing elements if necessary
///
private void RedrawGrid()
{
PreviewImageOverlay.RowDefinitions.Clear();
PreviewImageOverlay.ColumnDefinitions.Clear();
PreviewImageOverlay.Children.Clear();
for (int i = 0; i < NumberOfColumns.Value; i++)
{
PreviewImageOverlay.ColumnDefinitions.Add(new ColumnDefinition());
}
for (int i = 0; i < NumberOfRows.Value; i++)
{
PreviewImageOverlay.RowDefinitions.Add(new RowDefinition());
}
// Draw the overlay
Rectangle overlay = new();
overlay.Fill = new SolidColorBrush(Color.FromArgb(128, 0, 128, 255));
_ = PreviewImageOverlay.Children.Add(overlay);
Grid.SetRow(overlay, (int)ZoomBoxStartRow.Value);
Grid.SetColumn(overlay, (int)ZoomBoxStartColumn.Value);
Grid.SetRowSpan(overlay, (int)ZoomBoxSpan.Value);
Grid.SetColumnSpan(overlay, (int)ZoomBoxSpan.Value);
// Draw the starting position
overlay = new();
overlay.Fill = new SolidColorBrush(Color.FromArgb(64, 0, 255, 128));
_ = PreviewImageOverlay.Children.Add(overlay);
Grid.SetRow(overlay, (int)StartingRow.Value);
Grid.SetColumn(overlay, (int)StartingColumn.Value);
// Draw grids onto the preview image
for (int v = 0; v < PreviewImageOverlay.RowDefinitions.Count; v++)
{
for (int u = 0; u < PreviewImageOverlay.ColumnDefinitions.Count; u++)
{
Border border = new();
border.BorderBrush = borderBrush;
border.BorderThickness = new Thickness(2);
_ = PreviewImageOverlay.Children.Add(border);
Grid.SetRow(border, v);
Grid.SetColumn(border, u);
}
}
}
///
/// Loads the selected sprites into the image list
///
///
///
private void GameSpritesBrowser_Click(object sender, RoutedEventArgs e)
{
CommunicationHandler host = App.Context.GameCommunicationHandler;
OpenFileDialog ofd = new();
ofd.Filter = "Image Files|*.png;*.jpg;*.gif";
ofd.Multiselect = true;
if (ofd.ShowDialog() == true)
{
string[] resultFiles = ofd.FileNames;
// Clear existing sprites
host.ClearSprites();
// Finally, load the sprites
foreach (string file in resultFiles)
{
using FileStream fs = File.OpenRead(file);
string fileName = Path.GetFileName(file);
host.ImageList.Add(fileName);
App.Context.LoadStream(fs, fileName, true);
}
}
}
#endregion
}
}