package lt

import (
	"net"
	"net/url"
	"os"
	"path"
	"syscall"

	"golang.org/x/sys/unix"
)

// ---------------------------------------------------------------------------
// Conn
// ---------------------------------------------------------------------------

func dial(addr string) (net.Conn, error) {
	u, err := url.Parse(addr)
	if err != nil {
		return nil, err
	}

	return net.Dial("unix", path.Join(os.TempDir(), u.Scheme+".sock"))
}

// ---------------------------------------------------------------------------
// Shared buffer
// ---------------------------------------------------------------------------

type buffer struct {
	name string
	fd   int
	Data []byte
	ref  int
}

const shmPath = "/dev/shm/"

func mapBuffer(handle string, size int) (*buffer, error) {
	b := &buffer{}

	// Open the shared memory
	perm := uint32(unix.S_IRUSR | unix.S_IWUSR | unix.S_IRGRP | unix.S_IWGRP)
	fd, err := syscall.Open(shmPath+handle, unix.O_RDWR, perm)
	if err != nil {
		return nil, &os.PathError{Op: "open", Path: handle, Err: err}
	}

	// Setup the buffer now so we can clean it up easy
	b.name = handle
	b.fd = fd
	b.Data = nil
	b.ref = 0

	// Resize to the required size
	err = syscall.Ftruncate(b.fd, int64(size))
	if err != nil {
		b.Close()
		return nil, &os.PathError{Op: "ftruncate", Path: handle, Err: err}
	}

	// Map the file in memory
	b.Data, err = syscall.Mmap(b.fd, 0, size, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
	if err != nil {
		b.Close()
		return nil, &os.PathError{Op: "mmap", Path: handle, Err: err}
	}

	// Done
	return b, nil
}

func (b *buffer) Close() error {
	if b == nil {
		return nil
	}
	// Unmap memory
	if b.Data != nil {
		syscall.Munmap(b.Data)
	}
	// Close file descriptor
	if b.fd >= 0 {
		syscall.Close(b.fd)
	}

	return nil
}
