using System.Runtime.InteropServices;
using toff_t = System.IntPtr;
using tsize_t = System.IntPtr;
using thandle_t = System.IntPtr;
using tdata_t = System.IntPtr;

namespace NAPS2.Images.Gtk;

internal class LibTiffStreamClient
{
    private readonly Stream _stream;
    private readonly LibTiff.TIFFErrorHandler _error;
    private readonly LibTiff.TIFFErrorHandler _warning;
    private readonly LibTiff.TIFFReadWriteProc _read;
    private readonly LibTiff.TIFFReadWriteProc _write;
    private readonly LibTiff.TIFFSeekProc _seek;
    private readonly LibTiff.TIFFCloseProc _close;
    private readonly LibTiff.TIFFSizeProc _size;
    private readonly LibTiff.TIFFMapFileProc _map;
    private readonly LibTiff.TIFFUnmapFileProc _unmap;

    public LibTiffStreamClient(Stream stream)
    {
        _stream = stream;
        // We need to keep explicit references to the delegates to avoid garbage collection
        _error = Error;
        _warning = Warning;
        _read = Read;
        _write = Write;
        _seek = Seek;
        _close = Close;
        _size = Size;
        _map = Map;
        _unmap = UnMap;
    }

    public IntPtr TIFFClientOpen(string mode)
    {
        LibTiff.TIFFSetErrorHandler(_error);
        LibTiff.TIFFSetWarningHandler(_warning);
        return LibTiff.TIFFClientOpen("placeholder", mode, IntPtr.Zero,
            _read, _write, _seek, _close, _size, _map, _unmap);
    }

    private void Warning(string x, string y, IntPtr va_args)
    {
    }

    private void Error(string x, string y, IntPtr va_args)
    {
    }

    public tsize_t Read(thandle_t clientdata, tdata_t data, tsize_t size)
    {
        var buffer = new byte[(int) size];
        var count = _stream.Read(buffer);
        Marshal.Copy(buffer, 0, data, count);
        return (tsize_t) count;
    }

    public tsize_t Write(thandle_t clientdata, tdata_t data, tsize_t size)
    {
        var buffer = new byte[(int) size];
        Marshal.Copy(data, buffer, 0, buffer.Length);
        _stream.Write(buffer);
        return (tsize_t) buffer.Length;
    }

    public toff_t Seek(thandle_t clientdata, toff_t off, int c)
    {
        if (c == 0)
        {
            _stream.Seek((long) off, SeekOrigin.Begin);
        }
        if (c == 1)
        {
            _stream.Seek((long) off, SeekOrigin.Current);
        }
        if (c == 2)
        {
            _stream.Seek((long) off, SeekOrigin.End);
        }
        return (toff_t) _stream.Position;
    }

    public int Close(thandle_t clientdata)
    {
        return 0;
    }

    public toff_t Size(thandle_t clientdata)
    {
        return (toff_t) _stream.Length;
    }

    public int Map(thandle_t clientdata, ref tdata_t a, ref toff_t b)
    {
        return 0;
    }

    public void UnMap(thandle_t clientdata, tdata_t a, toff_t b)
    {
    }
}