package sshfx

// Attributes related flags.
const (
	AttrSize        = 1 << iota // SSH_FILEXFER_ATTR_SIZE
	AttrUIDGID                  // SSH_FILEXFER_ATTR_UIDGID
	AttrPermissions             // SSH_FILEXFER_ATTR_PERMISSIONS
	AttrACModTime               // SSH_FILEXFER_ACMODTIME

	AttrExtended = 1 << 31 // SSH_FILEXFER_ATTR_EXTENDED
)

// Attributes defines the file attributes type defined in draft-ietf-secsh-filexfer-02
//
// Defined in: https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-5
type Attributes struct {
	Flags uint32

	// AttrSize
	Size uint64

	// AttrUIDGID
	UID uint32
	GID uint32

	// AttrPermissions
	Permissions FileMode

	// AttrACmodTime
	ATime uint32
	MTime uint32

	// AttrExtended
	ExtendedAttributes []ExtendedAttribute
}

// GetSize returns the Size field and a bool that is true if and only if the value is valid/defined.
func ( *Attributes) () ( uint64,  bool) {
	return .Size, .Flags&AttrSize != 0
}

// SetSize is a convenience function that sets the Size field,
// and marks the field as valid/defined in Flags.
func ( *Attributes) ( uint64) {
	.Flags |= AttrSize
	.Size = 
}

// GetUIDGID returns the UID and GID fields and a bool that is true if and only if the values are valid/defined.
func ( *Attributes) () (,  uint32,  bool) {
	return .UID, .GID, .Flags&AttrUIDGID != 0
}

// SetUIDGID is a convenience function that sets the UID and GID fields,
// and marks the fields as valid/defined in Flags.
func ( *Attributes) (,  uint32) {
	.Flags |= AttrUIDGID
	.UID = 
	.GID = 
}

// GetPermissions returns the Permissions field and a bool that is true if and only if the value is valid/defined.
func ( *Attributes) () ( FileMode,  bool) {
	return .Permissions, .Flags&AttrPermissions != 0
}

// SetPermissions is a convenience function that sets the Permissions field,
// and marks the field as valid/defined in Flags.
func ( *Attributes) ( FileMode) {
	.Flags |= AttrPermissions
	.Permissions = 
}

// GetACModTime returns the ATime and MTime fields and a bool that is true if and only if the values are valid/defined.
func ( *Attributes) () (,  uint32,  bool) {
	return .ATime, .MTime, .Flags&AttrACModTime != 0
}

// SetACModTime is a convenience function that sets the ATime and MTime fields,
// and marks the fields as valid/defined in Flags.
func ( *Attributes) (,  uint32) {
	.Flags |= AttrACModTime
	.ATime = 
	.MTime = 
}

// Len returns the number of bytes a would marshal into.
func ( *Attributes) () int {
	 := 4

	if .Flags&AttrSize != 0 {
		 += 8
	}

	if .Flags&AttrUIDGID != 0 {
		 += 4 + 4
	}

	if .Flags&AttrPermissions != 0 {
		 += 4
	}

	if .Flags&AttrACModTime != 0 {
		 += 4 + 4
	}

	if .Flags&AttrExtended != 0 {
		 += 4

		for ,  := range .ExtendedAttributes {
			 += .Len()
		}
	}

	return 
}

// MarshalInto marshals e onto the end of the given Buffer.
func ( *Attributes) ( *Buffer) {
	.AppendUint32(.Flags)

	if .Flags&AttrSize != 0 {
		.AppendUint64(.Size)
	}

	if .Flags&AttrUIDGID != 0 {
		.AppendUint32(.UID)
		.AppendUint32(.GID)
	}

	if .Flags&AttrPermissions != 0 {
		.AppendUint32(uint32(.Permissions))
	}

	if .Flags&AttrACModTime != 0 {
		.AppendUint32(.ATime)
		.AppendUint32(.MTime)
	}

	if .Flags&AttrExtended != 0 {
		.AppendUint32(uint32(len(.ExtendedAttributes)))

		for ,  := range .ExtendedAttributes {
			.MarshalInto()
		}
	}
}

// MarshalBinary returns a as the binary encoding of a.
func ( *Attributes) () ([]byte, error) {
	 := NewBuffer(make([]byte, 0, .Len()))
	.MarshalInto()
	return .Bytes(), nil
}

// UnmarshalFrom unmarshals an Attributes from the given Buffer into e.
//
// NOTE: The values of fields not covered in the a.Flags are explicitly undefined.
func ( *Attributes) ( *Buffer) ( error) {
	 := .ConsumeUint32()

	return .XXX_UnmarshalByFlags(, )
}

// XXX_UnmarshalByFlags uses the pre-existing a.Flags field to determine which fields to decode.
// DO NOT USE THIS: it is an anti-corruption function to implement existing internal usage in pkg/sftp.
// This function is not a part of any compatibility promise.
func ( *Attributes) ( uint32,  *Buffer) ( error) {
	.Flags = 

	// Short-circuit dummy attributes.
	if .Flags == 0 {
		return .Err
	}

	if .Flags&AttrSize != 0 {
		.Size = .ConsumeUint64()
	}

	if .Flags&AttrUIDGID != 0 {
		.UID = .ConsumeUint32()
		.GID = .ConsumeUint32()
	}

	if .Flags&AttrPermissions != 0 {
		.Permissions = FileMode(.ConsumeUint32())
	}

	if .Flags&AttrACModTime != 0 {
		.ATime = .ConsumeUint32()
		.MTime = .ConsumeUint32()
	}

	if .Flags&AttrExtended != 0 {
		 := .ConsumeCount()

		.ExtendedAttributes = make([]ExtendedAttribute, )
		for  := range .ExtendedAttributes {
			.ExtendedAttributes[].UnmarshalFrom()
		}
	}

	return .Err
}

// UnmarshalBinary decodes the binary encoding of Attributes into e.
func ( *Attributes) ( []byte) error {
	return .UnmarshalFrom(NewBuffer())
}

// ExtendedAttribute defines the extended file attribute type defined in draft-ietf-secsh-filexfer-02
//
// Defined in: https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-5
type ExtendedAttribute struct {
	Type string
	Data string
}

// Len returns the number of bytes e would marshal into.
func ( *ExtendedAttribute) () int {
	return 4 + len(.Type) + 4 + len(.Data)
}

// MarshalInto marshals e onto the end of the given Buffer.
func ( *ExtendedAttribute) ( *Buffer) {
	.AppendString(.Type)
	.AppendString(.Data)
}

// MarshalBinary returns e as the binary encoding of e.
func ( *ExtendedAttribute) () ([]byte, error) {
	 := NewBuffer(make([]byte, 0, .Len()))
	.MarshalInto()
	return .Bytes(), nil
}

// UnmarshalFrom unmarshals an ExtendedAattribute from the given Buffer into e.
func ( *ExtendedAttribute) ( *Buffer) ( error) {
	* = ExtendedAttribute{
		Type: .ConsumeString(),
		Data: .ConsumeString(),
	}

	return .Err
}

// UnmarshalBinary decodes the binary encoding of ExtendedAttribute into e.
func ( *ExtendedAttribute) ( []byte) error {
	return .UnmarshalFrom(NewBuffer())
}

// NameEntry implements the SSH_FXP_NAME repeated data type from draft-ietf-secsh-filexfer-02
//
// This type is incompatible with versions 4 or higher.
type NameEntry struct {
	Filename string
	Longname string
	Attrs    Attributes
}

// Len returns the number of bytes e would marshal into.
func ( *NameEntry) () int {
	return 4 + len(.Filename) + 4 + len(.Longname) + .Attrs.Len()
}

// MarshalInto marshals e onto the end of the given Buffer.
func ( *NameEntry) ( *Buffer) {
	.AppendString(.Filename)
	.AppendString(.Longname)

	.Attrs.MarshalInto()
}

// MarshalBinary returns e as the binary encoding of e.
func ( *NameEntry) () ([]byte, error) {
	 := NewBuffer(make([]byte, 0, .Len()))
	.MarshalInto()
	return .Bytes(), nil
}

// UnmarshalFrom unmarshals an NameEntry from the given Buffer into e.
//
// NOTE: The values of fields not covered in the a.Flags are explicitly undefined.
func ( *NameEntry) ( *Buffer) ( error) {
	* = NameEntry{
		Filename: .ConsumeString(),
		Longname: .ConsumeString(),
	}

	return .Attrs.UnmarshalFrom()
}

// UnmarshalBinary decodes the binary encoding of NameEntry into e.
func ( *NameEntry) ( []byte) error {
	return .UnmarshalFrom(NewBuffer())
}