using System.Diagnostics;
using System.Reflection;
using System.Text;

namespace Shoko;

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
class ProtocolAttribute : Attribute
{
    public string Protocol;
    public ProtocolAttribute(string proto)
    {
        Protocol = proto;
    }
}

class ProtoHandler
{
    public Tab CurrentTab;
    public Uri URL;
    public string MediaType;
    public Stream Content;
    public string Status;
    public bool IsLoaded = false;
    protected long _loadedBytes = 0;
    public virtual long LoadedBytes => _loadedBytes;
    public virtual long TotalBytes => -1;
    public IEnumerable<KeyValuePair<string,IEnumerable<string>>> Headers;
    public IEnumerable<KeyValuePair<string,string>> MediaTypeParams;
    public MediaHandler Media;
    public ProtoHandler()
    {

    }
    public ProtoHandler(Uri url)
    {
        URL = url;
    }
    public virtual Task Load()
    {
        Content = new MemoryStream(Encoding.UTF8.GetBytes("error: no handler for this scheme"));
        MediaType = "text/plain";
        OnLoaded();
        return Task.CompletedTask;
    }

    public async Task<MemoryStream> Download(Stream stream, Action<long> progress)
    {
        var ret = new MemoryStream();
        var buf = new byte[81920];
        long totalBytes = 0;
        int bytesRead;
        while ((bytesRead = await stream.ReadAsync(buf, 0, buf.Length)) != 0) {
            await ret.WriteAsync(buf.AsMemory(0, bytesRead));
            totalBytes += bytesRead;
            progress(totalBytes);
        }
        ret.Position = 0;
        return ret;
    }

    public async Task<MemoryStream> Download(Stream stream)
    {
        return await Download(stream, b=>_loadedBytes=b);
    }


    public virtual void OnLoaded()
    {
        Media = MediaHandler.GetHandler(this);
        IsLoaded = true;
        Media.Load();
    }

    public virtual void Render()
    {
        Gui.Button("Open with external program", ()=>{
            try
            {
                if(OperatingSystem.IsLinux())
                    Process.Start("xdg-open", URL.ToString());
                else if(OperatingSystem.IsMacOS())
                    Process.Start("open", URL.ToString());
                else if(OperatingSystem.IsWindows())
                    Process.Start(URL.ToString());
            }
            catch{}
        });
    }

    public virtual void MenuBar()
    {
    }

    /// <summary>
    /// Get the appropriate protocol handler for the URL
    /// </summary>
    /// <param name="url"></param>
    /// <returns></returns>
    public static ProtoHandler GetHandler(Uri url)
    {
        var proto = new UriBuilder(url).Scheme;
        var type = Assembly.GetAssembly(typeof(ProtoHandler)).GetTypes()
            .Where(t => t.IsClass && !t.IsAbstract && t.IsSubclassOf(typeof(ProtoHandler)) && t.GetCustomAttributes<ProtocolAttribute>().Any(x => x.Protocol == proto))
            .SingleOrDefault(typeof(ProtoHandler));
        return (ProtoHandler)Activator.CreateInstance(type, url);
    }

    public static ProtoHandler GetHandler(string url)
    {
        return GetHandler(new Uri(url));
    }

    public static string[] SupportedProtos
    {
        get
        {
            return Assembly.GetAssembly(typeof(ProtoHandler)).GetTypes()
                .Where(t => t.IsClass && !t.IsAbstract && t.IsSubclassOf(typeof(ProtoHandler)))
                .SelectMany(t => t.GetCustomAttributes<ProtocolAttribute>().Select(x => x.Protocol))
                .ToArray();
        }
    }
}