package sqlite3

import (
	

	
)

// ZeroBlob represents a zero-filled, length n BLOB
// that can be used as an argument to
// [database/sql.DB.Exec] and similar methods.
type ZeroBlob int64

// Blob is an handle to an open BLOB.
//
// It implements [io.ReadWriteSeeker] for incremental BLOB I/O.
//
// https://sqlite.org/c3ref/blob.html
type Blob struct {
	c      *Conn
	bytes  int64
	offset int64
	handle ptr_t
	bufptr ptr_t
	buflen int64
}

var _ io.ReadWriteSeeker = &Blob{}

// OpenBlob opens a BLOB for incremental I/O.
//
// https://sqlite.org/c3ref/blob_open.html
func ( *Conn) (, ,  string,  int64,  bool) (*Blob, error) {
	if .interrupt.Err() != nil {
		return nil, INTERRUPT
	}

	defer .arena.mark()()
	 := .arena.new(ptrlen)
	 := .arena.string()
	 := .arena.string()
	 := .arena.string()

	var  int32
	if  {
		 = 1
	}

	 := res_t(.call("sqlite3_blob_open", stk_t(.handle),
		stk_t(), stk_t(), stk_t(),
		stk_t(), stk_t(), stk_t()))

	if  := .error();  != nil {
		return nil, 
	}

	 := Blob{c: }
	.handle = util.Read32[ptr_t](.mod, )
	.bytes = int64(int32(.call("sqlite3_blob_bytes", stk_t(.handle))))
	return &, nil
}

// Close closes a BLOB handle.
//
// It is safe to close a nil, zero or closed Blob.
//
// https://sqlite.org/c3ref/blob_close.html
func ( *Blob) () error {
	if  == nil || .handle == 0 {
		return nil
	}

	 := res_t(.c.call("sqlite3_blob_close", stk_t(.handle)))
	.c.free(.bufptr)
	.handle = 0
	return .c.error()
}

// Size returns the size of the BLOB in bytes.
//
// https://sqlite.org/c3ref/blob_bytes.html
func ( *Blob) () int64 {
	return .bytes
}

// Read implements the [io.Reader] interface.
//
// https://sqlite.org/c3ref/blob_read.html
func ( *Blob) ( []byte) ( int,  error) {
	if .offset >= .bytes {
		return 0, io.EOF
	}

	 := int64(len())
	 := .bytes - .offset
	if  >  {
		 = 
	}
	if  > .buflen {
		.bufptr = .c.realloc(.bufptr, )
		.buflen = 
	}

	 := res_t(.c.call("sqlite3_blob_read", stk_t(.handle),
		stk_t(.bufptr), stk_t(), stk_t(.offset)))
	 = .c.error()
	if  != nil {
		return 0, 
	}
	.offset += 
	if .offset >= .bytes {
		 = io.EOF
	}

	copy(, util.View(.c.mod, .bufptr, ))
	return int(), 
}

// WriteTo implements the [io.WriterTo] interface.
//
// https://sqlite.org/c3ref/blob_read.html
func ( *Blob) ( io.Writer) ( int64,  error) {
	if .offset >= .bytes {
		return 0, nil
	}

	 := int64(1024 * 1024)
	 := .bytes - .offset
	if  >  {
		 = 
	}
	if  > .buflen {
		.bufptr = .c.realloc(.bufptr, )
		.buflen = 
	}

	for  > 0 {
		 := res_t(.c.call("sqlite3_blob_read", stk_t(.handle),
			stk_t(.bufptr), stk_t(), stk_t(.offset)))
		 = .c.error()
		if  != nil {
			return , 
		}

		 := util.View(.c.mod, .bufptr, )
		,  := .Write([:])
		.offset += int64()
		 += int64()
		if  != nil {
			return , 
		}
		if int64() !=  {
			// notest // Write misbehaving
			return , io.ErrShortWrite
		}

		 = .bytes - .offset
		if  >  {
			 = 
		}
	}
	return , nil
}

// Write implements the [io.Writer] interface.
//
// https://sqlite.org/c3ref/blob_write.html
func ( *Blob) ( []byte) ( int,  error) {
	 := int64(len())
	if  > .buflen {
		.bufptr = .c.realloc(.bufptr, )
		.buflen = 
	}
	util.WriteBytes(.c.mod, .bufptr, )

	 := res_t(.c.call("sqlite3_blob_write", stk_t(.handle),
		stk_t(.bufptr), stk_t(), stk_t(.offset)))
	 = .c.error()
	if  != nil {
		return 0, 
	}
	.offset += int64(len())
	return len(), nil
}

// ReadFrom implements the [io.ReaderFrom] interface.
//
// https://sqlite.org/c3ref/blob_write.html
func ( *Blob) ( io.Reader) ( int64,  error) {
	 := int64(1024 * 1024)
	 := .bytes - .offset
	if ,  := .(*io.LimitedReader);  &&  > .N {
		 = .N
	}
	if  >  {
		 = 
	}
	if  < 1 {
		 = 1
	}
	if  > .buflen {
		.bufptr = .c.realloc(.bufptr, )
		.buflen = 
	}

	for {
		 := util.View(.c.mod, .bufptr, )
		,  := .Read([:])
		if  > 0 {
			 := res_t(.c.call("sqlite3_blob_write", stk_t(.handle),
				stk_t(.bufptr), stk_t(), stk_t(.offset)))
			 := .c.error()
			if  != nil {
				return , 
			}
			.offset += int64()
			 += int64()
		}
		if  == io.EOF {
			return , nil
		}
		if  != nil {
			return , 
		}

		 = .bytes - .offset
		if  >  {
			 = 
		}
		if  < 1 {
			 = 1
		}
	}
}

// Seek implements the [io.Seeker] interface.
func ( *Blob) ( int64,  int) (int64, error) {
	switch  {
	default:
		return 0, util.WhenceErr
	case io.SeekStart:
		break
	case io.SeekCurrent:
		 += .offset
	case io.SeekEnd:
		 += .bytes
	}
	if  < 0 {
		return 0, util.OffsetErr
	}
	.offset = 
	return , nil
}

// Reopen moves a BLOB handle to a new row of the same database table.
//
// https://sqlite.org/c3ref/blob_reopen.html
func ( *Blob) ( int64) error {
	if .c.interrupt.Err() != nil {
		return INTERRUPT
	}
	 := .c.error(res_t(.c.call("sqlite3_blob_reopen", stk_t(.handle), stk_t())))
	.bytes = int64(int32(.c.call("sqlite3_blob_bytes", stk_t(.handle))))
	.offset = 0
	return 
}