/*
 * Tape handling functions
 *
 * Copyright 1999 Chris Morgan <cmorgan@wpi.edu>
 *                James Abbatiello <abbeyj@wpi.edu>
 * Copyright 2006 Hans Leidekker 
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <stdarg.h>

#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "windef.h"
#include "winternl.h"
#include "winbase.h"
#include "winerror.h"
#include "winioctl.h"
#include "ddk/ntddtape.h"

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(tape);

static DWORD set_error_from_status( NTSTATUS status )
{
    DWORD error = RtlNtStatusToDosError( status );

    SetLastError( error );
    return error;
}

/************************************************************************
 *		BackupRead  (KERNEL32.@)
 *
 * Backup a file or directory.
 */
BOOL WINAPI BackupRead( HANDLE file, LPBYTE buffer, DWORD to_read,
    LPDWORD read, BOOL abort, BOOL security, LPVOID *context )
{
    FIXME( "(%p, %p, %d, %p, %d, %d, %p)\n", file, buffer,
           to_read, read, abort, security, context );
    SetLastError( ERROR_NOT_SUPPORTED );
    return FALSE;
}


/************************************************************************
 *		BackupSeek  (KERNEL32.@)
 *
 * Seek forward in a backup stream.
 */
BOOL WINAPI BackupSeek( HANDLE file, DWORD seek_low, DWORD seek_high,
    LPDWORD seeked_low, LPDWORD seeked_high, LPVOID *context )
{
    FIXME( "(%p, %d, %d, %p, %p, %p)\n", file, seek_low,
           seek_high, seeked_low, seeked_high, context );
    SetLastError( ERROR_NOT_SUPPORTED );
    return FALSE;
}


/************************************************************************
 *		BackupWrite  (KERNEL32.@)
 *
 * Restore a file or directory.
 */
BOOL WINAPI BackupWrite( HANDLE file, LPBYTE buffer, DWORD to_write,
    LPDWORD written, BOOL abort, BOOL security, LPVOID *context )
{
    FIXME( "(%p, %p, %d, %p, %d, %d, %p)\n", file, buffer,
           to_write, written, abort, security, context );
    SetLastError( ERROR_NOT_SUPPORTED );
    return FALSE;
}


/************************************************************************
 *		CreateTapePartition  (KERNEL32.@)
 *
 * Format a tape.
 */
DWORD WINAPI CreateTapePartition( HANDLE device, DWORD method,
    DWORD count, DWORD size )
{
    NTSTATUS status;
    TAPE_CREATE_PARTITION part;
    IO_STATUS_BLOCK io;

    TRACE( "(%p, %d, %d, %d)\n", device, method, count, size );

    part.Method = method;
    part.Count = count;
    part.Size = size;

    status = NtDeviceIoControlFile( device, NULL, NULL, NULL, &io,
        IOCTL_TAPE_CREATE_PARTITION, &part, sizeof(TAPE_CREATE_PARTITION), NULL, 0 );

    return set_error_from_status( status );
}


/************************************************************************
 *		EraseTape  (KERNEL32.@)
 *
 * Erase a tape.
 */
DWORD WINAPI EraseTape( HANDLE device, DWORD type, BOOL immediate )
{
    NTSTATUS status;
    TAPE_ERASE erase;
    IO_STATUS_BLOCK io;

    TRACE( "(%p, %d, %d)\n", device, type, immediate );

    erase.Type = type;
    erase.Immediate = immediate;
            
    status = NtDeviceIoControlFile( device, NULL, NULL, NULL, &io,
        IOCTL_TAPE_ERASE, &erase, sizeof(TAPE_ERASE), NULL, 0 );

    return set_error_from_status( status );
}


/************************************************************************
 *		GetTapeParameters  (KERNEL32.@)
 *
 * Retrieve information about a tape or tape drive.
 */
DWORD WINAPI GetTapeParameters( HANDLE device, DWORD operation,
    LPDWORD size, LPVOID info )
{
    NTSTATUS status = STATUS_INVALID_PARAMETER;
    IO_STATUS_BLOCK io;

    TRACE( "(%p, %d, %p, %p)\n", device, operation, size, info );

    switch (operation)
    {
    case GET_TAPE_DRIVE_INFORMATION:
        status = NtDeviceIoControlFile( device, NULL, NULL, NULL, &io,
            IOCTL_TAPE_GET_DRIVE_PARAMS, NULL, 0, info, *size );
        break;
    case GET_TAPE_MEDIA_INFORMATION:
        status = NtDeviceIoControlFile( device, NULL, NULL, NULL, &io,
            IOCTL_TAPE_GET_MEDIA_PARAMS, NULL, 0, info, *size ); 
        break;
    default:
        ERR( "Unhandled operation: 0x%08x\n", operation );
    }

    return set_error_from_status( status );
}


/************************************************************************
 *		GetTapePosition  (KERNEL32.@)
 *
 *	Retrieve the current tape position.
 */
DWORD WINAPI GetTapePosition( HANDLE device, DWORD type, LPDWORD partition,
    LPDWORD offset_low, LPDWORD offset_high )
{
    NTSTATUS status;
    TAPE_GET_POSITION in, out;
    IO_STATUS_BLOCK io;

    TRACE( "(%p, %d, %p, %p, %p)\n", device, type, partition, offset_low,
           offset_high );

    memset( &in, 0, sizeof(TAPE_GET_POSITION) );
    in.Type = type;

    status = NtDeviceIoControlFile( device, NULL, NULL, NULL, &io,
        IOCTL_TAPE_GET_POSITION, &in, sizeof(TAPE_GET_POSITION),
        &out, sizeof(TAPE_GET_POSITION) );

    if (status != STATUS_SUCCESS)
        return set_error_from_status( status );

    *partition = out.Partition;
    *offset_low = out.OffsetLow;
    *offset_high = out.OffsetHigh;

    return set_error_from_status( status );
}


/************************************************************************
 *		GetTapeStatus  (KERNEL32.@)
 *
 * Determine if the tape device is ready for operation.
 */
DWORD WINAPI GetTapeStatus( HANDLE device )
{
    NTSTATUS status;
    IO_STATUS_BLOCK io;

    TRACE( "(%p)\n", device );

    status = NtDeviceIoControlFile( device, NULL, NULL, NULL, &io,
        IOCTL_TAPE_GET_STATUS, NULL, 0, NULL, 0 );

    return set_error_from_status( status );
}


/************************************************************************
 *		PrepareTape  (KERNEL32.@)
 *
 * Prepare a tape for operation.
 */
DWORD WINAPI PrepareTape( HANDLE device, DWORD operation, BOOL immediate )
{
    NTSTATUS status;
    TAPE_PREPARE prep;
    IO_STATUS_BLOCK io;

    TRACE( "(%p, %d, %d)\n", device, operation, immediate );

    prep.Operation = operation;
    prep.Immediate = immediate;

    status = NtDeviceIoControlFile( device, NULL, NULL, NULL, &io,
        IOCTL_TAPE_PREPARE, &prep, sizeof(TAPE_PREPARE), NULL, 0 );

    return set_error_from_status( status );
}


/************************************************************************
 *		SetTapeParameters  (KERNEL32.@)
 *
 * Configure a tape or tape device.
 */
DWORD WINAPI SetTapeParameters( HANDLE device, DWORD operation, LPVOID info )
{
    NTSTATUS status = STATUS_INVALID_PARAMETER;
    IO_STATUS_BLOCK io;

    TRACE( "(%p, %d, %p)\n", device, operation, info );

    switch (operation)
    {
    case SET_TAPE_DRIVE_INFORMATION:
        status = NtDeviceIoControlFile( device, NULL, NULL, NULL, &io,
            IOCTL_TAPE_SET_DRIVE_PARAMS, info, sizeof(TAPE_SET_DRIVE_PARAMETERS),
            NULL, 0 );
        break;
    case SET_TAPE_MEDIA_INFORMATION:
        status = NtDeviceIoControlFile( device, NULL, NULL, NULL, &io,
            IOCTL_TAPE_SET_MEDIA_PARAMS, info, sizeof(TAPE_SET_MEDIA_PARAMETERS),
            NULL, 0 );
        break;
    default:
        ERR( "Unhandled operation: 0x%08x\n", operation );
    }

    return set_error_from_status( status );
}


/************************************************************************
 *		SetTapePosition  (KERNEL32.@)
 *
 * Set the tape position on a given device.
 */
DWORD WINAPI SetTapePosition( HANDLE device, DWORD method, DWORD partition,
    DWORD offset_low, DWORD offset_high, BOOL immediate )
{
    NTSTATUS status;
    TAPE_SET_POSITION pos;
    IO_STATUS_BLOCK io;

    TRACE( "(%p, %d, %d, %d, %d, %d)\n", device, method, partition,
           offset_low, offset_high, immediate );

    pos.Method = method;
    pos.Partition = partition;
    pos.Offset.u.LowPart = offset_low;
    pos.Offset.u.HighPart = offset_high;
    pos.Immediate = immediate;

    status = NtDeviceIoControlFile( device, NULL, NULL, NULL, &io,
        IOCTL_TAPE_SET_POSITION, &pos, sizeof(TAPE_SET_POSITION), NULL, 0 );

    return set_error_from_status( status );
}


/************************************************************************
 *		WriteTapemark  (KERNEL32.@)
 *
 * Write tapemarks on a tape.
 */
DWORD WINAPI WriteTapemark( HANDLE device, DWORD type, DWORD count,
    BOOL immediate )
{
    NTSTATUS status;
    TAPE_WRITE_MARKS marks;
    IO_STATUS_BLOCK io;

    TRACE( "(%p, %d, %d, %d)\n", device, type, count, immediate );

    marks.Type = type;
    marks.Count = count;
    marks.Immediate = immediate;

    status = NtDeviceIoControlFile( device, NULL, NULL, NULL, &io,
        IOCTL_TAPE_WRITE_MARKS, &marks, sizeof(TAPE_WRITE_MARKS), NULL, 0 );

    return set_error_from_status( status );
}