package main

import (
	"encoding/json"
	"fmt"
	"git.swzry.com/zry/pathutils"
	"gopkg.in/natefinch/lumberjack.v2"
	"io"
	"io/ioutil"
	"os/exec"
	"sync"
)

var ConfigData ConfigJSON
var FrontendShellLogger *lumberjack.Logger
var BackendShellLogger *lumberjack.Logger
var mainWaitGroup sync.WaitGroup

func main() {
	fmt.Println("zDWSI Launcher Start.")
	CheckInit()
	LoadJSON()
	StartFrontend()
	StartBackend()
	fmt.Println("Launcher Sleeping.")
	RoutineSleep()
}

func CheckInit() {
	pe, err := pathutils.PathExists("/data/config.json")
	if err == nil && pe {
		return
	}
	fmt.Println("Initializing Container...")
	pathutils.MkDirIfNotExist("/data/frontend/", 0777)
	pathutils.MkDirIfNotExist("/data/backend/", 0777)
	pathutils.MkDirIfNotExist("/data/wwwroot/", 0777)
	pathutils.MkDirIfNotExist("/data/logs/", 0777)
	pathutils.CopyFile("/data/config.json", "/rel/config.default.json")
	pathutils.CopyFile("/data/wwwroot/index.html", "/rel/index.default.html")
	pathutils.CopyFile("/data/frontend/zDWSIFrontend", "/rel/zDWSIFrontend_linux")
	fmt.Println("Container Initialized. Please Edit 'config.json'.")
}

func LoadJSON() {
	fmt.Println("Load Config '/data/config.json'")
	jdata, err := ioutil.ReadFile("/data/config.json")
	if err != nil {
		fmt.Println("failed read file 'config.json': ", err)
		Dead()
	}
	err = json.Unmarshal(jdata, &ConfigData)
	if err != nil {
		fmt.Println("failed parse 'config.json': ", err)
		Dead()
	}
	fmt.Println("Config Loaded.")
}

func Dead() {
	fmt.Println("Program Dead By Fatal Error. Please Restart.")
	FailureServer()
}

func RoutineSleep() {
	mainWaitGroup.Wait()
	fmt.Println("All GoRoutine Stopped. Please Restart.")
	FailureServer()
}

func InitLogger(cfg LumberjackLoggerJSON) *lumberjack.Logger {
	ljl := &lumberjack.Logger{
		Filename:   cfg.FileName,
		MaxSize:    cfg.MaxSize,
		MaxAge:     cfg.MaxAge,
		MaxBackups: cfg.MaxBackups,
		LocalTime:  cfg.UseLocalTime,
		Compress:   cfg.Compress,
	}
	return ljl
}

func StartFrontend() {
	if !ConfigData.Frontend.Enable {
		fmt.Println("Frontend Not Enable.")
		return
	}
	pe, err := pathutils.PathExists(ConfigData.Frontend.EntryPoint)
	if err != nil || !pe {
		fmt.Println("frontend entrypoint not exists")
		return
	}
	FrontendShellLogger = InitLogger(ConfigData.Frontend.ShellLog)
	mainWaitGroup.Add(1)
	go func() {
		runenv := make([]string, 0)
		runenv = append(runenv, makeEnvMember("listen", ConfigData.Frontend.ListenAddr))
		runenv = append(runenv, makeEnvMember("wwwroot", ConfigData.Frontend.WWWRoot))
		runenv = append(runenv, makeEnvMember("backend_prefix", ConfigData.Frontend.BackendProxy.Prefix))
		runenv = append(runenv, makeEnvMember("backend_target", ConfigData.Frontend.BackendProxy.ProxyTaret))
		err = StartProcess(ConfigData.Frontend.EntryPoint, runenv, FrontendShellLogger)
		if err != nil {
			fmt.Println("failed start frontend or frontend exited with error: ", err)
		}
		mainWaitGroup.Done()
	}()
}

func makeEnvMember(key string, value string) string {
	return fmt.Sprintf("%s=%s", key, value)
}

func StartBackend() {
	if !ConfigData.Backend.Enable {
		fmt.Println("Backend Not Enable.")
		return
	}
	pe, err := pathutils.PathExists(ConfigData.Backend.EntryPoint)
	if err != nil || !pe {
		fmt.Println("backend entrypoint not exists")
		return
	}
	BackendShellLogger = InitLogger(ConfigData.Backend.ShellLog)
	mainWaitGroup.Add(1)
	go func() {
		runenv := make([]string, 0)
		runenv = append(runenv, makeEnvMember("listen", ConfigData.Backend.ListenAddr))
		err = StartProcess(ConfigData.Backend.EntryPoint, runenv, BackendShellLogger)
		if err != nil {
			fmt.Println("failed start backend or backend exited with error: ", err)
		}
		mainWaitGroup.Done()
	}()
}

func StartProcess(entry string, runenv []string, logger io.Writer) error {
	cmd := exec.Command(entry)
	cmd.Env = runenv
	pipe_r, pipe_w := io.Pipe()
	cmd.Stdout = pipe_w
	cmd.Stderr = pipe_w
	go func() {
		io.Copy(logger, pipe_r)
	}()
	err := cmd.Start()
	if err != nil {
		return err
	}
	err = cmd.Wait()
	return err
}
