package main

import (
	"encoding/json"
	"errors"
	"io"
	"log"
	"os"
	"os/exec"
	"path"
	"time"
)

func copy(src, dst string) error {
	fi, err := os.Stat(src)
	if err != nil {
		return err
	}
	inmode := fi.Mode()

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

	out, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer out.Close()

	copied, err := io.Copy(out, in)
	if err != nil {
		return err
	}
	if copied < fi.Size() {
		return errors.New("copy not completed")
	}
	if err := out.Sync(); err != nil {
		return err
	}

	if err := out.Chmod(inmode); err != nil {
		return err
	}

	return nil
}

func main() {
	logfile, _ := os.OpenFile("replacer.log", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
	defer logfile.Close()
	stdlog := log.New(logfile, "", log.LstdFlags)

	args := os.Args
	// args[1] : 나를 시작한 pid. pid가 종료될 때 까지 기다림
	// args[2] : target 폴더
	// args[3:] : 다시 시작할 때 넘겨줄 arguments(프로세스 이름 포함)
	stdlog.Println(args)

	for {
		stdlog.Println("wait for terminating of", args[3])
		cmd := exec.Command("ps", "-p", args[1])
		if err := cmd.Run(); err != nil {
			break
		}
		time.Sleep(time.Second)
	}

	stdlog.Println("target is terminated")

	selfext, _ := os.Executable()
	selfext = path.Base(selfext)
	nextArgs := args[4:]
	entries, _ := os.ReadDir(args[2])
	for _, ent := range entries {
		if ent.Name() == selfext {
			continue
		}

		if ent.IsDir() {
			if err := os.MkdirAll(ent.Name(), 0775); err != nil {
				stdlog.Fatal(err)
			}
		} else {
			if ent.Name() == "@args" {
				var tempArgs []string
				argfile, _ := os.Open(path.Join(args[2], ent.Name()))
				dec := json.NewDecoder(argfile)
				if dec.Decode(&tempArgs) == nil {
					nextArgs = tempArgs
				}
			} else {
				err := copy(path.Join(args[2], ent.Name()), ent.Name())
				if err != nil {
					stdlog.Println("copy failed :", path.Join(args[2], ent.Name()), ent.Name())
					stdlog.Fatal(err)
				} else {
					stdlog.Println("file copied :", path.Join(args[2], ent.Name()), ent.Name())
				}
			}
		}
	}

	err := os.RemoveAll(args[2])
	if err != nil {
		stdlog.Println("os.RemoveAll failed :", args[2], err)
	}

	err = os.Chmod(args[3], 0775)
	if err != nil {
		stdlog.Println("os.Chmod failed :", err)
	}

	stdlog.Println("exec.Command :", args)
	cmd := exec.Command(args[3], nextArgs...)
	cmd.Start()
}
