package main

import (
	"crypto/tls"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"time"

	"gopkg.in/dutchcoders/goftp.v1"
)

var (
	host  string = ""
	port  string = ""
	user  string = ""
	pswd  string = ""
	loc   string = ""
	rmt   string = ""
	act   string = ""
	retry int    = 0
	intrv int    = 0
)
var ftp *goftp.FTP

func init() {
	//flag.StringVar(&host, "h", "203.93.100.58", "FTPS Server Address")
	flag.StringVar(&host, "h", "10.60.254.52", "FTPS Server Address")
	flag.StringVar(&port, "o", "3031", "FTPS Server Port")
	flag.StringVar(&user, "u", "idte", "Username")
	flag.StringVar(&pswd, "p", "wistronTE2022", "Password")
	flag.StringVar(&loc, "L", "./", "Local Path")
	flag.StringVar(&rmt, "R", "/", "Remote Path")
	flag.StringVar(&act, "A", "list", "Action: list,upld,dnld")
	flag.IntVar(&intrv, "I", 10, "Retry Interval (Seconds)")
	flag.IntVar(&retry, "T", 5, "Retry Times")
	flag.Parse()
}

func main() {
	log.SetFlags(log.Ldate | log.Ltime)
	log.SetOutput(os.Stdout)

	if host == "" || port == "" || user == "" || pswd == "" {
		log.Println("[ERR]  Invalid Arguments.")
		os.Exit(1)
	}

	err := _InitFTP()
	if err != nil {
		os.Exit(1)
	}

	switch act {
	case "list":
		_List(rmt)
	case "dnld":
		_Download(loc, rmt)
	case "upld":
		_Upload(loc, rmt)
	default:
		_List(rmt)
	}
}

func _InitFTP() error {
	var err error
	for i := 0; i <= retry; i++ {
		if i > 0 {
			fmt.Println("[###]  Retrying Connect ...")
		}
		ftp, err = goftp.Connect(fmt.Sprintf("%s:%s", host, port))
		if err != nil {
			log.Printf("[ERR]  %s\r\n", err.Error())
			continue
		}
		defer ftp.Close()

		cfg := tls.Config{
			InsecureSkipVerify: true,
			MinVersion:         tls.VersionTLS10,
		}

		err = ftp.AuthTLS(&cfg)
		if err != nil {
			log.Printf("[ERR]  %s\r\n", err.Error())
			continue
		}

		err = ftp.Login(user, pswd)
		if err != nil {
			log.Printf("[ERR]  %s\r\n", err.Error())
			continue
		}
	}
	if err != nil {
		log.Printf("[***]  %s; %s\r\n", rmt, loc)
		return err
	}
	return nil
}

func _List(path string) {
	files, err := ftp.List(path)
	if err != nil {
		log.Printf("[ERR]  %s\r\n", err.Error())
		return
	}
	for _, f := range files {
		fmt.Print(f)
	}
}

func _Download(local, remote string) {
	log.Printf("[MSG]  Downloading: %s\r\n", remote)
	var err error
	base := filepath.Base(remote)
	for i := 0; i <= retry; i++ {
		if i > 0 {
			time.Sleep(time.Second * time.Duration(intrv))
			log.Printf("[###]  Retrying Download %d/%d: %s\r\n", i, retry, remote)
		}
		_, err = ftp.Retr(remote, func(rdr io.Reader) error {
			wtr, oserr := os.Create(local + base)
			if oserr != nil {
				return oserr
			}
			defer wtr.Close()
			_, ioerr := io.Copy(wtr, rdr)
			if ioerr != nil {
				return ioerr
			}
			return nil
		})
		if err == nil {
			break
		}
	}
	if err != nil {
		log.Printf("[***]  %s; %s\r\n", remote, local)
		log.Printf("[ERR]  Download NG: %s; %s\r\n", remote, err.Error())
		return
	}
	log.Printf("[MSG]  Download OK: %s\r\n", remote)
}

func _Upload(local, remote string) {
	log.Printf("[MSG]  Uploading: %s\r\n", local)
	var err error
	for i := 0; i <= retry; i++ {
		if i > 0 {
			time.Sleep(time.Second * time.Duration(intrv))
			log.Printf("[###]  Retrying Upload %d/%d: %s\r\n", i, retry, local)
		}
		err = ftp.Cwd(remote)
		if err != nil {
			continue
		}
		err = ftp.Upload(local)
		if err != nil {
			continue
		}
		break
	}
	if err != nil {
		log.Printf("[***]  %s; %s\r\n", remote, local)
		log.Printf("[ERR]  Upload NG: %s; %s\r\n", local, err.Error())
		return
	}
	log.Printf("[MSG]  Upload OK: %s\r\n", local)
}
