package mmap

import "os"

type File struct {
	f   *os.File
	Map []byte
}

func Create(path string, size int64) (*File, error) {
	f, err := os.Create(path)
	if err != nil {
		return nil, err
	}
	if err := f.Truncate(size); err != nil {
		f.Close()
		return nil, err
	}
	m, err := Map(f, PROT_READ|PROT_WRITE)
	if err != nil {
		f.Close()
		return nil, err
	}
	return &File{f, m}, nil
}

// Opens a mapped file in read-only mode.
func Open(path string) (*File, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	m, err := Map(f, PROT_READ)
	if err != nil {
		f.Close()
		return nil, err
	}
	return &File{f, m}, nil
}

func OpenFile(
	path string,
	fileFlags int,
	perm os.FileMode,
	size int64, // -1 for file size.
) (*File, error) {
	f, err := os.OpenFile(path, fileFlags, perm)
	if err != nil {
		return nil, err
	}

	writable := fileFlags|os.O_RDWR != 0 || fileFlags|os.O_WRONLY != 0

	fi, err := f.Stat()
	if err != nil {
		f.Close()
		return nil, err
	}

	if writable && size > 0 && size != fi.Size() {
		if err := f.Truncate(size); err != nil {
			f.Close()
			return nil, err
		}
	}

	mapFlags := PROT_READ
	if writable {
		mapFlags |= PROT_WRITE
	}

	m, err := Map(f, mapFlags)
	if err != nil {
		f.Close()
		return nil, err
	}

	return &File{f, m}, nil
}

func (f *File) Sync() error {
	return Sync(f.Map)
}

func (f *File) Close() error {
	if f.Map != nil {
		if err := Unmap(f.Map); err != nil {
			return err
		}
		f.Map = nil
	}

	if f.f != nil {
		if err := f.f.Close(); err != nil {
			return err
		}
		f.f = nil
	}
	return nil
}