using PleaseIgnore.IntelMap.Properties;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
namespace PleaseIgnore.IntelMap {
///
/// Manages the list of watched by an instance
/// of .
///
///
[DefaultEvent("IntelReported")]
public class IntelChannelContainer : Component, IContainer, INestedContainer,
INotifyPropertyChanged {
// Default value of the ChannelUpdateInterval property
internal const string defaultUpdateInterval = "24:00:00";
// Default value of the RetryInterval property
internal const string defaultRetryInterval = "00:15:00";
// Thread Synchronization object
private readonly object syncRoot = new object();
// List of active IntelChannel objects
private readonly List channels
= new List();
// Timer object used to fetch updated channel lists
private readonly Timer updateTimer;
// The current channel processing state
[ContractPublicPropertyName("Status")]
private volatile IntelStatus status;
// The update period for the channel list
[ContractPublicPropertyName("ChannelUpdateInterval")]
private TimeSpan? updateInterval = TimeSpan.Parse(
defaultUpdateInterval,
CultureInfo.InvariantCulture);
// The network retry period for the channel list
[ContractPublicPropertyName("RetryInterval")]
private TimeSpan retryInterval = TimeSpan.Parse(
defaultRetryInterval,
CultureInfo.InvariantCulture);
// The intel upload count
[ContractPublicPropertyName("IntelCount")]
private int uploadCount;
// URI to use when fetching the channel list
[ContractPublicPropertyName("ChannelListUri")]
private Uri channelListUri = IntelExtensions.ChannelsUrl;
// Directory to use when overriding the IntelChannel's Path
[ContractPublicPropertyName("Path")]
public string logDirectory;
// The contents of the channel list the last time we fetched it
private string[] channelList;
///
/// Initializes a new instance of the
/// class.
///
public IntelChannelContainer() : this(null) {
Contract.Ensures(Status == IntelStatus.Stopped);
Contract.Ensures(Container == null);
}
///
/// Initializes a new instance of the
/// class and adds it to the specified container.
///
public IntelChannelContainer(IContainer container) {
Contract.Ensures(Status == IntelStatus.Stopped);
Contract.Ensures(Container == container);
this.updateTimer = new Timer(this.timer_Callback);
if (container != null) {
container.Add(this);
}
}
///
/// Occurs when a new log entry has been read from the chat logs.
///
public event EventHandler IntelReported;
///
/// Occurs when a property value changes.
///
public event PropertyChangedEventHandler PropertyChanged;
///
/// Gets the current operational status of the
/// object and its children.
///
public IntelStatus Status {
get { return this.status; }
private set {
Contract.Ensures(Status == value);
if (this.status != value) {
this.status = value;
this.OnPropertyChanged(new PropertyChangedEventArgs("Status"));
}
}
}
///
/// Gets a value indicating whether the
/// is currently running and watching for log entries.
///
public bool IsRunning { get { return this.status.IsRunning(); } }
///
/// Gets an instance of
///
///
/// Calling on an
/// will remove it from
/// . It may be readded when the
/// channel list is redownloaded.
///
public IntelChannelCollection Channels {
get {
lock (this.syncRoot) {
return new IntelChannelCollection(this.channels.ToArray());
}
}
}
///
/// Gets or sets the time between downloads of the intel
/// channel list.
///
///
/// The time between downloads of the intel channel list or
/// to disable periodic downloads of
/// the channel list.
///
[DefaultValue(typeof(TimeSpan), defaultUpdateInterval)]
public TimeSpan? ChannelUpdateInterval {
get {
Contract.Ensures(!Contract.Result().HasValue
|| (Contract.Result() > TimeSpan.Zero));
return this.updateInterval;
}
set {
Contract.Requires(!IsRunning);
Contract.Requires(
!value.HasValue || (value > TimeSpan.Zero),
"value");
Contract.Ensures(ChannelUpdateInterval == value);
if (this.updateInterval != value) {
this.updateInterval = value;
this.OnPropertyChanged(new PropertyChangedEventArgs("ChannelUpdatePeriod"));
}
}
}
///
/// Gets or sets the time to wait after a failed attempt to
/// download the channel list before trying again.
///
[DefaultValue(typeof(TimeSpan), defaultRetryInterval)]
public TimeSpan RetryInterval {
get {
Contract.Ensures(Contract.Result() > TimeSpan.Zero);
return this.retryInterval;
}
set {
Contract.Requires(!IsRunning);
Contract.Requires(
value > TimeSpan.Zero,
"value");
Contract.Ensures(RetryInterval == value);
if (this.retryInterval != value) {
this.retryInterval = value;
this.OnPropertyChanged(new PropertyChangedEventArgs("RetryInterval"));
}
}
}
///
/// Gets or sets the to use when downloading
/// the channel list.
///
[AmbientValue((string)null)]
public string ChannelListUri {
get {
Contract.Ensures(!String.IsNullOrEmpty(Contract.Result()));
return (this.channelListUri ?? IntelExtensions.ChannelsUrl).OriginalString;
}
set {
Contract.Requires(!IsRunning);
var uri = (value != null) ? new Uri(value) : IntelExtensions.ChannelsUrl;
if (!uri.IsAbsoluteUri) {
// TODO: Proper exception
throw new ArgumentException();
}
if (this.channelListUri != uri) {
this.channelListUri = uri;
this.OnPropertyChanged(new PropertyChangedEventArgs("ChannelListUri"));
}
}
}
///
/// Gets or sets the directory to search for log files.
///
[DefaultValue((string)null)]
public string Path {
get { return this.logDirectory; }
set {
lock (this.syncRoot) {
if (value != this.logDirectory) {
this.logDirectory = value;
this.channels.ForEach(x => x.Path = (value ?? IntelChannel.DefaultPath));
this.OnPropertyChanged(new PropertyChangedEventArgs("Path"));
}
}
}
}
///
/// Gets an object that can be used for synchronization of
/// the component state.
///
///
/// An that can be used by classes
/// derived from to
/// synchronize their own internal state.
///
protected object SyncRoot {
get {
Contract.Ensures(Contract.Result