using System;
using System.IO;
using System.IO.Compression;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;

class Test
{
    IDisposable d;
    IDisposable dProp { get; set; }
    IDisposable this[int i] { get => null; set { } }

    public IDisposable Method()
    {
        // GOOD: Disposed automatically.
        using (var c1 = new Timer(TimerProc))
        {
        }

        // GOOD: Dispose called in finally
        Timer c1a = null;
        try
        {
            c1a = new Timer(TimerProc);
        }
        finally
        {
            if (c1a != null)
            {
                c1a.Dispose();
            }
        }

        // GOOD: Close called in finally
        FileStream c1b = null;
        try
        {
            c1b = new FileStream("", FileMode.CreateNew, FileAccess.Write);
        }
        finally
        {
            if (c1b != null)
            {
                c1b.Close();
            }
        }

        // BAD: No Dispose call
        var c1d = new Timer(TimerProc); // $ Alert
        var fs = new FileStream("", FileMode.CreateNew, FileAccess.Write); // $ Alert
        new FileStream("", FileMode.CreateNew, FileAccess.Write).Fluent(); // $ Alert

        // GOOD: Disposed via wrapper
        fs = new FileStream("", FileMode.CreateNew, FileAccess.Write);
        var z = new GZipStream(fs, CompressionMode.Compress);
        z.Close();

        // GOOD: Escapes
        d = new Timer(TimerProc);
        if (d == null)
            return new Timer(TimerProc);
        fs = new FileStream("", FileMode.CreateNew, FileAccess.Write);
        d = new GZipStream(fs, CompressionMode.Compress);
        dProp = new Timer(TimerProc);
        this[0] = new Timer(TimerProc);
        d = new FileStream("", FileMode.CreateNew, FileAccess.Write).Fluent();

        // GOOD: Passed to another IDisposable
        using (var reader = new StreamReader(new FileStream("", FileMode.Open)))
            ;

        // GOOD: XmlDocument.Load disposes incoming XmlReader (False positive as this is disposed in library code)
        var xmlReader = XmlReader.Create(new StringReader("xml"), null); // $ Alert
        var xmlDoc = new XmlDocument();
        xmlDoc.Load(xmlReader);

        // GOOD: Disposed automatically.
        using var c2 = new Timer(TimerProc);

        // GOOD: ownership taken via ??
        StringReader source = null;
        using (XmlReader.Create(source ?? new StringReader("xml"), null))
            ;

        // GOOD: Flagging these generates too much noise and there is a general
        // acceptance that Tasks are not disposed.
        // https://devblogs.microsoft.com/pfxteam/do-i-need-to-dispose-of-tasks/
        Task t = new Task(() => { });
        t.Start();
        t.Wait();

        return null;
    }

    public void M(IHttpClientFactory factory)
    {
        // GOOD: Factory tracks and disposes.
        HttpClient client1 = factory.CreateClient();

        // BAD: No Dispose call
        var client2 = new HttpClient(); // $ Alert
    }

    // GOOD: Escapes
    IDisposable Create() => new Timer(TimerProc);

    void TimerProc(object obj)
    {
    }

    public void Dispose() { }
}

static class Extensions
{
    public static FileStream Fluent(this FileStream fs) => fs;
}
