"""
This demonstrates the creation of miniversions of a file during a transaction.
The FSCTL_TXFS_CREATE_MINIVERSION control code saves any changes to a new
miniversion (effectively a savepoint within a transaction).
"""

import os
import struct

import win32api
import win32con
import win32file
import win32transaction
import winerror
import winioctlcon


def demo():
    """
    Definition of buffer used with FSCTL_TXFS_CREATE_MINIVERSION:
    typedef struct _TXFS_CREATE_MINIVERSION_INFO{
        USHORT StructureVersion;
        USHORT StructureLength;
        ULONG BaseVersion;
        USHORT MiniVersion;}
    """
    buf_fmt = "HHLH0L"  ## buffer size must include struct padding
    buf_size = struct.calcsize(buf_fmt)

    tempdir = win32api.GetTempPath()
    tempfile = win32api.GetTempFileName(tempdir, "cft")[0]
    print("Demonstrating transactions on tempfile", tempfile)
    f = open(tempfile, "w")
    f.write("This is original file.\n")
    f.close()

    trans = win32transaction.CreateTransaction(
        Description="Test creating miniversions of a file"
    )
    hfile = win32file.CreateFileW(
        tempfile,
        win32con.GENERIC_READ | win32con.GENERIC_WRITE,
        win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
        None,
        win32con.OPEN_EXISTING,
        0,
        None,
        Transaction=trans,
    )

    win32file.WriteFile(hfile, b"This is first miniversion.\n")
    buf = win32file.DeviceIoControl(
        hfile, winioctlcon.FSCTL_TXFS_CREATE_MINIVERSION, None, buf_size, None
    )
    struct_ver, struct_len, base_ver, ver_1 = struct.unpack(buf_fmt, buf)

    win32file.SetFilePointer(hfile, 0, win32con.FILE_BEGIN)
    win32file.WriteFile(hfile, b"This is second miniversion!\n")
    buf = win32file.DeviceIoControl(
        hfile, winioctlcon.FSCTL_TXFS_CREATE_MINIVERSION, None, buf_size, None
    )
    struct_ver, struct_len, base_ver, ver_2 = struct.unpack(buf_fmt, buf)
    hfile.Close()

    ## miniversions can't be opened with write access
    hfile_0 = win32file.CreateFileW(
        tempfile,
        win32con.GENERIC_READ,
        win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
        None,
        win32con.OPEN_EXISTING,
        0,
        None,
        Transaction=trans,
        MiniVersion=base_ver,
    )
    print("version:", base_ver, win32file.ReadFile(hfile_0, 100))
    hfile_0.Close()

    hfile_1 = win32file.CreateFileW(
        tempfile,
        win32con.GENERIC_READ,
        win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
        None,
        win32con.OPEN_EXISTING,
        0,
        None,
        Transaction=trans,
        MiniVersion=ver_1,
    )
    print("version:", ver_1, win32file.ReadFile(hfile_1, 100))
    hfile_1.Close()

    hfile_2 = win32file.CreateFileW(
        tempfile,
        win32con.GENERIC_READ,
        win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
        None,
        win32con.OPEN_EXISTING,
        0,
        None,
        Transaction=trans,
        MiniVersion=ver_2,
    )
    print("version:", ver_2, win32file.ReadFile(hfile_2, 100))
    hfile_2.Close()

    ## MiniVersions are destroyed when transaction is committed or rolled back
    win32transaction.CommitTransaction(trans)

    os.unlink(tempfile)


if __name__ == "__main__":
    # When run on CI, this fails with NOT_SUPPORTED, so don't have that cause "failure"
    try:
        demo()
    except win32file.error as e:
        if e.winerror == winerror.ERROR_NOT_SUPPORTED:
            print("These features are not supported by this filesystem.")
        else:
            raise
