package btrfs

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
)

func Send(w io.Writer, parent string, subvols ...string) error {
	if len(subvols) == 0 {
		return nil
	}
	// use first send subvol to determine mount_root
	subvol, err := filepath.Abs(subvols[0])
	if err != nil {
		return err
	}
	mountRoot, err := findMountRoot(subvol)
	if err == os.ErrNotExist {
		return fmt.Errorf("cannot find a mountpoint for %s", subvol)
	} else if err != nil {
		return err
	}
	var (
		cloneSrc []uint64
		parentID uint64
	)
	if parent != "" {
		parent, err = filepath.Abs(parent)
		if err != nil {
			return err
		}
		f, err := os.Open(parent)
		if err != nil {
			return fmt.Errorf("cannot open parent: %v", err)
		}
		id, err := getPathRootID(f)
		f.Close()
		if err != nil {
			return fmt.Errorf("cannot get parent root id: %v", err)
		}
		parentID = id
		cloneSrc = append(cloneSrc, id)
	}
	// check all subvolumes
	paths := make([]string, 0, len(subvols))
	for _, sub := range subvols {
		sub, err = filepath.Abs(sub)
		if err != nil {
			return err
		}
		paths = append(paths, sub)
		mount, err := findMountRoot(sub)
		if err != nil {
			return err
		} else if mount != mountRoot {
			return fmt.Errorf("all subvolumes must be from the same filesystem (%s is not)", sub)
		}
		ok, err := IsReadOnly(sub)
		if err != nil {
			return err
		} else if !ok {
			return fmt.Errorf("subvolume %s is not read-only", sub)
		}
	}
	//full := len(cloneSrc) == 0
	for i, sub := range paths {
		//if len(cloneSrc) > 1 {
		//	// TODO: find_good_parent
		//}
		//if !full { // TODO
		//	cloneSrc = append(cloneSrc, )
		//}
		fs, err := Open(sub, true)
		if err != nil {
			return err
		}
		var flags uint64
		if i != 0 { // not first
			flags |= _BTRFS_SEND_FLAG_OMIT_STREAM_HEADER
		}
		if i < len(paths)-1 { // not last
			flags |= _BTRFS_SEND_FLAG_OMIT_END_CMD
		}
		err = send(w, fs.f, parentID, cloneSrc, flags)
		if err != nil {
			return fmt.Errorf("error sending %s: %v", sub, err)
		}
	}
	return nil
}

func send(w io.Writer, subvol *os.File, parent uint64, sources []uint64, flags uint64) error {
	pr, pw, err := os.Pipe()
	if err != nil {
		return err
	}
	errc := make(chan error, 1)
	go func() {
		defer pr.Close()
		_, err := io.Copy(w, pr)
		errc <- err
	}()
	fd := pw.Fd()
	wait := func() error {
		pw.Close()
		return <-errc
	}
	args := &btrfs_ioctl_send_args{
		send_fd:     int64(fd),
		parent_root: parent,
		flags:       flags,
	}
	if len(sources) != 0 {
		args.clone_sources = &sources[0]
		args.clone_sources_count = uint64(len(sources))
	}
	if err := iocSend(subvol, args); err != nil {
		wait()
		return err
	}
	return wait()
}