using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Vanara.Collections;
using static Vanara.PInvoke.Ole32;
using static Vanara.PInvoke.PropSys;
namespace Vanara.Windows.Shell;
///
/// A dictionary of properties that can be used to set or update property values on Shell items via the method. This class wraps the COM interface.
///
///
///
public class ShellItemPropertyUpdates : IDictionary, IDisposable
{
private IPropertyChangeArray changes;
/// Initializes a new instance of the class.
public ShellItemPropertyUpdates() => PSCreatePropertyChangeArray(null, null, null, 0, typeof(IPropertyChangeArray).GUID, out changes).ThrowIfFailed();
/// Gets the number of elements contained in the .
public int Count => (int)changes.GetCount();
/// Gets the COM interface for .
/// The value.
public IPropertyChangeArray IPropertyChangeArray => changes;
/// Gets an containing the keys of the .
public ICollection Keys
{
get
{
var l = new List(Count);
for (uint i = 0; i < Count; i++)
{
using var p = new ComReleaser(changes.GetAt(i));
l.Add(p.Item.GetPropertyKey());
}
return l;
}
}
/// Gets an containing the values in the .
public ICollection Values
{
get
{
var l = new List(Count);
for (int i = 0; i < Count; i++)
l.Add(this[i].Value);
return l;
}
}
/// Gets a value indicating whether the is read-only.
bool ICollection>.IsReadOnly => false;
/// Gets or sets the with the specified key.
/// The .
/// The key.
/// The object associated with .
/// key
public object? this[PROPERTYKEY key]
{
get => TryGetValue(key, out var value) ? value : throw new ArgumentOutOfRangeException(nameof(key));
set => changes.AppendOrReplace(ToPC(key, value));
}
internal KeyValuePair this[int index]
{
get
{
using var p = new ComReleaser(changes.GetAt((uint)index));
p.Item.ApplyToPropVariant(new PROPVARIANT(), out var pv);
return new KeyValuePair(p.Item.GetPropertyKey(), pv.Value);
}
}
/// Adds an element with the provided key and value to the .
/// The object to use as the key of the element to add.
/// The object to use as the value of the element to add.
public void Add(PROPERTYKEY key, object? value) => changes.Append(ToPC(key, value));
/// Removes all items from the .
public void Clear()
{
for (uint i = (uint)Count - 1; i >= 0; i--)
changes.RemoveAt(i);
}
/// Determines whether the contains an element with the specified key.
/// The key to locate in the .
/// true if the contains an element with the key; otherwise, false.
public bool ContainsKey(PROPERTYKEY key) => changes.IsKeyInArray(key).Succeeded;
/// Returns an enumerator that iterates through the collection.
/// A that can be used to iterate through the collection.
public IEnumerator> GetEnumerator() =>
new IEnumFromIndexer>(changes.GetCount, i => this[(int)i]).GetEnumerator();
/// Removes the element with the specified key from the .
/// The key of the element to remove.
///
/// true if the element is successfully removed; otherwise, false. This method also returns false if was not
/// found in the original .
///
public bool Remove(PROPERTYKEY key)
{
var idx = IndexOf(key);
if (idx == -1) return false;
try { changes.RemoveAt((uint)idx); return true; } catch { return false; }
}
/// Gets the value associated with the specified key.
/// The key whose value to get.
///
/// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the
/// type of the parameter. This parameter is passed uninitialized.
///
///
/// true if the object that implements contains an element with the specified key;
/// otherwise, false.
///
public bool TryGetValue(PROPERTYKEY key, [NotNullWhen(true)] out object? value)
{
value = null;
var idx = IndexOf(key);
if (idx == -1) return false;
try { value = this[idx]; return true; } catch { return false; }
}
void ICollection>.Add(KeyValuePair item) =>
Add(item.Key, item.Value);
bool ICollection>.Contains(KeyValuePair item) =>
ContainsKey(item.Key) && this[item.Key] == item.Value;
void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex)
{
if (array == null) throw new ArgumentNullException(nameof(array));
if (arrayIndex + Count > array.Length) throw new ArgumentOutOfRangeException(nameof(arrayIndex));
for (int i = 0; i < Count; i++)
array[i + arrayIndex] = this[i];
}
void IDisposable.Dispose() { }
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
bool ICollection>.Remove(KeyValuePair item)
{
var idx = IndexOf(item.Key);
if (idx == -1) return false;
if (this[idx].Value != item.Value) return false;
try { changes.RemoveAt((uint)idx); return true; } catch { return false; }
}
private int IndexOf(PROPERTYKEY key)
{
for (uint i = 0; i < Count; i++)
{
using var p = new ComReleaser(changes.GetAt(i));
if (key == p.Item.GetPropertyKey())
return (int)i;
}
return -1;
}
private IPropertyChange ToPC(PROPERTYKEY key, object? value, PKA_FLAGS flags = PKA_FLAGS.PKA_SET)
{
PSCreateSimplePropertyChange(flags, key, new PROPVARIANT(value), typeof(IPropertyChange).GUID, out var pc).ThrowIfFailed();
return pc;
}
}