package main

import (
	"fmt"
	"golang.org/x/xerrors"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"sync/atomic"
	"time"
)

func (api *api) Spawn() (nodeInfo, error) {
	dir, err := ioutil.TempDir(os.TempDir(), "lotus-")
	if err != nil {
		return nodeInfo{}, err
	}

	genParam := "--genesis=" + api.genesis
	id := atomic.AddInt32(&api.cmds, 1)
	if id == 1 {
		// make genesis
		genf, err := ioutil.TempFile(os.TempDir(), "lotus-genesis-")
		if err != nil {
			return nodeInfo{}, err
		}

		api.genesis = genf.Name()
		genParam = "--lotus-make-random-genesis=" + api.genesis

		if err := genf.Close(); err != nil {
			return nodeInfo{}, err
		}

	}

	errlogfile, err := os.OpenFile(dir+".err.log", os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return nodeInfo{}, err
	}
	logfile, err := os.OpenFile(dir+".out.log", os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return nodeInfo{}, err
	}

	mux := newWsMux()

	cmd := exec.Command("./lotus", "daemon", "--bootstrap=false", genParam, "--api", fmt.Sprintf("%d", 2500+id))
	cmd.Stderr = io.MultiWriter(os.Stderr, errlogfile, mux.errpw)
	cmd.Stdout = io.MultiWriter(os.Stdout, logfile, mux.outpw)
	cmd.Env = []string{"LOTUS_PATH=" + dir}
	if err := cmd.Start(); err != nil {
		return nodeInfo{}, err
	}

	info := nodeInfo{
		Repo:    dir,
		ID:      id,
		ApiPort: 2500 + id,
		State:   NodeRunning,
	}

	api.runningLk.Lock()
	api.running[id] = &runningNode{
		cmd:  cmd,
		meta: info,

		mux: mux,
		stop: func() {
			cmd.Process.Signal(os.Interrupt)
			cmd.Process.Wait()

			api.runningLk.Lock()
			api.running[id].meta.State = NodeStopped
			api.runningLk.Unlock()

			//logfile.Close()
			//errlogfile.Close()

			//close(mux.stop)
		},
	}
	api.runningLk.Unlock()

	time.Sleep(time.Millisecond * 750) // TODO: Something less terrible

	return info, nil
}

func (api *api) SpawnStorage(fullNodeRepo string) (nodeInfo, error) {
	dir, err := ioutil.TempDir(os.TempDir(), "lotus-storage-")
	if err != nil {
		return nodeInfo{}, err
	}

	errlogfile, err := os.OpenFile(dir+".err.log", os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return nodeInfo{}, err
	}
	logfile, err := os.OpenFile(dir+".out.log", os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return nodeInfo{}, err
	}

	initArgs := []string{"init"}
	if fullNodeRepo == api.running[1].meta.Repo {
		initArgs = []string{"init", "--actor=t0101", "--genesis-miner"}
	}

	id := atomic.AddInt32(&api.cmds, 1)
	cmd := exec.Command("./lotus-storage-miner", initArgs...)
	cmd.Stderr = io.MultiWriter(os.Stderr, errlogfile)
	cmd.Stdout = io.MultiWriter(os.Stdout, logfile)
	cmd.Env = []string{"LOTUS_STORAGE_PATH=" + dir, "LOTUS_PATH=" + fullNodeRepo}
	if err := cmd.Run(); err != nil {
		return nodeInfo{}, err
	}

	time.Sleep(time.Millisecond * 300)

	mux := newWsMux()

	cmd = exec.Command("./lotus-storage-miner", "run", "--api", fmt.Sprintf("%d", 2500+id))
	cmd.Stderr = io.MultiWriter(os.Stderr, errlogfile, mux.errpw)
	cmd.Stdout = io.MultiWriter(os.Stdout, logfile, mux.outpw)
	cmd.Env = []string{"LOTUS_STORAGE_PATH=" + dir, "LOTUS_PATH=" + fullNodeRepo}
	if err := cmd.Start(); err != nil {
		return nodeInfo{}, err
	}

	info := nodeInfo{
		Repo:    dir,
		ID:      id,
		ApiPort: 2500 + id,
		State:   NodeRunning,

		FullNode: fullNodeRepo,
		Storage:  true,
	}

	api.runningLk.Lock()
	api.running[id] = &runningNode{
		cmd:  cmd,
		meta: info,

		mux: mux,
		stop: func() {
			cmd.Process.Signal(os.Interrupt)
			cmd.Process.Wait()

			api.runningLk.Lock()
			api.running[id].meta.State = NodeStopped
			api.runningLk.Unlock()

			//logfile.Close()
			//errlogfile.Close()

			//close(mux.stop)
		},
	}
	api.runningLk.Unlock()

	time.Sleep(time.Millisecond * 750) // TODO: Something less terrible

	return info, nil
}

func (api *api) RestartNode(id int32) (nodeInfo, error) {
	api.runningLk.Lock()
	defer api.runningLk.Unlock()
	nd, ok := api.running[id]

	if !ok {
		return nodeInfo{}, xerrors.New("node not found")
	}

	if nd.meta.State != NodeStopped {
		return nodeInfo{}, xerrors.New("node not stopped")
	}

	var cmd *exec.Cmd
	if nd.meta.Storage {
		cmd = exec.Command("./lotus-storage-miner", "run", "--api", fmt.Sprintf("%d", 2500+id))
	} else {
		cmd = exec.Command("./lotus", "daemon", "--api", fmt.Sprintf("%d", 2500+id))
	}

	cmd.Stderr = nd.cmd.Stderr // recycle old vars
	cmd.Stdout = nd.cmd.Stdout
	cmd.Env = nd.cmd.Env

	if err := cmd.Start(); err != nil {
		return nodeInfo{}, err
	}

	nd.cmd = cmd

	nd.stop = func() {
		cmd.Process.Signal(os.Interrupt)
		cmd.Process.Wait()

		api.runningLk.Lock()
		api.running[id].meta.State = NodeStopped
		api.runningLk.Unlock()

		//logfile.Close()
		//errlogfile.Close()

		//close(mux.stop)
	}

	nd.meta.State = NodeRunning

	time.Sleep(time.Millisecond * 750) // TODO: Something less terrible

	return nd.meta, nil
}