/// <summary>
        /// Beginnt mit der Ausführung der Aufgabe.
        /// </summary>
        /// <returns>Gesetzt, wenn die Aufgabe synchron abgeschlossen wurde.</returns>
        bool IPlugInControl.Start()
        {
            // Attach to the scanning filter
            ScanningFilter filter = m_PlugIn.Profile.ScanConfiguration;

            // Wipe out current settings
            filter.ExcludedSourceGroups.Clear();

            // Process all
            foreach (ListViewItem item in lstGroups.CheckedItems)
            {
                // Attach to the group
                SourceGroup group = (SourceGroup)item.Tag;

                // Clone it
                group = SourceGroup.FromString <SourceGroup>(group.ToString());

                // Add to list
                filter.ExcludedSourceGroups.Add(group);
            }

            // Store to disk
            m_PlugIn.Profile.Save();

            // Done
            return(true);
        }
        /// <summary>
        /// Analysiert die aktuelle Konfiguration des Suchlaufs auf Basis
        /// der DVB <i>Network Information Table (NIT)</i> Informationen.
        /// </summary>
        private void AnalyserThread()
        {
            // Be safe
            try
            {
                // Configure language
                UserProfile.ApplyLanguage();

                // Uses hardware manager
                using (HardwareManager.Open())
                {
                    // Attach to the hardware itself
                    Hardware device = HardwareManager.OpenHardware(Profile);

                    // Reset counters
                    TotalLocations  = m_Locations.Count;
                    CurrentLocation = 0;

                    // Last inversion used
                    SpectrumInversions lastInversion = SpectrumInversions.On;

                    // Process all transponders
                    foreach (GroupLocation location in m_Locations)
                    {
                        // Update counter
                        ++CurrentLocation;

                        // Reset
                        CurrentLocationGroupsPending = 0;
                        CurrentLocationGroup         = 0;

                        // Check caller
                        if (!LocationStart(location))
                        {
                            return;
                        }

                        // Groups found on this location
                        Dictionary <SourceGroup, bool> found = new Dictionary <SourceGroup, bool>();

                        // Groups with valid network information - only those are considered
                        List <SourceGroup> nitAvailable = new List <SourceGroup>();

                        // Load counter
                        CurrentLocationGroupsPending = location.Groups.Count;

                        // Process all groups
                        foreach (SourceGroup group in location.Groups)
                        {
                            // Get the expected type
                            Type groupType = group.GetType();

                            // Clone the group
                            SourceGroup newGroup = SourceGroup.FromString <SourceGroup>(group.ToString());

                            // Count up
                            --CurrentLocationGroupsPending;
                            ++CurrentLocationGroup;

                            // See if this is already processed
                            if (null != found.Keys.FirstOrDefault(p => p.CompareTo(newGroup, true)))
                            {
                                continue;
                            }

                            // Check for external termination
                            if (null == m_Worker)
                            {
                                return;
                            }

                            // Not supported
                            if (!Profile.SupportsGroup(newGroup) || !device.CanHandle(newGroup))
                            {
                                // Remember
                                m_UnhandledGroups.Add(newGroup);

                                // Next
                                continue;
                            }

                            // Check caller
                            if (!GroupStart(location, newGroup))
                            {
                                return;
                            }

                            // Attach to the group
                            if (null != SelectGroup(device, location, newGroup, ref lastInversion))
                            {
                                // See if we are a cable group
                                CableGroup cableGroup = newGroup as CableGroup;

                                // Attach to the NIT
                                var nit = device.GetLocationInformation(15000);
                                if (null != nit)
                                {
                                    // Remember
                                    nitAvailable.Add(newGroup);

                                    // Process
                                    foreach (SourceGroup other in nit.Groups)
                                    {
                                        if (other.GetType() == groupType)
                                        {
                                            // See if this is a cable group
                                            CableGroup otherCable = other as CableGroup;

                                            // Update inversion
                                            if (null != otherCable)
                                            {
                                                // Other must be cable, too
                                                if (null == cableGroup)
                                                {
                                                    continue;
                                                }

                                                // Use same parameters
                                                otherCable.SpectrumInversion = cableGroup.SpectrumInversion;
                                                otherCable.Bandwidth         = cableGroup.Bandwidth;
                                            }

                                            // Report
                                            if (ScannerTraceSwitch.Level >= TraceLevel.Info)
                                            {
                                                Trace.WriteLine(string.Format(Properties.Resources.Trace_Scanner_NIT, other), ScannerTraceSwitch.DisplayName);
                                            }

                                            // Mark it
                                            found[other] = true;
                                        }
                                    }
                                }
                            }

                            // Check caller
                            if (!GroupDone(location, newGroup))
                            {
                                return;
                            }
                        }

                        // Create a brand new scan location
                        ScanLocation scan = location.ToScanLocation();

                        // Try to update
                        foreach (SourceGroup group in nitAvailable)
                        {
                            // Try to find the full qualified name
                            SourceGroup nitGroup = found.Keys.FirstOrDefault(p => p.CompareTo(group, true));

                            // Update
                            if (null == nitGroup)
                            {
                                scan.Groups.Add(group);
                            }
                            else
                            {
                                scan.Groups.Add(nitGroup);
                            }
                        }

                        // Just remember
                        m_AnalyseResult[location] = scan;

                        // Check caller
                        if (!LocationDone(location))
                        {
                            return;
                        }
                    }
                }
            }
            catch (Exception e)
            {
                // Remember
                m_ThreadException = e;
            }
        }
        /// <summary>
        /// Übernimmt das Ergebnis des Sendersuchlaufs in das Geräteprofil.
        /// </summary>
        public void UpdateProfile()
        {
            // Create a map of all current locations
            Dictionary <GroupLocation, GroupLocation> newLocations = new Dictionary <GroupLocation, GroupLocation>();

            // Fill current locations
            foreach (GroupLocation location in m_ScanResults)
            {
                newLocations[location] = location;
            }

            // All previous locations
            List <GroupLocation> oldLocations = new List <GroupLocation>();

            // Fill all which are still active
            foreach (GroupLocation location in Profile.Locations)
            {
                if (newLocations.ContainsKey(location))
                {
                    oldLocations.Add(location);
                }
            }

            // Wipe out
            Profile.Locations.Clear();

            // Reload
            foreach (GroupLocation location in m_ScanResults)
            {
                Profile.Locations.Add(location);
            }

            // All locations to process
            Dictionary <GroupLocation, bool> unprocessedLocations = newLocations.Keys.ToDictionary(l => l, l => true);

            // Process each location individually for a possible merge
            foreach (GroupLocation location in oldLocations)
            {
                // Find the related new location
                GroupLocation newLocation = newLocations[location];

                // No need to check for protocol
                unprocessedLocations.Remove(newLocation);

                // Create a map of all source groups we found
                Dictionary <SourceIdentifier, SourceIdentifier> found = new Dictionary <SourceIdentifier, SourceIdentifier>();

                // Fill it
                foreach (SourceGroup newGroup in newLocation.Groups)
                {
                    foreach (SourceIdentifier newSource in newGroup.Sources)
                    {
                        found[newSource] = newSource;
                    }
                }

                // Already processed
                Dictionary <SourceIdentifier, bool> updatedSources = new Dictionary <SourceIdentifier, bool>();

                // Now see which old sources are no longer available
                foreach (SourceGroup oldGroup in location.Groups)
                {
                    foreach (SourceIdentifier oldSource in oldGroup.Sources)
                    {
                        // Load the new source
                        if (found.ContainsKey(oldSource))
                        {
                            // Mark
                            updatedSources[oldSource] = true;

                            // Next
                            continue;
                        }

                        // At least a group must exist
                        SourceGroup newGroup = null;

                        // Find it
                        foreach (SourceGroup testGroup in newLocation.Groups)
                        {
                            if (testGroup.CompareTo(oldGroup, true))
                            {
                                // Nearly the same
                                newGroup = testGroup;

                                // Done
                                break;
                            }
                        }

                        // Must create the group
                        if (null == newGroup)
                        {
                            // Clone it
                            newGroup = SourceGroup.FromString <SourceGroup>(oldGroup.ToString());

                            // Remember it
                            newLocation.Groups.Add(newGroup);
                        }

                        // Add the missing source
                        newGroup.Sources.Add(oldSource);

                        // Remember
                        m_Protocol.Add(new ProtocolRecord {
                            Mode = ProtocolRecordMode.NotFound, Location = newLocation, Group = newGroup, Source = oldSource
                        });
                    }
                }

                // Finish
                foreach (SourceGroup newGroup in newLocation.Groups)
                {
                    foreach (SourceIdentifier newSource in newGroup.Sources)
                    {
                        if (found.ContainsKey(newSource))
                        {
                            // Check mode
                            ProtocolRecordMode mode;
                            if (updatedSources.ContainsKey(newSource))
                            {
                                mode = ProtocolRecordMode.Found;
                            }
                            else
                            {
                                mode = ProtocolRecordMode.Added;
                            }

                            // Add to protocol
                            m_Protocol.Add(new ProtocolRecord {
                                Mode = mode, Location = newLocation, Group = newGroup, Source = newSource
                            });
                        }
                    }
                }
            }

            // Add all sources to protocol
            foreach (GroupLocation newLocation in unprocessedLocations.Keys)
            {
                foreach (SourceGroup newGroup in newLocation.Groups)
                {
                    foreach (SourceIdentifier newSource in newGroup.Sources)
                    {
                        m_Protocol.Add(new ProtocolRecord {
                            Mode = ProtocolRecordMode.Added, Location = newLocation, Group = newGroup, Source = newSource
                        });
                    }
                }
            }
        }
        /// <summary>
        /// Führt den eigentlichen Sendersuchlauf aus.
        /// </summary>
        private void ScannerThread()
        {
            // Be safe
            try
            {
                // Configure language
                UserProfile.ApplyLanguage();

                // Uses hardware manager
                using (HardwareManager.Open())
                {
                    // Attach to the hardware itself
                    var device = HardwareManager.OpenHardware(Profile);

                    // Tell it that we only do a scan
                    device.PrepareSourceScan();

                    // Start loading location result list
                    foreach (var location in m_Locations)
                    {
                        m_ScanResults.Add(location.Clone());
                    }

                    // Reset counters
                    TotalLocations = m_Locations.Count;

                    // Last successfull inversion
                    var lastInversion = SpectrumInversions.On;

                    // Process all transponders
                    for (CurrentLocation = 0; CurrentLocation < m_Locations.Count;)
                    {
                        // Get new and old
                        var location    = m_Locations[CurrentLocation];
                        var newLocation = m_ScanResults[CurrentLocation];

                        // Count
                        ++CurrentLocation;

                        // Reset
                        CurrentLocationGroupsPending = 0;
                        CurrentLocationGroup         = 0;

                        // Check caller
                        if (!LocationStart(location))
                        {
                            return;
                        }

                        // Allow NIT scan on...
                        var enableNIT = new List <SourceGroup>();
                        var foundInfo = new List <SourceGroup>();

                        // Startup and load the groups from the configuration
                        foreach (SourceGroup group in location.Groups)
                        {
                            enableNIT.Add(group);
                        }

                        // All we have to process
                        var process = new List <SourceGroup>(enableNIT);

                        // Allowed group type
                        var groupType = (process.Count > 0) ? process[0].GetType() : null;

                        // All we did so far
                        var done = new HashSet <SourceGroup>();

                        // Process all groups
                        while (process.Count > 0)
                        {
                            // Take the first one
                            var group = process[0];

                            // Remove it
                            process.RemoveAt(0);

                            // Reset counter
                            CurrentLocationGroupsPending = process.Count;

                            // Count what we did
                            ++CurrentLocationGroup;

                            // Already done
                            if (done.Contains(group))
                            {
                                continue;
                            }

                            // Found group information on equivalent group
                            if (foundInfo.FirstOrDefault(g => g.CompareTo(group, true)) != null)
                            {
                                continue;
                            }

                            // Mark as done
                            done.Add(group);

                            // Check for external termination
                            if (m_Worker == null)
                            {
                                return;
                            }

                            // Not supported
                            if (!Profile.SupportsGroup(group) || !device.CanHandle(group))
                            {
                                // Remember
                                m_UnhandledGroups.Add(group);

                                // Next
                                continue;
                            }

                            // Read the configuration
                            var filter = Profile.GetFilter(group);

                            // See if this should be skiped
                            if (filter != null)
                            {
                                if (filter.ExcludeFromScan)
                                {
                                    // Remember
                                    m_GroupsExcluded.Add(group);

                                    // Next
                                    continue;
                                }
                            }

                            // Check caller
                            if (!GroupStart(location, group))
                            {
                                return;
                            }

                            // See if we should process the NIT
                            var checkNIT = enableNIT.Contains(group);

                            // See if we should process
                            if (checkNIT || group.IsComplete)
                            {
                                // Clone the group - may change
                                group = SourceGroup.FromString <SourceGroup>(group.ToString());

                                // Attach to the group
                                var info = SelectGroup(device, location, group, ref lastInversion);

                                // Mark as done again - group may be updated for DVB-C and if unchanged this is simply a no-operation
                                done.Add(group);

                                // Read the configuration again - actually for DVB-C it may change
                                filter = Profile.GetFilter(group);

                                // See if this should be skiped
                                if (filter != null)
                                {
                                    if (filter.ExcludeFromScan)
                                    {
                                        // Remember
                                        m_GroupsExcluded.Add(group);

                                        // Next
                                        continue;
                                    }
                                }

                                // Remember that we found a group information - transponder is not dead
                                if (info != null)
                                {
                                    foundInfo.Add(group);
                                }

                                // See if NIT update is allowed
                                if (checkNIT)
                                {
                                    if (info != null)
                                    {
                                        // See if we are a cable group
                                        var cableGroup = group as CableGroup;

                                        // Try load
                                        var nit = device.GetLocationInformation(15000);
                                        if (nit != null)
                                        {
                                            foreach (SourceGroup other in nit.Groups)
                                            {
                                                if (other.GetType() == groupType)
                                                {
                                                    // See if this is a cable group
                                                    var otherCable = other as CableGroup;

                                                    // Disable NIT scan on the group as is
                                                    enableNIT.RemoveAll(g => g.CompareTo(other, true));
                                                    process.RemoveAll(g => g.CompareTo(other, true));

                                                    // Set inversion
                                                    if (otherCable != null)
                                                    {
                                                        if (cableGroup != null)
                                                        {
                                                            // Use same parameters
                                                            otherCable.SpectrumInversion = cableGroup.SpectrumInversion;
                                                            otherCable.Bandwidth         = cableGroup.Bandwidth;

                                                            // Disable NIT scan on the group which may be DVB-C corrected
                                                            enableNIT.RemoveAll(g => g.CompareTo(otherCable, true));
                                                            process.RemoveAll(g => g.CompareTo(otherCable, true));
                                                        }
                                                    }

                                                    // Report
                                                    if (ScannerTraceSwitch.Level >= TraceLevel.Info)
                                                    {
                                                        Trace.WriteLine(string.Format(Properties.Resources.Trace_Scanner_NIT, other), ScannerTraceSwitch.DisplayName);
                                                    }

                                                    // Add for processing
                                                    process.Insert(0, other);
                                                }
                                            }
                                        }
                                    }
                                }

                                // Reset counter - may be updated if a new NIT was processed
                                CurrentLocationGroupsPending = process.Count;

                                // Skip if a legacy template group
                                if (group.IsComplete)
                                {
                                    // Merge
                                    newLocation.Groups.Add(group);

                                    // Process this group
                                    if (info == null)
                                    {
                                        m_Protocol.Add(new ProtocolRecord {
                                            Mode = ProtocolRecordMode.EmptyGroup, Location = location, Group = group
                                        });
                                    }
                                    else
                                    {
                                        foreach (var source in info.Sources)
                                        {
                                            // Only stations
                                            var station = source as Station;
                                            if (station == null)
                                            {
                                                continue;
                                            }

                                            // Attach to the filter
                                            var modifiers = Profile.GetFilter(source);
                                            if (modifiers != null)
                                            {
                                                if (modifiers.ExcludeFromScan)
                                                {
                                                    continue;
                                                }
                                                else
                                                {
                                                    modifiers.ApplyTo(station);
                                                }
                                            }

                                            // Remember
                                            group.Sources.Add(station);

                                            // Get the count
                                            int foundOfType;
                                            if (!SourcesFound.TryGetValue(station.SourceType, out foundOfType))
                                            {
                                                foundOfType = 0;
                                            }

                                            // Update
                                            SourcesFound[station.SourceType] = ++foundOfType;

                                            // Ask user
                                            if (!StationFound(location, group, station))
                                            {
                                                return;
                                            }
                                        }
                                    }
                                }
                            }

                            // Check caller
                            if (!GroupDone(location, group))
                            {
                                return;
                            }
                        }

                        // Check caller
                        if (!LocationDone(location))
                        {
                            return;
                        }
                    }
                }
            }
            catch (Exception e)
            {
                // Remember
                m_ThreadException = e;
            }
        }