package common

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
)

const VMWARE_FUSION_VERSION = "6"

// Fusion6Driver is a driver that can run VMware Fusion 6.
type Fusion6Driver struct {
	Fusion5Driver
}

func NewFusion6Driver(dconfig *DriverConfig, config *SSHConfig) Driver {
	return &Fusion6Driver{
		Fusion5Driver: Fusion5Driver{
			AppPath:   dconfig.FusionAppPath,
			SSHConfig: config,
		},
	}
}

func (d *Fusion6Driver) Clone(dst, src string, linked bool) error {

	var cloneType string
	if linked {
		cloneType = "linked"
	} else {
		cloneType = "full"
	}

	cmd := exec.Command(d.vmrunPath(),
		"-T", "fusion",
		"clone", src, dst,
		cloneType)
	if _, _, err := runAndLog(cmd); err != nil {
		if strings.Contains(err.Error(), "parameters was invalid") {
			return fmt.Errorf(
				"Clone is not supported with your version of Fusion. Packer "+
					"only works with Fusion %s Professional or above. Please verify your version.", VMWARE_FUSION_VERSION)
		}

		return err
	}

	return nil
}

func (d *Fusion6Driver) Verify() error {
	if err := d.Fusion5Driver.Verify(); err != nil {
		return err
	}

	vmxpath := filepath.Join(d.AppPath, "Contents", "Library", "vmware-vmx")
	if _, err := os.Stat(vmxpath); err != nil {
		if os.IsNotExist(err) {
			return fmt.Errorf("vmware-vmx could not be found at path: %s",
				vmxpath)
		}

		return err
	}

	var stderr bytes.Buffer
	cmd := exec.Command(vmxpath, "-v")
	cmd.Stderr = &stderr
	if err := cmd.Run(); err != nil {
		return err
	}

	// Example: VMware Fusion e.x.p build-6048684 Release
	techPreviewRe := regexp.MustCompile(`(?i)VMware [a-z0-9-]+ e\.x\.p `)
	matches := techPreviewRe.FindStringSubmatch(stderr.String())
	if matches != nil {
		log.Printf("Detected VMware version: e.x.p (Tech Preview)")
		return nil
	}

	// Example: VMware Fusion 7.1.3 build-3204469 Release
	versionRe := regexp.MustCompile(`(?i)VMware [a-z0-9-]+ (\d+)\.`)
	matches = versionRe.FindStringSubmatch(stderr.String())
	if matches == nil {
		return fmt.Errorf(
			"Couldn't find VMware version in output: %s", stderr.String())
	}
	log.Printf("Detected VMware version: %s", matches[1])

	libpath := filepath.Join("/", "Library", "Preferences", "VMware Fusion")

	d.VmwareDriver.DhcpLeasesPath = func(device string) string {
		return "/var/db/vmware/vmnet-dhcpd-" + device + ".leases"
	}
	d.VmwareDriver.DhcpConfPath = func(device string) string {
		return filepath.Join(libpath, device, "dhcpd.conf")
	}

	d.VmwareDriver.VmnetnatConfPath = func(device string) string {
		return filepath.Join(libpath, device, "nat.conf")
	}
	d.VmwareDriver.NetworkMapper = func() (NetworkNameMapper, error) {
		pathNetworking := filepath.Join(libpath, "networking")
		if _, err := os.Stat(pathNetworking); err != nil {
			return nil, fmt.Errorf("Could not find networking conf file: %s", pathNetworking)
		}
		log.Printf("Located networkmapper configuration file using Fusion6: %s", pathNetworking)

		fd, err := os.Open(pathNetworking)
		if err != nil {
			return nil, err
		}
		defer fd.Close()

		return ReadNetworkingConfig(fd)
	}

	return compareVersions(matches[1], VMWARE_FUSION_VERSION, "Fusion Professional")
}

func (d *Fusion6Driver) GetVmwareDriver() VmwareDriver {
	return d.Fusion5Driver.VmwareDriver
}
