private static void UpdateMultiViewers(AtemState state, UpdateResultImpl result, ICommand command)
        {
            if (command is MultiviewerConfigV811Command multiviewer811Cmd)
            {
                state.Info.MultiViewers = new InfoState.MultiViewInfoState
                {
                    CanRouteInputs   = multiviewer811Cmd.CanRouteInputs,
                    SupportsVuMeters = multiviewer811Cmd.SupportsVuMeters,
                    SupportsProgramPreviewSwapped = multiviewer811Cmd.CanSwapPreviewProgram,
                    SupportsQuadrantLayout        = multiviewer811Cmd.SupportsQuadrants,
                    SupportsToggleSafeArea        = multiviewer811Cmd.CanToggleSafeArea,
                    CanChangeLayout = multiviewer811Cmd.CanChangeLayout,
                    //CanChangeVuMeterOpacity = multiviewer811Cmd.CanChangeVuMeterOpacity,
                };

                state.Settings.MultiViewers.ForEach(mv => mv.Windows = UpdaterUtil.UpdateList(mv.Windows,
                                                                                              multiviewer811Cmd.WindowCount,
                                                                                              w => new MultiViewerState.WindowState()));
                result.SetSuccess(new[] { $"Info.MultiViewers", $"Settings.MultiViewers" });
            }
            else if (command is MultiviewerConfigV8Command multiview8Cmd)
            {
                state.Info.MultiViewers = new InfoState.MultiViewInfoState
                {
                    CanRouteInputs   = multiview8Cmd.CanRouteInputs,
                    SupportsVuMeters = multiview8Cmd.SupportsVuMeters,
                    SupportsProgramPreviewSwapped = multiview8Cmd.CanSwapPreviewProgram,
                    SupportsQuadrantLayout        = multiview8Cmd.SupportsQuadrants,
                    SupportsToggleSafeArea        = multiview8Cmd.CanToggleSafeArea,
                };

                state.Settings.MultiViewers = UpdaterUtil.UpdateList(state.Settings.MultiViewers, multiview8Cmd.Count, i => new MultiViewerState
                {
                    Windows = new List <MultiViewerState.WindowState>(), // Size gets done in a second
                });
                state.Settings.MultiViewers.ForEach(mv => mv.Windows = UpdaterUtil.UpdateList(mv.Windows,
                                                                                              multiview8Cmd.WindowCount,
                                                                                              w => new MultiViewerState.WindowState()));
                result.SetSuccess(new[] { $"Info.MultiViewers", $"Settings.MultiViewers" });
            }
            else if (command is MultiviewerConfigCommand multiviewCmd)
            {
                state.Info.MultiViewers = new InfoState.MultiViewInfoState
                {
                    CanRouteInputs = multiviewCmd.CanRouteInputs,
                    SupportsProgramPreviewSwapped = multiviewCmd.CanSwapPreviewProgram
                };

                state.Settings.MultiViewers = UpdaterUtil.UpdateList(state.Settings.MultiViewers, multiviewCmd.Count, i => new MultiViewerState
                {
                    Windows = new List <MultiViewerState.WindowState>(), // Size gets done in a second
                });
                state.Settings.MultiViewers.ForEach(mv => mv.Windows = UpdaterUtil.UpdateList(mv.Windows,
                                                                                              multiviewCmd.WindowCount,
                                                                                              w => new MultiViewerState.WindowState()));
                result.SetSuccess(new[] { $"Info.MultiViewers", $"Settings.MultiViewers" });
            }
            else if (command is MultiviewVuOpacityCommand vuOpacityCmd)
            {
                // HACK - dont see a real property anywhere
                state.Info.MultiViewers.CanChangeVuMeterOpacity =
                    state.Info.Model != ModelId.MiniPro && state.Info.Model != ModelId.MiniProISO;

                UpdaterUtil.TryForIndex(result, state.Settings.MultiViewers, (int)vuOpacityCmd.MultiviewIndex, mv =>
                {
                    mv.VuMeterOpacity = vuOpacityCmd.Opacity;
                    result.SetSuccess($"Settings.MultiViewers.{vuOpacityCmd.MultiviewIndex:D}.VuMeterOpacity");
                });
            }
            else if (command is MultiviewPropertiesGetV8Command props8Cmd)
            {
                UpdaterUtil.TryForIndex(result, state.Settings.MultiViewers, (int)props8Cmd.MultiviewIndex, mv =>
                {
                    mv.Properties.Layout = props8Cmd.Layout;
                    mv.Properties.ProgramPreviewSwapped = props8Cmd.ProgramPreviewSwapped;

                    result.SetSuccess($"Settings.MultiViewers.{props8Cmd.MultiviewIndex:D}.Properties");
                });
            }
            else if (command is MultiviewPropertiesGetCommand propsCmd)
            {
                UpdaterUtil.TryForIndex(result, state.Settings.MultiViewers, (int)propsCmd.MultiviewIndex, mv =>
                {
                    if (!Enum.TryParse(propsCmd.Layout.ToString(), true, out MultiViewLayoutV8 layout))
                    {
                        layout = 0;
                    }
                    mv.Properties.Layout = layout;
                    mv.Properties.ProgramPreviewSwapped = propsCmd.ProgramPreviewSwapped;

                    result.SetSuccess($"Settings.MultiViewers.{propsCmd.MultiviewIndex:D}.Properties");
                });
            }
            else if (command is MultiviewWindowInputGetCommand winCmd)
            {
                UpdaterUtil.TryForIndex(result, state.Settings.MultiViewers, (int)winCmd.MultiviewIndex, mv =>
                {
                    UpdaterUtil.TryForIndex(result, mv.Windows, (int)winCmd.WindowIndex, win =>
                    {
                        win.Source           = winCmd.Source;
                        win.SupportsVuMeter  = winCmd.SupportVuMeter;
                        win.SupportsSafeArea = winCmd.SupportsSafeArea;

                        result.SetSuccess($"Settings.MultiViewers.{winCmd.MultiviewIndex:D}.Windows.{winCmd.WindowIndex:D}");
                    });
                });
            }
            else if (command is MultiviewWindowVuMeterGetCommand vuMeterCmd)
            {
                UpdaterUtil.TryForIndex(result, state.Settings.MultiViewers, (int)vuMeterCmd.MultiviewIndex, mv =>
                {
                    UpdaterUtil.TryForIndex(result, mv.Windows, (int)vuMeterCmd.WindowIndex, win =>
                    {
                        win.VuMeterEnabled = vuMeterCmd.VuEnabled;
                        result.SetSuccess($"Settings.MultiViewers.{vuMeterCmd.MultiviewIndex:D}.Windows.{vuMeterCmd.WindowIndex:D}");
                    });
                });
            }
            else if (command is MultiviewWindowSafeAreaCommand safeAreaCmd)
            {
                UpdaterUtil.TryForIndex(result, state.Settings.MultiViewers, (int)safeAreaCmd.MultiviewIndex, mv =>
                {
                    UpdaterUtil.TryForIndex(result, mv.Windows, (int)safeAreaCmd.WindowIndex, win =>
                    {
                        win.SafeAreaEnabled = safeAreaCmd.SafeAreaEnabled;
                        result.SetSuccess($"Settings.MultiViewers.{safeAreaCmd.MultiviewIndex:D}.Windows.{safeAreaCmd.WindowIndex:D}.SafeAreaEnabled");
                    });
                });
            }
        }
        public static void Update(AtemState state, UpdateResultImpl result, ICommand command)
        {
            if (command is FairlightAudioMixerConfigCommand confCmd)
            {
                state.Fairlight = new FairlightAudioState
                {
                    Monitors = UpdaterUtil.CreateList(confCmd.Monitors,
                                                      i => new FairlightAudioState.MonitorOutputState()),
                };
                result.SetSuccess("Fairlight.Monitors");
            }
            else if (state.Fairlight != null)
            {
                if (command is FairlightMixerMasterGetCommand masterCmd)
                {
                    var pgmState = state.Fairlight.ProgramOut;
                    UpdaterUtil.CopyAllProperties(masterCmd, pgmState,
                                                  new[] { "EqualizerGain", "EqualizerEnabled", "MakeUpGain", "EqualizerBands" },
                                                  new[] { "Dynamics", "Equalizer", "AudioFollowVideoCrossfadeTransitionEnabled", "Levels", "Peaks" });

                    pgmState.Dynamics.MakeUpGain = masterCmd.MakeUpGain;
                    pgmState.Equalizer.Enabled   = masterCmd.EqualizerEnabled;
                    pgmState.Equalizer.Gain      = masterCmd.EqualizerGain;
                    if (masterCmd.EqualizerBands != pgmState.Equalizer.Bands.Count)
                    {
                        pgmState.Equalizer.Bands = pgmState.Equalizer.Bands.RebuildToLength(masterCmd.EqualizerBands,
                                                                                            (i) => new FairlightAudioState.EqualizerBandState());
                    }

                    result.SetSuccess(new[]
                    {
                        "Fairlight.ProgramOut",
                        "Fairlight.ProgramOut.Dynamics",
                        "Fairlight.ProgramOut.Equalizer"
                    });
                }
                else if (command is FairlightMixerMasterLimiterGetCommand masterLimCmd)
                {
                    var pgmDynamics = state.Fairlight.ProgramOut.Dynamics;
                    if (pgmDynamics.Limiter == null)
                    {
                        pgmDynamics.Limiter = new FairlightAudioState.LimiterState();
                    }

                    UpdaterUtil.CopyAllProperties(masterLimCmd, pgmDynamics.Limiter, null, new[] { "GainReductionLevel" });
                    result.SetSuccess("Fairlight.ProgramOut.Dynamics.Limiter");
                }
                else if (command is FairlightMixerMasterCompressorGetCommand masterCompCmd)
                {
                    var pgmDynamics = state.Fairlight.ProgramOut.Dynamics;
                    if (pgmDynamics.Compressor == null)
                    {
                        pgmDynamics.Compressor = new FairlightAudioState.CompressorState();
                    }

                    UpdaterUtil.CopyAllProperties(masterCompCmd, pgmDynamics.Compressor, null, new[] { "GainReductionLevel" });
                    result.SetSuccess("Fairlight.ProgramOut.Dynamics.Compressor");
                }
                else if (command is FairlightMixerInputGetCommand inpCmd)
                {
                    if (!state.Fairlight.Inputs.TryGetValue((long)inpCmd.Index, out var inputState))
                    {
                        inputState = state.Fairlight.Inputs[(long)inpCmd.Index] = new FairlightAudioState.InputState();
                    }

                    if (inpCmd.SupportsRcaToXlr)
                    {
                        inputState.Analog = new FairlightAudioState.AnalogState
                        {
                            SupportedInputLevel =
                                FairlightAnalogInputLevel.ConsumerLine | FairlightAnalogInputLevel.ProLine,
                            InputLevel = inpCmd.RcaToXlrEnabled
                                ? FairlightAnalogInputLevel.ConsumerLine
                                : FairlightAnalogInputLevel.ProLine
                        };
                    }

                    UpdaterUtil.CopyAllProperties(inpCmd, inputState, new[] { "Index", "SupportsRcaToXlr", "RcaToXlrEnabled" },
                                                  new[] { "Sources", "Analog", "Xlr" });
                    result.SetSuccess(new[]
                    {
                        $"Fairlight.Inputs.{inpCmd.Index:D}.ExternalPortType",
                        $"Fairlight.Inputs.{inpCmd.Index:D}.ActiveConfiguration",
                        $"Fairlight.Inputs.{inpCmd.Index:D}.Analog"
                    });
                }
                else if (command is FairlightMixerInputGetV811Command inp811Cmd)
                {
                    if (!state.Fairlight.Inputs.TryGetValue((long)inp811Cmd.Index, out var inputState))
                    {
                        inputState = state.Fairlight.Inputs[(long)inp811Cmd.Index] = new FairlightAudioState.InputState();
                    }

                    UpdaterUtil.CopyAllProperties(inp811Cmd, inputState, new[] { "Index", "SupportedInputLevels", "ActiveInputLevel" },
                                                  new[] { "Sources", "Analog", "Xlr" });

                    if (inp811Cmd.SupportedInputLevels != 0)
                    {
                        inputState.Analog = new FairlightAudioState.AnalogState
                        {
                            SupportedInputLevel = inp811Cmd.SupportedInputLevels,
                            InputLevel          = inp811Cmd.ActiveInputLevel
                        };
                    }

                    result.SetSuccess(new[]
                    {
                        $"Fairlight.Inputs.{inp811Cmd.Index:D}.ExternalPortType",
                        $"Fairlight.Inputs.{inp811Cmd.Index:D}.ActiveConfiguration",
                        $"Fairlight.Inputs.{inp811Cmd.Index:D}.Analog"
                    });
                }
                else if (command is FairlightMixerSourceGetCommand srcCmd)
                {
                    UpdaterUtil.TryForKey(result, state.Fairlight.Inputs, (long)srcCmd.Index, inputState =>
                    {
                        FairlightAudioState.InputSourceState srcState = inputState.Sources.FirstOrDefault(s => s.SourceId == srcCmd.SourceId);
                        if (srcState == null)
                        {
                            srcState = new FairlightAudioState.InputSourceState();
                            inputState.Sources.Add(srcState);
                        }

                        srcState.Dynamics.MakeUpGain = srcCmd.MakeUpGain;
                        srcState.Equalizer.Enabled   = srcCmd.EqualizerEnabled;
                        srcState.Equalizer.Gain      = srcCmd.EqualizerGain;
                        if (srcCmd.EqualizerBands != srcState.Equalizer.Bands.Count)
                        {
                            srcState.Equalizer.Bands = srcState.Equalizer.Bands.RebuildToLength(srcCmd.EqualizerBands,
                                                                                                (i) => new FairlightAudioState.EqualizerBandState());
                        }

                        UpdaterUtil.CopyAllProperties(srcCmd, srcState,
                                                      new[] { "Index", "EqualizerBands", "EqualizerEnabled", "EqualizerGain", "MakeUpGain" },
                                                      new[] { "Dynamics", "Equalizer", "Levels" });
                        result.SetSuccess($"Fairlight.Inputs.{srcCmd.Index:D}.Sources.{srcCmd.SourceId:D}");
                    });
                }
                else if (command is FairlightMixerSourceDeleteCommand delCmd)
                {
                    UpdaterUtil.TryForKey(result, state.Fairlight.Inputs, (long)delCmd.Index, inputState =>
                    {
                        inputState.Sources.RemoveAll(src => src.SourceId == delCmd.SourceId);
                        result.SetSuccess($"Fairlight.Inputs.{delCmd.Index:D}.Sources.{delCmd.SourceId:D}");
                    });
                }
                else if (command is FairlightMixerSourceCompressorGetCommand compCmd)
                {
                    UpdaterUtil.TryForKey(result, state.Fairlight.Inputs, (long)compCmd.Index, inputState =>
                    {
                        FairlightAudioState.InputSourceState srcState = inputState.Sources.FirstOrDefault(s => s.SourceId == compCmd.SourceId);
                        if (srcState != null)
                        {
                            if (srcState.Dynamics.Compressor == null)
                            {
                                srcState.Dynamics.Compressor = new FairlightAudioState.CompressorState();
                            }

                            UpdaterUtil.CopyAllProperties(compCmd, srcState.Dynamics.Compressor, new[] { "Index", "SourceId" }, new[] { "GainReductionLevel" });
                            result.SetSuccess($"Fairlight.Inputs.{compCmd.Index:D}.Sources.{compCmd.SourceId:D}.Dynamics.Compressor");
                        }
                    });
                }
                else if (command is FairlightMixerSourceLimiterGetCommand limCmd)
                {
                    UpdaterUtil.TryForKey(result, state.Fairlight.Inputs, (long)limCmd.Index, inputState =>
                    {
                        FairlightAudioState.InputSourceState srcState = inputState.Sources.FirstOrDefault(s => s.SourceId == limCmd.SourceId);
                        if (srcState != null)
                        {
                            if (srcState.Dynamics.Limiter == null)
                            {
                                srcState.Dynamics.Limiter = new FairlightAudioState.LimiterState();
                            }

                            UpdaterUtil.CopyAllProperties(limCmd, srcState.Dynamics.Limiter, new[] { "Index", "SourceId" }, new[] { "GainReductionLevel" });
                            result.SetSuccess($"Fairlight.Inputs.{limCmd.Index:D}.Sources.{limCmd.SourceId:D}.Dynamics.Limiter");
                        }
                    });
                }
                else if (command is FairlightMixerSourceExpanderGetCommand expandCmd)
                {
                    UpdaterUtil.TryForKey(result, state.Fairlight.Inputs, (long)expandCmd.Index, inputState =>
                    {
                        FairlightAudioState.InputSourceState srcState = inputState.Sources.FirstOrDefault(s => s.SourceId == expandCmd.SourceId);
                        if (srcState != null)
                        {
                            if (srcState.Dynamics.Expander == null)
                            {
                                srcState.Dynamics.Expander = new FairlightAudioState.ExpanderState();
                            }

                            UpdaterUtil.CopyAllProperties(expandCmd, srcState.Dynamics.Expander, new[] { "Index", "SourceId" }, new[] { "GainReductionLevel" });
                            result.SetSuccess($"Fairlight.Inputs.{expandCmd.Index:D}.Sources.{expandCmd.SourceId:D}.Dynamics.Expander");
                        }
                    });
                }
                else if (command is FairlightMixerAnalogAudioGetCommand analogCmd)
                {
                    UpdaterUtil.TryForKey(result, state.Fairlight.Inputs, (long)analogCmd.Index, inputState =>
                    {
                        if (inputState.Analog == null)
                        {
                            inputState.Analog = new FairlightAudioState.AnalogState();
                        }

                        UpdaterUtil.CopyAllProperties(analogCmd, inputState.Analog, new[] { "Index" });
                        result.SetSuccess($"Fairlight.Inputs.{analogCmd.Index:D}.Analog");
                    });
                }
                else if (command is FairlightMixerMonitorGetCommand monCmd)
                {
                    UpdaterUtil.TryForIndex(result, state.Fairlight.Monitors, 0, monState =>
                    {
                        UpdaterUtil.CopyAllProperties(monCmd, monState, new[] { "Index" });
                        result.SetSuccess($"Fairlight.Monitors.{0:D}");
                    });
                }
                else if (command is FairlightMixerMasterPropertiesGetCommand master2Cmd)
                {
                    state.Fairlight.ProgramOut.AudioFollowVideoCrossfadeTransitionEnabled =
                        master2Cmd.AudioFollowVideoCrossfadeTransitionEnabled;
                    result.SetSuccess($"Fairlight.ProgramOut");
                }
                else if (command is FairlightMixerMasterLevelsCommand pgmLevelCmd)
                {
                    FairlightAudioState.ProgramOutState pgmOutState = state.Fairlight.ProgramOut;
                    pgmOutState.Levels = new FairlightAudioState.LevelsState
                    {
                        Levels = new[] { pgmLevelCmd.LeftLevel, pgmLevelCmd.RightLevel },
                        Peaks  = new[] { pgmLevelCmd.LeftPeak, pgmLevelCmd.RightPeak },

                        DynamicsInputLevels  = new[] { pgmLevelCmd.InputLeftLevel, pgmLevelCmd.InputRightLevel },
                        DynamicsInputPeaks   = new[] { pgmLevelCmd.InputLeftPeak, pgmLevelCmd.InputRightPeak },
                        DynamicsOutputLevels = new[] { pgmLevelCmd.OutputLeftLevel, pgmLevelCmd.OutputRightLevel },
                        DynamicsOutputPeaks  = new[] { pgmLevelCmd.OutputLeftPeak, pgmLevelCmd.OutputRightPeak },

                        CompressorGainReductionLevel = pgmLevelCmd.CompressorGainReduction,
                        LimiterGainReductionLevel    = pgmLevelCmd.LimiterGainReduction,
                    };

                    result.SetSuccess($"Fairlight.ProgramOut.Levels");
                }
                else if (command is FairlightMixerSourceLevelsCommand srcLevelCmd)
                {
                    UpdaterUtil.TryForKey(result, state.Fairlight.Inputs, (long)srcLevelCmd.Index, inputState =>
                    {
                        FairlightAudioState.InputSourceState srcState = inputState.Sources.FirstOrDefault(s => s.SourceId == srcLevelCmd.SourceId);
                        if (srcState != null)
                        {
                            srcState.Levels = new FairlightAudioState.LevelsState
                            {
                                Levels = new[] { srcLevelCmd.LeftLevel, srcLevelCmd.RightLevel },
                                Peaks  = new[] { srcLevelCmd.LeftPeak, srcLevelCmd.RightPeak },

                                DynamicsInputLevels  = new[] { srcLevelCmd.InputLeftLevel, srcLevelCmd.InputRightLevel },
                                DynamicsInputPeaks   = new[] { srcLevelCmd.InputLeftPeak, srcLevelCmd.InputRightPeak },
                                DynamicsOutputLevels = new[] { srcLevelCmd.OutputLeftLevel, srcLevelCmd.OutputRightLevel },
                                DynamicsOutputPeaks  = new[] { srcLevelCmd.OutputLeftPeak, srcLevelCmd.OutputRightPeak },

                                CompressorGainReductionLevel = srcLevelCmd.CompressorGainReduction,
                                LimiterGainReductionLevel    = srcLevelCmd.LimiterGainReduction,
                                ExpanderGainReductionLevel   = srcLevelCmd.ExpanderGainReduction,
                            };

                            result.SetSuccess($"Fairlight.Inputs.{srcLevelCmd.Index:D}.Sources.{srcLevelCmd.SourceId:D}.Levels");
                        }
                    });
                }
                else if (command is FairlightMixerTallyCommand tallyCmd)
                {
                    state.Fairlight.Tally = tallyCmd.Tally;
                    result.SetSuccess("Fairlight.Tally");
                }
                else if (command is FairlightMixerSourceEqualizerBandGetCommand srcBandCmd)
                {
                    UpdaterUtil.TryForKey(result, state.Fairlight.Inputs, (long)srcBandCmd.Index, inputState =>
                    {
                        FairlightAudioState.InputSourceState srcState =
                            inputState.Sources.FirstOrDefault(s => s.SourceId == srcBandCmd.SourceId);
                        if (srcState != null)
                        {
                            UpdaterUtil.TryForIndex(result, srcState.Equalizer.Bands, (int)srcBandCmd.Band, band =>
                            {
                                UpdaterUtil.CopyAllProperties(srcBandCmd, band, new[] { "Index", "SourceId", "Band" });
                                result.SetSuccess($"Fairlight.Inputs.{srcBandCmd.Index:D}.Sources.{srcBandCmd.SourceId:D}.Equalizer.Bands.{srcBandCmd.Band:D}");
                            });
                        }
                    });
                }
                else if (command is FairlightMixerMasterEqualizerBandGetCommand pgmBandCmd)
                {
                    UpdaterUtil.TryForIndex(result, state.Fairlight.ProgramOut.Equalizer.Bands, (int)pgmBandCmd.Band, band =>
                    {
                        UpdaterUtil.CopyAllProperties(pgmBandCmd, band, new[] { "Band" });
                        result.SetSuccess($"Fairlight.ProgramOut.Equalizer.Bands.{pgmBandCmd.Band:D}");
                    });
                }
            }
        }