Newer
Older
Qwilight / Qwilight / ViewModel / SiteViewModel.cs
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Google.Protobuf;
using Qwilight.MSG;
using Qwilight.NoteFile;
using Qwilight.UIComponent;
using Qwilight.Utilities;
using Qwilight.View;
using Qwilight.View.SiteYell;
using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Windows.ApplicationModel.DataTransfer;
using Windows.Storage.Streams;
using Windows.System;

namespace Qwilight.ViewModel
{
    public sealed partial class SiteViewModel : Model
    {
        public sealed class NetSiteComputing : Computing
        {
            public override BaseNoteFile.NoteVariety NoteVarietyValue => default;

            public string NoteID { get; set; }

            public string WantLevelID { get; set; } = string.Empty;

            public BaseNoteFile.Handled HandledValue { get; set; }

            public ImageSource HandledWallDrawing => BaseUI.Instance.HandledWallDrawings[(int)HandledValue];

            public bool NotHaveIt { get; set; }

            public bool IsDefault { get; set; }

            public override void OnCompiled()
            {
            }

            public override void OnFault(Exception e)
            {
            }

            public override bool Equals(object obj) => obj is NetSiteComputing netSiteComputing && NoteID == netSiteComputing.NoteID;

            public override int GetHashCode() => NoteID.GetHashCode();
        }

        public SiteView View { get; set; }

        public enum SiteSituation
        {
            Default, Compiling, Net
        };

        readonly ConcurrentDictionary<int, ISiteYell> _siteYellsMap = new();
        readonly LinkedList<Func<ISiteYell>> _pendingNewSiteYellQueue = new();
        readonly List<Action> _pendingModifySiteYellQueue = new();
        readonly List<Action> _pendingWipeSiteYellQueue = new();
        NetSiteComputing _valueComputing;
        NetSiteComputing _defaultNetSiteComputing;
        string[] _netSiteNoteID;
        string _bundleEntryPath;
        double _lastPosition1BeforeCall;
        bool _isSiteYellsViewerLowest = true;
        string _siteName;
        string _siteNotify;
        string _input = string.Empty;
        string _siteHand;
        bool _isFavorNoteFile = true;
        bool _isAutoNetLevying;
        bool _isNew;
        bool _isNetSite;
        bool _isFavorModeComponent = true;
        bool _isFavorAudioMultiplier;
        AvatarGroup _avatarGroupValue;
        bool _isAutoSiteHand;
        bool _allowTotalLevying = true;
        bool _isGetNotify;
        bool _isAudioInput;
        SiteSituation _siteSituationValue;
        int _validHunterMode;
        ValidNetMode _validNetMode;
        string _bundleName;
        AvatarItem _avatarItemValue;
        bool _hasPendingNew;

        public PostableUIItem[] PostableUIItemCollection { get; } = Enumerable.Range(0, PostableItem.Values.Length).Select(i => new PostableUIItem
        {
            PostableItemValue = PostableItem.Values[i]
        }).ToArray();

        public bool IsTotalWantPostableUIItem => PostableUIItemCollection.All(postableUIItem => postableUIItem.IsWanted);

        public bool IsOpened { get; set; }

        [RelayCommand]
        static void OnTwilightConfigure()
        {
            if (TwilightSystem.Instance.IsSignedIn)
            {
                TwilightSystem.Instance.SendParallel<object>(Event.Types.EventID.CallConfigure, null);
            }
            else
            {
                ViewModels.Instance.TwilightConfigure.Open();
            }
        }

        [RelayCommand]
        static void OnSite() => ViewModels.Instance.SiteWindowValue.Toggle();

        [RelayCommand]
        static void OnUbuntu() => ViewModels.Instance.UbuntuValue.Toggle();

        [RelayCommand]
        void OnQuit()
        {
            TwilightSystem.Instance.StopBundle(SiteID);
            TwilightSystem.Instance.SendParallel(Event.Types.EventID.QuitSite, SiteID);
        }

        [RelayCommand]
        void OnTotalWantPostableItem(bool? e)
        {
            if (e.HasValue)
            {
                foreach (var postableUIItem in PostableUIItemCollection)
                {
                    postableUIItem.IsWanted = e.Value;
                }
                OnInputPostableItem();
            }
        }

        [RelayCommand]
        void OnViewBundle() => AvatarItemValue?.AvatarWwwValue?.ViewBundleCommand?.Execute(null);

        [RelayCommand]
        void OnNewUbuntu() => AvatarItemValue?.AvatarWwwValue?.NewUbuntuCommand?.Execute(null);

        [RelayCommand]
        void OnViewAvatar() => AvatarItemValue?.AvatarWwwValue?.ViewAvatarCommand?.Execute(null);

        [RelayCommand]
        void OnCallIO()
        {
            var siteAvatarID = AvatarItemValue?.AvatarID;
            if (!string.IsNullOrEmpty(siteAvatarID))
            {
                TwilightSystem.Instance.SendParallel(Event.Types.EventID.CallIo, new
                {
                    avatarID = siteAvatarID,
                    ioMillis = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
                });
            }
        }

        [RelayCommand]
        void OnNewSilentSite()
        {
            var siteAvatarID = AvatarItemValue?.AvatarID;
            if (!string.IsNullOrEmpty(siteAvatarID))
            {
                TwilightSystem.Instance.SendParallel(Event.Types.EventID.NewSilentSite, siteAvatarID);
            }
        }

        [RelayCommand]
        void OnExileAvatar()
        {
            var siteAvatarID = AvatarItemValue?.AvatarID;
            if (!string.IsNullOrEmpty(siteAvatarID))
            {
                TwilightSystem.Instance.SendParallel(Event.Types.EventID.ExileAvatar, new
                {
                    siteID = SiteID,
                    avatarID = siteAvatarID
                });
            }
        }

        [RelayCommand]
        void OnSetSiteHand()
        {
            var siteAvatarID = AvatarItemValue?.AvatarID;
            if (!string.IsNullOrEmpty(siteAvatarID))
            {
                TwilightSystem.Instance.SendParallel(Event.Types.EventID.SetSiteHand, new
                {
                    siteID = SiteID,
                    avatarID = siteAvatarID
                });
            }
        }

        [RelayCommand]
        void OnLevying() => HandleLevyingInput(false);

        [RelayCommand]
        void OnSaveAsNetBundle() => TwilightSystem.Instance.SendParallel(Event.Types.EventID.SaveAsBundle, new
        {
            bundleVariety = BundleItem.BundleVariety.Net,
            bundleEntryPath = _bundleEntryPath,
            etc = SiteID
        });

        [RelayCommand]
        void OnSaveNetBundle() => TwilightSystem.Instance.SendParallel(Event.Types.EventID.SaveBundle, new
        {
            etc = SiteID,
            bundleName = Path.GetFileNameWithoutExtension(BundleName)
        });

        [RelayCommand]
        void OnStopSiteNet() => TwilightSystem.Instance.SendParallel(Event.Types.EventID.QuitNet, SiteID);

        [RelayCommand]
        void OnSetValidHunterMode() => TwilightSystem.Instance.SendParallel(Event.Types.EventID.SetValidHunterMode, SiteID);

        [RelayCommand]
        void OnNetSIteComments() => TwilightSystem.Instance.SendParallel(Event.Types.EventID.CallNetSiteComments, SiteID);

        [RelayCommand]
        void OnSetSiteName()
        {
            var inputTextViewModel = ViewModels.Instance.InputTextValue;
            inputTextViewModel.Text = LanguageSystem.Instance.SetSiteNameContents;
            inputTextViewModel.Input = SiteName;
            inputTextViewModel.HandleOK = new Action<string>(text =>
            {
                TwilightSystem.Instance.SendParallel(Event.Types.EventID.SetSiteName, new
                {
                    siteID = SiteID,
                    siteName = text
                });
            });
            inputTextViewModel.Open();
        }

        [RelayCommand]
        static async Task OnPostFile()
        {
            var fileName = await StrongReferenceMessenger.Default.Send(new ViewFileWindow
            {
                Filters = new[] { "*" }
            });
            if (!string.IsNullOrEmpty(fileName))
            {
                TwilightSystem.Instance.SendParallel(Event.Types.EventID.PostFile, Path.GetFileName(fileName), UnsafeByteOperations.UnsafeWrap(await File.ReadAllBytesAsync(fileName).ConfigureAwait(false)));
            }
        }

        public void OnSetFavorNoteFile() => TwilightSystem.Instance.SendParallel(Event.Types.EventID.SetFavorNoteFile, SiteID);

        public void OnSetFavorModeComponent() => TwilightSystem.Instance.SendParallel(Event.Types.EventID.SetFavorModeComponent, SiteID);

        public void OnSetFavorAudioMultiplier() => TwilightSystem.Instance.SendParallel(Event.Types.EventID.SetFavorAudioMultiplier, SiteID);

        public void OnSetAutoSiteHand() => TwilightSystem.Instance.SendParallel(Event.Types.EventID.SetAutoSiteHand, SiteID);

        public void OnInputPostableItem()
        {
            OnPropertyChanged(nameof(IsTotalWantPostableUIItem));
            TwilightSystem.Instance.SendParallel(Event.Types.EventID.SetAllowedPostableItems, new
            {
                siteID = SiteID,
                allowedPostableItems = PostableUIItemCollection.Where(postableUIItem => postableUIItem.IsWanted).Select(postableUIitem => (int)postableUIitem.PostableItemValue.VarietyValue).ToArray()
            });
        }
        public void OnInputLower(KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                if (!string.IsNullOrWhiteSpace(Input))
                {
                    TwilightSystem.Instance.SendParallel(Event.Types.EventID.SiteYell, new
                    {
                        siteID = SiteID,
                        siteYell = Input
                    });
                }
                Input = string.Empty;
            }
        }

        public async Task OnEssentialInputLower(KeyEventArgs e)
        {
            if (e.Key == Key.V && Utility.HasInput(VirtualKey.LeftControl) && string.IsNullOrEmpty(Input))
            {
                try
                {
                    using var ras = await (await Clipboard.GetContent().GetBitmapAsync()).OpenReadAsync();
                    var data = ArrayPool<byte>.Shared.Rent((int)ras.Size);
                    try
                    {
                        await ras.ReadAsync(data.AsBuffer(), (uint)ras.Size, InputStreamOptions.None);
                        TwilightSystem.Instance.SendParallel(Event.Types.EventID.PostFile, ras.ContentType.Split("/")[1], UnsafeByteOperations.UnsafeWrap(data));
                    }
                    finally
                    {
                        ArrayPool<byte>.Shared.Return(data);
                    }
                    e.Handled = true;
                }
                catch
                {
                }
            }
        }

        public void OnPointedModified(bool e) => IsInputPointed = e;

        public void OnSiteYellsViewerMove(ScrollChangedEventArgs e)
        {
            var siteYellsViewer = View.SiteYellsViewer;
            if (_lastPosition1BeforeCall > 0.0)
            {
                siteYellsViewer.ScrollToVerticalOffset(siteYellsViewer.ExtentHeight - siteYellsViewer.ActualHeight - _lastPosition1BeforeCall);
                _lastPosition1BeforeCall = 0.0;
            }
            else
            {
                var siteYellID = (SiteYellCollection.FirstOrDefault() as ISiteYell)?.SiteYellID;
                if (siteYellID.HasValue && siteYellID.Value > 0 && siteYellsViewer.VerticalOffset == 0.0)
                {
                    TwilightSystem.Instance.SendParallel(Event.Types.EventID.GetSiteYells, new
                    {
                        siteID = SiteID,
                        siteYellID = siteYellID.Value
                    });
                }
            }
            _isSiteYellsViewerLowest = siteYellsViewer.VerticalOffset + siteYellsViewer.ActualHeight >= siteYellsViewer.ExtentHeight;
        }

        public ModeComponent ModeComponentValue { get; } = new();

        public ObservableCollection<ISiteYell> SiteYellCollection { get; } = new();

        public ObservableCollection<AvatarItem> AvatarItemCollection { get; } = new();

        public ObservableCollection<NetSiteComputing> ComputingValues { get; } = new();

        public AvatarGroup[] AvatarGroupCollection { get; } = Enumerable.Range(0, 5).Select(data => new AvatarGroup
        {
            Data = data
        }).ToArray();

        public ValidNetMode[] ValidNetModeCollection { get; } = Enumerable.Range(0, 5).Select(data => new ValidNetMode
        {
            Data = data
        }).ToArray();

        public NetSiteComputing ComputingValue
        {
            get => _valueComputing;

            set
            {
                if (SetProperty(ref _valueComputing, value, nameof(ComputingValue)))
                {
                    OnPropertyChanged(nameof(LengthText));
                    NotifyComputedValues();
                }
            }
        }

        public bool AllowSiteHandLevying => !AllowTotalLevying || AvatarItemCollection.All(avatarItem => avatarItem.IsMe || avatarItem.AvatarID.StartsWith('@') || avatarItem.AvatarConfigureValue == AvatarItem.AvatarConfigure.Levying);

        public bool IsSiteHand => SiteHand == TwilightSystem.Instance.AvatarID;

        public bool IsInputPointed { get; set; }

        public bool IsGetNotify
        {
            get => _isGetNotify;

            set => SetProperty(ref _isGetNotify, value, nameof(IsGetNotify));
        }

        public bool IsAudioInput
        {
            get => _isAudioInput;

            set
            {
                if (SetProperty(ref _isAudioInput, value, nameof(IsAudioInput)))
                {
                    NotifyIsSendingAudioInput();
                }
            }
        }

        public bool IsFavorNoteFile
        {
            get => _isFavorNoteFile;

            set => SetProperty(ref _isFavorNoteFile, value, nameof(IsFavorNoteFile));
        }

        public bool IsAutoNetLevying
        {
            get => _isAutoNetLevying;

            set => SetProperty(ref _isAutoNetLevying, value, nameof(IsAutoNetLevying));
        }

        public bool IsFavorModeComponent
        {
            get => _isFavorModeComponent;

            set => SetProperty(ref _isFavorModeComponent, value, nameof(IsFavorModeComponent));
        }

        public bool IsFavorAudioMultiplier
        {
            get => _isFavorAudioMultiplier;

            set
            {
                if (SetProperty(ref _isFavorAudioMultiplier, value, nameof(IsFavorAudioMultiplier)))
                {
                    OnPropertyChanged(nameof(BPMText));
                }
            }
        }

        public bool IsAutoSiteHand
        {
            get => _isAutoSiteHand;

            set => SetProperty(ref _isAutoSiteHand, value, nameof(IsAutoSiteHand));
        }

        public bool AllowTotalLevying
        {
            get => _allowTotalLevying;

            set
            {
                if (SetProperty(ref _allowTotalLevying, value, nameof(AllowTotalLevying)))
                {
                    OnPropertyChanged(nameof(AllowSiteHandLevying));
                }
            }
        }

        public int ValidHunterMode
        {
            get => _validHunterMode;

            set
            {
                if (SetProperty(ref _validHunterMode, value, nameof(ValidHunterMode)))
                {
                    OnPropertyChanged(nameof(ValidHunterModeText));
                }
            }
        }

        public string ValidHunterModeText => ValidHunterMode switch
        {
            0 => LanguageSystem.Instance.StandValidHunterModeText,
            1 => LanguageSystem.Instance.PointValidHunterModeText,
            2 => LanguageSystem.Instance.BandValidHunterModeText,
            _ => default,
        };

        public string BundleName
        {
            get => _bundleName;

            set
            {
                if (SetProperty(ref _bundleName, value, nameof(BundleName)))
                {
                    OnPropertyChanged(nameof(CanSaveNetBundle));
                }
            }
        }

        public AvatarGroup AvatarGroupValue
        {
            get => _avatarGroupValue;

            set
            {
                if (SetProperty(ref _avatarGroupValue, value, nameof(AvatarGroupValue)))
                {
                    TwilightSystem.Instance.SendParallel(Event.Types.EventID.SetAvatarGroup, new
                    {
                        siteID = SiteID,
                        avatarGroup = value.Data
                    });
                }
            }
        }

        public ValidNetMode ValidNetModeValue
        {
            get => _validNetMode;

            set
            {
                if (SetProperty(ref _validNetMode, value, nameof(ValidNetModeValue)))
                {
                    OnPropertyChanged(nameof(CanSetPostableItems));
                    if (IsSiteHand)
                    {
                        TwilightSystem.Instance.SendParallel(Event.Types.EventID.SetValidNetMode, new
                        {
                            siteID = SiteID,
                            validNetMode = value.Data
                        });
                    }
                }
            }
        }

        public bool WasNotify { get; set; }

        public bool IsEditable { get; set; }

        public bool CanAudioInput { get; set; }

        public string HighestInputCountText => Utility.GetHighestInputCountText(ComputingValue?.AverageInputCount ?? 0.0, ComputingValue?.HighestInputCount ?? 0, IsFavorAudioMultiplier ? 1.0 : ModeComponentValue.AudioMultiplier);

        public string BPMText => IsFavorAudioMultiplier ? $"{ComputingValue?.BPM ?? Component.StandardBPM} BPM" : Utility.GetBPMText(ComputingValue?.BPM ?? Component.StandardBPM, ModeComponentValue.AudioMultiplier);

        public string LengthText => Utility.GetLengthText(ComputingValue?.Length ?? 0.0);

        public bool IsSendingAudioInput => IsAudioInput && Configure.Instance.AudioInput;

        public void NotifyComputedValues()
        {
            OnPropertyChanged(nameof(HighestInputCountText));
            OnPropertyChanged(nameof(BPMText));
        }

        public void NotifyIsSendingAudioInput() => OnPropertyChanged(nameof(IsSendingAudioInput));

        public bool IsNew
        {
            get => _isNew;

            set => SetProperty(ref _isNew, value, nameof(SiteName));
        }

        public bool IsNetSite
        {
            get => _isNetSite;

            set => SetProperty(ref _isNetSite, value, nameof(IsNetSite));
        }

        public string SiteName
        {
            get => IsNew ? $"{_siteName} 💬" : _siteName;

            set => SetProperty(ref _siteName, value, nameof(SiteName));
        }

        public string SiteNotify
        {
            get => _siteNotify;

            set => SetProperty(ref _siteNotify, value, nameof(SiteNotify));
        }

        public string SiteID { get; set; }

        public string Input
        {
            get => _input;

            set => SetProperty(ref _input, value, nameof(Input));
        }

        public bool IsIdle => SiteSituationValue == SiteSituation.Default;

        public bool CanSaveNetBundle => !string.IsNullOrEmpty(BundleName) && IsIdle;

        public bool CanSetTwilightConfigures => IsSiteHand && IsIdle;

        public bool CanSetPostableItems => IsSiteHand && IsIdle && ValidNetModeValue.Data > 0;

        public bool CanSiteHandLevying => IsSiteHand && IsIdle;

        public bool CanStopSiteNet => IsSiteHand && !IsIdle;

        public SiteSituation SiteSituationValue
        {
            get => _siteSituationValue;

            set
            {
                if (SetProperty(ref _siteSituationValue, value, nameof(SiteSituationValue)))
                {
                    OnPropertyChanged(nameof(IsIdle));
                    OnPropertyChanged(nameof(CanSaveNetBundle));
                    OnPropertyChanged(nameof(CanSetTwilightConfigures));
                    OnPropertyChanged(nameof(CanSetPostableItems));
                    OnPropertyChanged(nameof(CanSiteHandLevying));
                    OnPropertyChanged(nameof(CanStopSiteNet));
                }
            }
        }

        public AvatarItem AvatarItemValue
        {
            get => _avatarItemValue;

            set => SetProperty(ref _avatarItemValue, value, nameof(AvatarItemValue));
        }

        public string SiteHand
        {
            get => _siteHand;

            set
            {
                if (SetProperty(ref _siteHand, value))
                {
                    OnPropertyChanged(nameof(IsSiteHand));
                    OnPropertyChanged(nameof(IsIdle));
                    OnPropertyChanged(nameof(CanSetTwilightConfigures));
                    OnPropertyChanged(nameof(CanSetPostableItems));
                    OnPropertyChanged(nameof(CanSiteHandLevying));
                    OnPropertyChanged(nameof(CanStopSiteNet));
                }
            }
        }

        public JSON.TwilightCallSiteAvatar? PendingCallSiteAvatarData { get; set; }

        public void SetCallSiteAvatarData(JSON.TwilightCallSiteAvatar pendingCallSiteAvatarData)
        {
            var siteHand = pendingCallSiteAvatarData.siteHand;
            SiteName = LanguageSystem.Instance.GetSiteName(pendingCallSiteAvatarData.siteName);
            SiteHand = siteHand;
            SiteSituationValue = (SiteSituation)pendingCallSiteAvatarData.situationValue;
            Utility.SetUICollection(AvatarItemCollection, pendingCallSiteAvatarData.data.Select(data =>
            {
                var avatarID = data.avatarID;
                return new AvatarItem(avatarID)
                {
                    AvatarConfigureValue = (AvatarItem.AvatarConfigure)data.avatarConfigure,
                    IsSiteHand = !string.IsNullOrEmpty(siteHand) && avatarID == siteHand,
                    IsMe = Utility.GetDefaultAvatarID(avatarID) == TwilightSystem.Instance.AvatarID,
                    AvatarName = data.avatarName,
                    AvatarGroupValue = new()
                    {
                        Data = data.avatarGroup
                    },
                    IsValve = data.isValve,
                    IsAudioInput = data.isAudioInput
                };
            }).ToArray(), null, null, (valueItem, targetItem) =>
            {
                valueItem.AvatarConfigureValue = targetItem.AvatarConfigureValue;
                valueItem.IsSiteHand = targetItem.IsSiteHand;
                valueItem.IsMe = targetItem.IsMe;
                valueItem.AvatarGroupValue = targetItem.AvatarGroupValue;
                valueItem.IsAudioInput = targetItem.IsAudioInput;
            });
            OnPropertyChanged(nameof(AllowSiteHandLevying));
            if (pendingCallSiteAvatarData.setNoteFile)
            {
                CallSetNoteFile();
            }
        }

        public JSON.TwilightCallSiteNet PendingCallSiteNetData { get; set; }

        public void SetComputingValues(JSON.TwilightCallSiteNet twilightCallSiteNet = null)
        {
            if (IsNetSite)
            {
                var mainViewModel = ViewModels.Instance.MainValue;
                if (twilightCallSiteNet != null)
                {
                    var noteID = twilightCallSiteNet.noteID;
                    _defaultNetSiteComputing = new()
                    {
                        NoteID = noteID,
                        IsDefault = true,
                        Title = twilightCallSiteNet.title,
                        Artist = twilightCallSiteNet.artist,
                        LevelText = twilightCallSiteNet.levelText,
                        LevelValue = twilightCallSiteNet.level,
                        Genre = twilightCallSiteNet.genre,
                        JudgmentStage = twilightCallSiteNet.judgmentStage,
                        HitPointsValue = twilightCallSiteNet.hitPointsValue,
                        TotalNotes = twilightCallSiteNet.totalNotes,
                        LongNotes = twilightCallSiteNet.longNotes,
                        AutoableNotes = twilightCallSiteNet.autoableNotes,
                        TrapNotes = twilightCallSiteNet.trapNotes,
                        HighestInputCount = twilightCallSiteNet.highestInputCount,
                        Length = twilightCallSiteNet.length,
                        BPM = twilightCallSiteNet.bpm,
                        LowestBPM = twilightCallSiteNet.lowestBPM,
                        HighestBPM = twilightCallSiteNet.highestBPM,
                        InputMode = twilightCallSiteNet.inputMode,
                        WantLevelID = twilightCallSiteNet.wantLevelID,
                        HandledValue = mainViewModel.NoteID512s.TryGetValue(noteID, out var netSiteNoteFile) ? netSiteNoteFile.HandledValue : BaseNoteFile.Handled.Not,
                        IsAutoLongNote = twilightCallSiteNet.isAutoLongNote
                    };
                    _netSiteNoteID = twilightCallSiteNet.noteIDs;
                    _bundleEntryPath = twilightCallSiteNet.bundleEntryPath;
                    BundleName = twilightCallSiteNet.bundleName;
                    ModeComponentValue.ComputingValue = _defaultNetSiteComputing;
                }
                _defaultNetSiteComputing.NotHaveIt = !mainViewModel.NoteID512s.ContainsKey(_defaultNetSiteComputing.NoteID);
                UIHandler.Instance.HandleParallel(() =>
                {
                    var noteID = ComputingValue?.NoteID;
                    ComputingValues.Clear();
                    ComputingValues.Add(_defaultNetSiteComputing);
                    foreach (var netSiteNoteID in _netSiteNoteID)
                    {
                        if (netSiteNoteID != _defaultNetSiteComputing.NoteID && mainViewModel.NoteID512s.TryGetValue(netSiteNoteID, out var netSiteNoteFile))
                        {
                            var netSiteComputing = new NetSiteComputing();
                            netSiteComputing.NoteID = netSiteNoteID;
                            netSiteComputing.Title = netSiteNoteFile.Title;
                            netSiteComputing.Artist = netSiteNoteFile.Artist;
                            netSiteComputing.LevelText = netSiteNoteFile.LevelText;
                            netSiteComputing.LevelValue = netSiteNoteFile.LevelValue;
                            netSiteComputing.Genre = netSiteNoteFile.Genre;
                            netSiteComputing.JudgmentStage = netSiteNoteFile.JudgmentStage;
                            netSiteComputing.HitPointsValue = netSiteNoteFile.HitPointsValue;
                            netSiteComputing.TotalNotes = netSiteNoteFile.TotalNotes;
                            netSiteComputing.LongNotes = netSiteNoteFile.LongNotes;
                            netSiteComputing.AutoableNotes = netSiteNoteFile.AutoableNotes;
                            netSiteComputing.TrapNotes = netSiteNoteFile.TrapNotes;
                            netSiteComputing.HighestInputCount = netSiteNoteFile.HighestInputCount;
                            netSiteComputing.Length = netSiteNoteFile.Length;
                            netSiteComputing.BPM = netSiteNoteFile.BPM;
                            netSiteComputing.LowestBPM = netSiteNoteFile.LowestBPM;
                            netSiteComputing.HighestBPM = netSiteNoteFile.HighestBPM;
                            netSiteComputing.InputMode = netSiteNoteFile.InputMode;
                            netSiteComputing.WantLevelID = netSiteNoteFile.WantLevelID;
                            netSiteComputing.HandledValue = netSiteNoteFile.HandledValue;
                            ComputingValues.Add(netSiteComputing);
                        }
                    }
                    ComputingValue = IsSiteHand ? _defaultNetSiteComputing : ComputingValues.FirstOrDefault(valueComputing => valueComputing.NoteID == noteID) ?? ComputingValues.First();
                });
            }
        }

        public void SetAllowedPostableItems(JSON.TwilightCallSiteNet twilightCallSiteNet)
        {
            foreach (var postableUIItem in PostableUIItemCollection)
            {
                postableUIItem.IsWanted = twilightCallSiteNet.allowedPostableItems.Contains((int)postableUIItem.PostableItemValue.VarietyValue);
            }
            OnPropertyChanged(nameof(IsTotalWantPostableUIItem));
        }

        public void SetCallSiteNetData(JSON.TwilightCallSiteNet pendingTwilightCallSiteNetData)
        {
            IsFavorNoteFile = pendingTwilightCallSiteNetData.isFavorNoteFile;
            IsFavorModeComponent = pendingTwilightCallSiteNetData.isFavorModeComponent;
            IsFavorAudioMultiplier = pendingTwilightCallSiteNetData.isFavorAudioMultiplier;
            ValidHunterMode = pendingTwilightCallSiteNetData.validHunterMode;
            ValidNetModeValue = new()
            {
                Data = pendingTwilightCallSiteNetData.validNetMode
            };
            SetAllowedPostableItems(pendingTwilightCallSiteNetData);
            IsAutoSiteHand = pendingTwilightCallSiteNetData.isAutoSiteHand;
            if (IsSiteHand)
            {
                SetComputingValues(pendingTwilightCallSiteNetData);
            }
            else
            {
                UIHandler.Instance.HandleParallel(() =>
                {
                    var lastComputingValues = ComputingValues.ToArray();
                    SetComputingValues(pendingTwilightCallSiteNetData);
                    if (IsAutoNetLevying && lastComputingValues.Except(ComputingValues).Any())
                    {
                        HandleLevyingInput(true);
                    }
                    if (ViewModels.Instance.SiteContainerValue.IsOpened && IsOpened)
                    {
                        var mainViewModel = ViewModels.Instance.MainValue;
                        if (mainViewModel.NoteID512s.TryGetValue(ComputingValue.NoteID, out var noteFile))
                        {
                            mainViewModel.EntryItemValue = noteFile.EntryItem;
                        }
                    }
                });
            }
        }

        public JSON.TwilightCallSiteModeComponent? PendingCallSiteModeComponentData { get; set; }

        public void SetCallSiteModeComponentData(JSON.TwilightCallSiteModeComponent pendingTwilightCallModeComponentData)
        {
            ModeComponentValue.CopyAsJSON(pendingTwilightCallModeComponentData.modeComponentData);
            NotifyComputedValues();
        }

        public void HandleLevyingInput(bool isSilent)
        {
            if (ComputingValue != null && ViewModels.Instance.MainValue.NoteID512s.ContainsKey(ComputingValue.NoteID))
            {
                TwilightSystem.Instance.SendParallel(Event.Types.EventID.LevyNet, SiteID);
            }
            else if (!isSilent)
            {
                NotifySystem.Instance.Notify(NotifySystem.NotifyVariety.Warning, NotifySystem.NotifyConfigure.Default, LanguageSystem.Instance.HasNotNetNoteFile);
            }
        }

        public void CallSetNoteFile()
        {
            if (IsNetSite && IsSiteHand)
            {
                var entryItem = ViewModels.Instance.MainValue.EntryItemValue;
                var noteFile = entryItem?.NoteFile;
                if (noteFile?.IsLogical == false)
                {
                    TwilightSystem.Instance.SendParallel(Event.Types.EventID.SetNoteFile, new
                    {
                        siteID = SiteID,
                        noteID = noteFile.GetNoteID512(),
                        noteIDs = IsFavorNoteFile ? noteFile.EntryItem.CompatibleNoteFiles.Select(noteFile => noteFile.GetNoteID512()) : new[] { noteFile.GetNoteID512() },
                        title = noteFile.Title,
                        artist = noteFile.Artist,
                        genre = noteFile.Genre,
                        levelText = noteFile.LevelText,
                        level = noteFile.LevelValue,
                        wantLevelID = noteFile.WantLevelID,
                        judgmentStage = noteFile.JudgmentStage,
                        hitPointsValue = noteFile.HitPointsValue,
                        totalNotes = noteFile.TotalNotes,
                        longNotes = noteFile.LongNotes,
                        autoableNotes = noteFile.AutoableNotes,
                        trapNotes = noteFile.TrapNotes,
                        highestInputCount = noteFile.HighestInputCount,
                        length = noteFile.Length,
                        bpm = noteFile.BPM,
                        lowestBPM = noteFile.LowestBPM,
                        highestBPM = noteFile.HighestBPM,
                        inputMode = noteFile.InputMode,
                        isAutoLongNote = noteFile.IsAutoLongNote,
                        bundleEntryPath = noteFile.EntryItem.EntryPath
                    });
                }
            }
        }

        public void CallUpdateModeComponent()
        {
            if (IsNetSite && IsSiteHand)
            {
                TwilightSystem.Instance.SendParallel(Event.Types.EventID.SetModeComponent, new
                {
                    siteID = SiteID,
                    data = ViewModels.Instance.MainValue.ModeComponentValue.GetJSON()
                });
            }
        }

        public void NewSiteYells(ICollection<JSON.TwilightSiteYellItem> siteYells, bool isGetSiteYell)
        {
            var siteContainerViewModel = ViewModels.Instance.SiteContainerValue;
            var targetAvatarID = TwilightSystem.Instance.AvatarID;
            var isMySiteYell = siteYells.Any(data => data.avatarID == targetAvatarID);
            var pendingNewSiteYells = siteYells.Select(data => new Func<ISiteYell>(() =>
            {
                var avatarID = data.avatarID;
                var avatarName = data.avatarName;
                var date = DateTimeOffset.FromUnixTimeMilliseconds(data.date).LocalDateTime.ToLongTimeString();
                var siteYellID = data.siteYellID;
                var siteYell = data.siteYell;
                switch (avatarName)
                {
                    case "@Enter":
                        return new EnterSiteYell(avatarID, siteYell, date, siteYellID);
                    case "@Quit":
                        return new QuitSiteYell(avatarID, siteYell, date, siteYellID);
                    case "@Site":
                        return new NewSiteYell(avatarID, siteYell, date, siteYellID);
                    case "@Net":
                        return new NewNetSiteYell(avatarID, siteYell, date, siteYellID);
                    case "@Comment":
                        return new CommentSiteYell(siteYell, date, siteYellID);
                    case "@Ability":
                        return new AbilitySiteYell(siteYell, date, siteYellID);
                    case "@Level":
                        return new LevelSiteYell(siteYell, date, siteYellID);
                    case "@Notify":
                        SiteNotify = siteYell;
                        return new TaehuiSiteYell(siteYell, date, siteYellID);
                    case "@Invite":
                        return new InviteSiteYell(avatarID, siteYell, date, siteYellID);
                    case "@TV":
                        return new TVSiteYell(avatarID, siteYell, date, siteYellID);
                    case "@Wiped":
                        return new WipedSiteYell(avatarID, siteYell, date, siteYellID);
                    case "":
                        return new NotifySiteYell(siteYell, siteYellID);
                    default:
                        var href = Utility.CompileSiteYells(siteYell);
                        var position = href.IndexOf('?');
                        switch (Utility.GetFileFormat(position != -1 ? href.Substring(0, position) : href))
                        {
                            case Utility.FileFormatFlag.Audio:
                                return new AudioSiteYell(avatarID, avatarName, date, siteYell, href, siteYellID);
                            case Utility.FileFormatFlag.Media:
                                return new MediaSiteYell(avatarID, avatarName, date, siteYell, href, siteYellID);
                            case Utility.FileFormatFlag.Drawing:
                                return new DrawingSiteYell(avatarID, avatarName, date, siteYell, href, siteYellID);
                            default:
                                return new DefaultSiteYell(avatarID, avatarName, date, siteYell, href, siteYellID);
                        }
                }
            }));
            if (siteContainerViewModel.IsOpened && IsOpened)
            {
                UIHandler.Instance.HandleParallel(() =>
                {
                    if (isGetSiteYell)
                    {
                        foreach (var pendingNewSiteYell in pendingNewSiteYells.Reverse())
                        {
                            SiteYellCollection.Insert(0, PutPlatformSiteYell(pendingNewSiteYell()));
                        }
                    }
                    else
                    {
                        foreach (var pendingNewSiteYell in pendingNewSiteYells)
                        {
                            SiteYellCollection.Add(PutPlatformSiteYell(pendingNewSiteYell()));
                        }
                    }
                    var siteYellsViewer = View.SiteYellsViewer;
                    if (isGetSiteYell)
                    {
                        _lastPosition1BeforeCall = siteYellsViewer.ExtentHeight - siteYellsViewer.ActualHeight;
                    }
                    else if (isMySiteYell || _isSiteYellsViewerLowest)
                    {
                        siteYellsViewer.ScrollToEnd();
                    }
                });
            }
            else
            {
                lock (_pendingNewSiteYellQueue)
                {
                    if (isGetSiteYell)
                    {
                        foreach (var pendingNewSiteYell in pendingNewSiteYells.Reverse())
                        {
                            _pendingNewSiteYellQueue.AddFirst(pendingNewSiteYell);
                        }
                    }
                    else
                    {
                        foreach (var putSiteYell in pendingNewSiteYells)
                        {
                            _pendingNewSiteYellQueue.AddLast(putSiteYell);
                        }
                    }
                }
            }
        }

        public void ModifySiteYell(JSON.TwilightModifySiteYell twilightModifySiteYell)
        {
            var siteContainerViewModel = ViewModels.Instance.SiteContainerValue;
            var targetAvatarID = TwilightSystem.Instance.AvatarID;
            var pendingModifySiteYell = new Action(() =>
            {
                if (_siteYellsMap.TryGetValue(twilightModifySiteYell.siteYellID, out var siteYell))
                {
                    siteYell.SiteYell = twilightModifySiteYell.siteYell;
                }
            });
            if (siteContainerViewModel.IsOpened && IsOpened)
            {
                pendingModifySiteYell();
            }
            else
            {
                lock (_pendingModifySiteYellQueue)
                {
                    _pendingModifySiteYellQueue.Add(pendingModifySiteYell);
                }
            }
        }

        public void WipeSiteYell(JSON.TwilightWipeSiteYell twilightWipeSiteYell)
        {
            var siteContainerViewModel = ViewModels.Instance.SiteContainerValue;
            var targetAvatarID = TwilightSystem.Instance.AvatarID;
            var pendingWipeSiteYell = new Action(() =>
            {
                if (_siteYellsMap.TryGetValue(twilightWipeSiteYell.siteYellID, out var siteYell))
                {
                    UIHandler.Instance.HandleParallel(() =>
                    {
                        var i = SiteYellCollection.IndexOf(siteYell);
                        SiteYellCollection.Remove(siteYell);
                        SiteYellCollection.Insert(i, new WipedSiteYell(siteYell.AvatarID, siteYell.AvatarName, siteYell.Date, siteYell.SiteYellID));
                    });
                }
            });
            if (siteContainerViewModel.IsOpened && IsOpened)
            {
                pendingWipeSiteYell();
            }
            else
            {
                lock (_pendingWipeSiteYellQueue)
                {
                    _pendingWipeSiteYellQueue.Add(pendingWipeSiteYell);
                }
            }
        }

        public void Notify(JSON.TwilightSiteYellItem siteYell)
        {
            if (IsGetNotify)
            {
                if (!WasNotify && !ViewModels.Instance.SiteContainerValue.IsOpened)
                {
                    NotifySystem.Instance.Notify(NotifySystem.NotifyVariety.Info, NotifySystem.NotifyConfigure.Default, siteYell.ToString(), false);
                    WasNotify = true;
                }
            }
        }

        ISiteYell PutPlatformSiteYell(ISiteYell siteYell)
        {
            var siteYellID = siteYell.SiteYellID;
            if (siteYellID != -1)
            {
                _siteYellsMap[siteYellID] = siteYell;
            }
            return siteYell;
        }

        public void OnOpened()
        {
            if (IsOpened)
            {
                WasNotify = false;
                lock (_pendingNewSiteYellQueue)
                {
                    if (_pendingNewSiteYellQueue.Count > 0)
                    {
                        foreach (var pendingNewSiteYell in _pendingNewSiteYellQueue)
                        {
                            SiteYellCollection.Add(PutPlatformSiteYell(pendingNewSiteYell()));
                        }
                        _pendingNewSiteYellQueue.Clear();
                    }
                }
                lock (_pendingModifySiteYellQueue)
                {
                    if (_pendingModifySiteYellQueue.Count > 0)
                    {
                        foreach (var pendingModifySiteYell in _pendingModifySiteYellQueue)
                        {
                            pendingModifySiteYell();
                        }
                        _pendingModifySiteYellQueue.Clear();
                    }
                }
                lock (_pendingWipeSiteYellQueue)
                {
                    if (_pendingWipeSiteYellQueue.Count > 0)
                    {
                        foreach (var pendingWipeSiteYell in _pendingWipeSiteYellQueue)
                        {
                            pendingWipeSiteYell();
                        }
                        _pendingWipeSiteYellQueue.Clear();
                    }
                }
                if (_isSiteYellsViewerLowest)
                {
                    View.SiteYellsViewer.ScrollToEnd();
                }
                if (IsNew)
                {
                    IsNew = false;
                }
            }
            if (PendingCallSiteAvatarData.HasValue)
            {
                var pendingCallSiteAvatarData = PendingCallSiteAvatarData.Value;
                PendingCallSiteAvatarData = null;
                SetCallSiteAvatarData(pendingCallSiteAvatarData);
            }
            if (PendingCallSiteNetData != null)
            {
                var pendingSetSiteNetData = PendingCallSiteNetData;
                PendingCallSiteNetData = null;
                SetCallSiteNetData(pendingSetSiteNetData);
            }
            if (PendingCallSiteModeComponentData.HasValue)
            {
                var pendingCallSiteModeComponentData = PendingCallSiteModeComponentData.Value;
                PendingCallSiteModeComponentData = null;
                SetCallSiteModeComponentData(pendingCallSiteModeComponentData);
            }
            if (_hasPendingNew)
            {
                _hasPendingNew = false;
                IsNew = true;
            }
        }

        public void SetSiteAvatar(JSON.TwilightCallSiteAvatar pendingCallSiteAvatarData)
        {
            if (ViewModels.Instance.SiteContainerValue.IsOpened)
            {
                SetCallSiteAvatarData(pendingCallSiteAvatarData);
            }
            else
            {
                PendingCallSiteAvatarData = pendingCallSiteAvatarData;
            }
        }

        public void SetSiteNet(JSON.TwilightCallSiteNet pendingCallSiteNetData)
        {
            if (ViewModels.Instance.SiteContainerValue.IsOpened)
            {
                SetCallSiteNetData(pendingCallSiteNetData);
            }
            else
            {
                PendingCallSiteNetData = pendingCallSiteNetData;
            }
        }

        public void SetSiteModeComponent(JSON.TwilightCallSiteModeComponent pendingCallSiteModeComponentData)
        {
            if (ViewModels.Instance.SiteContainerValue.IsOpened)
            {
                SetCallSiteModeComponentData(pendingCallSiteModeComponentData);
            }
            else
            {
                PendingCallSiteModeComponentData = pendingCallSiteModeComponentData;
            }
        }

        public void SetNew()
        {
            if (!IsOpened)
            {
                if (ViewModels.Instance.SiteContainerValue.IsOpened)
                {
                    IsNew = true;
                }
                else
                {
                    _hasPendingNew = true;
                }
            }
        }

        public void AudioInput(byte[] data, int length) => TwilightSystem.Instance.SendParallel(Event.Types.EventID.AudioInput, SiteID, UnsafeByteOperations.UnsafeWrap(data.AsMemory(0, length)));
    }
}