package tdesktop

import (
	
	 // #nosec G501
	
	
	
	

	
	
)

type tdesktopFile struct {
	data    []byte
	n       int
	version uint32
}

func ( fs.FS,  string) (*tdesktopFile, error) {
	 := []string{"0", "1", "s"}

	 := func( string) ( *tdesktopFile,  error) {
		,  := .Open()
		if  != nil {
			return nil, errors.Wrap(, "open")
		}
		defer multierr.AppendInvoke(&, multierr.Close())

		return fromFile()
	}

	for ,  := range  {
		 :=  + 
		if ,  := fs.Stat(, );  != nil {
			if errors.Is(, fs.ErrNotExist) ||
				errors.Is(, fs.ErrPermission) {
				continue
			}
			return nil, errors.Wrap(, "stat")
		}

		,  := ()
		if  != nil {
			if errors.Is(, io.ErrUnexpectedEOF) {
				continue
			}

			var  *WrongMagicError
			if errors.As(, &) {
				continue
			}
			return nil, errors.Wrap(, "read tdesktop file")
		}

		return , nil
	}

	return nil, errors.Errorf("file %q not found", )
}

var tdesktopFileMagic = [4]byte{'T', 'D', 'F', '$'}

// fromFile creates new Telegram Desktop storage file.
// Based on https://github.com/telegramdesktop/tdesktop/blob/v2.9.8/Telegram/SourceFiles/storage/details/storage_file_utilities.cpp#L473.
func ( io.Reader) (*tdesktopFile, error) {
	 := make([]byte, 16)
	if ,  := io.ReadFull(, [:8]);  != nil {
		return nil, errors.Wrap(, "read magic and version")
	}

	var ,  [4]byte
	copy([:], [:4])
	// TODO(tdakkota): check version
	copy([:], [4:8])
	if  != tdesktopFileMagic {
		return nil, &WrongMagicError{
			Magic: ,
		}
	}

	,  := io.ReadAll()
	if  != nil {
		return nil, errors.Wrap(, "read data")
	}
	if  := len();  < 16 {
		return nil, errors.Errorf("invalid data length %d", )
	}
	 := [len()-16:]
	 = [:len()-16]

	 := telegramFileHash(, )
	if !bytes.Equal([:], ) {
		return nil, errors.New("hash mismatch")
	}

	 := binary.LittleEndian.Uint32([:])
	return &tdesktopFile{
		data:    ,
		version: ,
	}, nil
}

func ( io.Writer,  []byte,  [4]byte) error {
	if ,  := .Write(tdesktopFileMagic[:]);  != nil {
		return errors.Wrap(, "write magic")
	}
	if ,  := .Write([:]);  != nil {
		return errors.Wrap(, "write version")
	}
	if ,  := .Write();  != nil {
		return errors.Wrap(, "write data")
	}
	 := telegramFileHash(, )
	if ,  := .Write([:]);  != nil {
		return errors.Wrap(, "write hash")
	}
	return nil
}

func ( []byte,  [4]byte) ( [md5.Size]byte) {
	 := md5.New() // #nosec G401
	_, _ = .Write()
	var  [4]byte
	binary.LittleEndian.PutUint32([:], uint32(len()))
	_, _ = .Write([:])
	_, _ = .Write([:])
	_, _ = .Write(tdesktopFileMagic[:])
	.Sum([:0])
	return 
}

func ( *tdesktopFile) () ([]byte, error) {
	, ,  := readArray(.data[.n:], binary.BigEndian)
	if  != nil {
		return nil, 
	}
	.n += 
	return , nil
}

func ( []byte,  binary.ByteOrder) ( []byte,  int,  error) {
	if len() < 4 {
		return nil, 0, io.ErrUnexpectedEOF
	}
	// See https://github.com/qt/qtbase/blob/5.15.2/src/corelib/text/qbytearray.cpp#L3314.
	 := .Uint32()
	if  == 0xffffffff {
		return nil, 4, nil
	}

	if uint64() >= uint64(len()) {
		return nil, 0, io.ErrUnexpectedEOF
	}
	 := [4 : 4+]
	return , len() + 4, nil
}

func ( io.Writer,  []byte,  binary.ByteOrder) error {
	 := len()
	if uint64() > uint64(math.MaxUint32) {
		return errors.Errorf("data length too big (%d)", )
	}

	 := make([]byte, 4)
	.PutUint32(, uint32())
	if ,  := .Write();  != nil {
		return errors.Wrap(, "write length")
	}

	if ,  := .Write();  != nil {
		return errors.Wrap(, "write data")
	}

	return nil
}