// Copyright 2009 The Go9p Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package go9p

import "fmt"

// Create a Rversion message in the specified Fcall.
func PackRversion(fc *Fcall, msize uint32, version string) error {
	size := 4 + 2 + len(version) /* msize[4] version[s] */
	p, err := packCommon(fc, size, Rversion)
	if err != nil {
		return err
	}

	fc.Msize = msize
	fc.Version = version
	p = pint32(msize, p)
	p = pstr(version, p)

	return nil
}

// Create a Rauth message in the specified Fcall.
func PackRauth(fc *Fcall, aqid *Qid) error {
	size := 13 /* aqid[13] */
	p, err := packCommon(fc, size, Rauth)
	if err != nil {
		return err
	}

	fc.Qid = *aqid
	p = pqid(aqid, p)
	return nil
}

// Create a Rerror message in the specified Fcall. If dotu is true,
// the function will create a 9P2000.u message. If false, errornum is
// ignored.
// The Akaros global is hurl-inducing but this whole blob of code
// needs a redo. dotu is even worse, since it bakes in ONE PARTICULAR
// EXTENSION ...

func PackRerror(fc *Fcall, error string, errornum uint32, dotu bool) error {
	if *Akaros {
		error = fmt.Sprintf("%04X %v", errornum, error)
	}

	size := 2 + len(error) /* ename[s] */
	if dotu {
		size += 4 /* ecode[4] */
	}

	p, err := packCommon(fc, size, Rerror)
	if err != nil {
		return err
	}

	fc.Error = error
	p = pstr(error, p)
	if dotu {
		fc.Errornum = errornum
		p = pint32(errornum, p)
	}

	return nil
}

// Create a Rflush message in the specified Fcall.
func PackRflush(fc *Fcall) error {
	_, err := packCommon(fc, 0, Rflush)

	return err
}

// Create a Rattach message in the specified Fcall.
func PackRattach(fc *Fcall, aqid *Qid) error {
	size := 13 /* aqid[13] */
	p, err := packCommon(fc, size, Rattach)
	if err != nil {
		return err
	}

	fc.Qid = *aqid
	p = pqid(aqid, p)
	return nil
}

// Create a Rwalk message in the specified Fcall.
func PackRwalk(fc *Fcall, wqids []Qid) error {
	nwqid := len(wqids)
	size := 2 + nwqid*13 /* nwqid[2] nwname*wqid[13] */
	p, err := packCommon(fc, size, Rwalk)
	if err != nil {
		return err
	}

	p = pint16(uint16(nwqid), p)
	fc.Wqid = make([]Qid, nwqid)
	for i := 0; i < nwqid; i++ {
		fc.Wqid[i] = wqids[i]
		p = pqid(&wqids[i], p)
	}

	return nil
}

// Create a Ropen message in the specified Fcall.
func PackRopen(fc *Fcall, qid *Qid, iounit uint32) error {
	size := 13 + 4 /* qid[13] iounit[4] */
	p, err := packCommon(fc, size, Ropen)
	if err != nil {
		return err
	}

	fc.Qid = *qid
	fc.Iounit = iounit
	p = pqid(qid, p)
	p = pint32(iounit, p)
	return nil
}

// Create a Rcreate message in the specified Fcall.
func PackRcreate(fc *Fcall, qid *Qid, iounit uint32) error {
	size := 13 + 4 /* qid[13] iounit[4] */
	p, err := packCommon(fc, size, Rcreate)
	if err != nil {
		return err
	}

	fc.Qid = *qid
	fc.Iounit = iounit
	p = pqid(qid, p)
	p = pint32(iounit, p)
	return nil
}

// Initializes the specified Fcall value to contain Rread message.
// The user should copy the returned data to the slice pointed by
// fc.Data and call SetRreadCount to update the data size to the
// actual value.
func InitRread(fc *Fcall, count uint32) error {
	size := int(4 + count) /* count[4] data[count] */
	p, err := packCommon(fc, size, Rread)
	if err != nil {
		return err
	}

	fc.Count = count
	fc.Data = p[4 : fc.Count+4]
	p = pint32(count, p)
	return nil
}

// Updates the size of the data returned by Rread. Expects that
// the Fcall value is already initialized by InitRread.
func SetRreadCount(fc *Fcall, count uint32) {
	/* we need to update both the packet size as well as the data count */
	size := 4 + 1 + 2 + 4 + count /* size[4] id[1] tag[2] count[4] data[count] */
	pint32(size, fc.Pkt)
	pint32(count, fc.Pkt[7:])
	fc.Size = size
	fc.Count = count
	fc.Pkt = fc.Pkt[0:size]
	fc.Data = fc.Data[0:count]
	fc.Size = size
}

// Create a Rread message in the specified Fcall.
func PackRread(fc *Fcall, data []byte) error {
	count := uint32(len(data))
	err := InitRread(fc, count)
	if err != nil {
		return err
	}

	copy(fc.Data, data)
	return nil
}

// Create a Rwrite message in the specified Fcall.
func PackRwrite(fc *Fcall, count uint32) error {
	p, err := packCommon(fc, 4, Rwrite) /* count[4] */
	if err != nil {
		return err
	}

	fc.Count = count

	p = pint32(count, p)
	return nil
}

// Create a Rclunk message in the specified Fcall.
func PackRclunk(fc *Fcall) error {
	_, err := packCommon(fc, 0, Rclunk)
	return err
}

// Create a Rremove message in the specified Fcall.
func PackRremove(fc *Fcall) error {
	_, err := packCommon(fc, 0, Rremove)
	return err
}

// Create a Rstat message in the specified Fcall. If dotu is true, the
// function will create a 9P2000.u stat representation that includes
// st.Nuid, st.Ngid, st.Nmuid and st.Ext. Otherwise these values will be
// ignored.
func PackRstat(fc *Fcall, d *Dir, dotu bool) error {
	stsz := statsz(d, dotu)
	size := 2 + stsz /* stat[n] */
	p, err := packCommon(fc, size, Rstat)
	if err != nil {
		return err
	}

	p = pint16(uint16(stsz), p)
	p = pstat(d, p, dotu)
	fc.Dir = *d
	return nil
}

// Create a Rwstat message in the specified Fcall.
func PackRwstat(fc *Fcall) error {
	_, err := packCommon(fc, 0, Rwstat)
	return err
}
