using System.Runtime.InteropServices;

namespace NAPS2.Scan.Twain.Legacy;

internal class TwProtocol
{									// TWON_PROTOCOL...
    public const short MAJOR = 1;
    public const short MINOR = 9;
}

[Flags]
internal enum TwDG : short
{									// DG_.....
    Control = 0x0001,
    Image = 0x0002,
    Audio = 0x0004
}

internal enum TwData : short
{									// DAT_....
    Null = 0x0000,
    Capability = 0x0001,
    Event = 0x0002,
    Identity = 0x0003,
    Parent = 0x0004,
    PendingXfers = 0x0005,
    SetupMemXfer = 0x0006,
    SetupFileXfer = 0x0007,
    Status = 0x0008,
    UserInterface = 0x0009,
    XferGroup = 0x000a,
    TwunkIdentity = 0x000b,
    CustomDSData = 0x000c,
    DeviceEvent = 0x000d,
    FileSystem = 0x000e,
    PassThru = 0x000f,

    ImageInfo = 0x0101,
    ImageLayout = 0x0102,
    ImageMemXfer = 0x0103,
    ImageNativeXfer = 0x0104,
    ImageFileXfer = 0x0105,
    CieColor = 0x0106,
    GrayResponse = 0x0107,
    RGBResponse = 0x0108,
    JpegCompression = 0x0109,
    Palette8 = 0x010a,
    ExtImageInfo = 0x010b,

    SetupFileXfer2 = 0x0301
}

internal enum TwMessageCode : short
{									// MSG_.....
    Null = 0x0000,
    Get = 0x0001,
    GetCurrent = 0x0002,
    GetDefault = 0x0003,
    GetFirst = 0x0004,
    GetNext = 0x0005,
    Set = 0x0006,
    Reset = 0x0007,
    QuerySupport = 0x0008,

    XFerReady = 0x0101,
    CloseDSReq = 0x0102,
    CloseDSOK = 0x0103,
    DeviceEvent = 0x0104,

    CheckStatus = 0x0201,

    OpenDSM = 0x0301,
    CloseDSM = 0x0302,

    OpenDS = 0x0401,
    CloseDS = 0x0402,
    UserSelect = 0x0403,

    DisableDS = 0x0501,
    EnableDS = 0x0502,
    EnableDSUIOnly = 0x0503,

    ProcessEvent = 0x0601,

    EndXfer = 0x0701,
    StopFeeder = 0x0702,

    ChangeDirectory = 0x0801,
    CreateDirectory = 0x0802,
    Delete = 0x0803,
    FormatMedia = 0x0804,
    GetClose = 0x0805,
    GetFirstFile = 0x0806,
    GetInfo = 0x0807,
    GetNextFile = 0x0808,
    Rename = 0x0809,
    Copy = 0x080A,
    AutoCaptureDir = 0x080B,

    PassThru = 0x0901
}

internal enum TwReturnCode : short
{									// TWRC_....
    Success = 0x0000,
    Failure = 0x0001,
    CheckStatus = 0x0002,
    Cancel = 0x0003,
    DSEvent = 0x0004,
    NotDSEvent = 0x0005,
    XferDone = 0x0006,
    EndOfList = 0x0007,
    InfoNotSupported = 0x0008,
    DataNotAvailable = 0x0009
}

internal enum TwConditionCode : short
{									// TWCC_....
    Success = 0x0000,
    Bummer = 0x0001,
    LowMemory = 0x0002,
    NoDS = 0x0003,
    MaxConnections = 0x0004,
    OperationError = 0x0005,
    BadCap = 0x0006,
    BadProtocol = 0x0009,
    BadValue = 0x000a,
    SeqError = 0x000b,
    BadDest = 0x000c,
    CapUnsupported = 0x000d,
    CapBadOperation = 0x000e,
    CapSeqError = 0x000f,
    Denied = 0x0010,
    FileExists = 0x0011,
    FileNotFound = 0x0012,
    NotEmpty = 0x0013,
    PaperJam = 0x0014,
    PaperDoubleFeed = 0x0015,
    FileWriteError = 0x0016,
    CheckDeviceOnline = 0x0017
}

internal enum TwOn : short
{									// TWON_....
    Array = 0x0003,
    Enum = 0x0004,
    One = 0x0005,
    Range = 0x0006,
    DontCare = -1
}

internal enum TwType : short
{									// TWTY_....
    Int8 = 0x0000,
    Int16 = 0x0001,
    Int32 = 0x0002,
    UInt8 = 0x0003,
    UInt16 = 0x0004,
    UInt32 = 0x0005,
    Bool = 0x0006,
    Fix32 = 0x0007,
    Frame = 0x0008,
    Str32 = 0x0009,
    Str64 = 0x000a,
    Str128 = 0x000b,
    Str255 = 0x000c,
    Str1024 = 0x000d,
    Str512 = 0x000e
}

internal enum TwCap : short
{
    XferCount = 0x0001,			// CAP_XFERCOUNT
    ICompression = 0x0100,			// ICAP_...
    IPixelType = 0x0101,
    IUnits = 0x0102,
    IXferMech = 0x0103
}

// ------------------- STRUCTS --------------------------------------------

[StructLayout(LayoutKind.Sequential, Pack = 2, CharSet = CharSet.Ansi)]
internal class TwIdentity
{									// TW_IDENTITY
    public IntPtr Id;
    public TwVersion Version;
    public short ProtocolMajor;
    public short ProtocolMinor;
    public int SupportedGroups;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 34)]
    public string? Manufacturer;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 34)]
    public string? ProductFamily;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 34)]
    public string? ProductName;
}

[StructLayout(LayoutKind.Sequential, Pack = 2, CharSet = CharSet.Ansi)]
internal struct TwVersion
{									// TW_VERSION
    public short MajorNum;
    public short MinorNum;
    public short Language;
    public short Country;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 34)]
    public string Info;
}

[StructLayout(LayoutKind.Sequential, Pack = 2)]
internal class TwUserInterface
{									// TW_USERINTERFACE
    public short ShowUI;				// bool is strictly 32 bit, so use short
    public short ModalUI;
    public IntPtr ParentHand;
}

[StructLayout(LayoutKind.Sequential, Pack = 2)]
internal class TwStatus
{									// TW_STATUS
    public short ConditionCode;		// TwConditionCode
    public short Reserved;
}

[StructLayout(LayoutKind.Sequential, Pack = 2)]
internal struct TwEvent
{									// TW_EVENT
    public IntPtr EventPtr;
    public short Message;
}


[StructLayout(LayoutKind.Sequential, Pack = 2)]
internal class TwImageInfo
{									// TW_IMAGEINFO
    public int XResolution;
    public int YResolution;
    public int ImageWidth;
    public int ImageLength;
    public short SamplesPerPixel;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public short[]? BitsPerSample;
    public short BitsPerPixel;
    public short Planar;
    public short PixelType;
    public short Compression;
}

[StructLayout(LayoutKind.Sequential, Pack = 2)]
internal class TwPendingXfers
{									// TW_PENDINGXFERS
    public short Count;
    public int EOJ;
}

[StructLayout(LayoutKind.Sequential, Pack = 2)]
internal struct TwFix32
{												// TW_FIX32
    public short Whole;
    public ushort Frac;

    public float ToFloat()
    {
        return Whole + (Frac / 65536.0f);
    }
    public void FromFloat(float f)
    {
        var i = (int)((f * 65536.0f) + 0.5f);
        Whole = (short)(i >> 16);
        Frac = (ushort)(i & 0x0000ffff);
    }
}

[StructLayout(LayoutKind.Sequential, Pack = 2)]
internal class TwCapability
{									// TW_CAPABILITY
    public TwCapability(TwCap cap)
    {
        Cap = (short)cap;
        ConType = -1;
    }
    public TwCapability(TwCap cap, short sval)
    {
        Cap = (short)cap;
        ConType = (short)TwOn.One;
        Handle = Twain.GlobalAlloc(0x42, 6);
        IntPtr pv = Twain.GlobalLock(Handle);
        Marshal.WriteInt16(pv, 0, (short)TwType.Int16);
        Marshal.WriteInt32(pv, 2, sval);
        Twain.GlobalUnlock(Handle);
    }
    ~TwCapability()
    {
        if (Handle != IntPtr.Zero)
            Twain.GlobalFree(Handle);
    }
    public short Cap;
    public short ConType;
    public IntPtr Handle;
}