package main

import (
	"bytes"
	"context"
	"encoding/base64"
	"errors"
	"fmt"
	"github.com/boombuler/barcode"
	"github.com/boombuler/barcode/code128"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"html/template"
	"image/png"
	"io/ioutil"
	"log"
	"main/pb"
	"main/wkhtmltopdf"
	"math/rand"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"
)

func main() {
	file := "jnt_sg"
	templateFile := "../../awb-templates/" + file + ".html"
	timeName := fmt.Sprintf("%d", time.Now().Unix())
	total := 2

	fmt.Println(templateFile, total, timeName)
	fmt.Println()

	t, err := template.ParseFiles(templateFile)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Template", t.Name())
	fmt.Println()

	files := make([]string, total)
	wg := sync.WaitGroup{}
	wg.Add(total)
	for i := 0; i < total; i++ {
		go func(i int) {
			defer wg.Done()
			data := createSampleData("860003990742" + strconv.Itoa(i+1))
			buf := new(bytes.Buffer)
			if err := t.Execute(buf, data); err != nil {
				log.Println("Error Execute", err)
			}
			tempFile, err := ioutil.TempFile("tmp", "pdf-*.html")
			if err != nil {
				log.Fatal(err)
			}

			if _, err := tempFile.Write(buf.Bytes()); err != nil {
				log.Fatal(err)
			}

			if err := tempFile.Close(); err != nil {
				log.Println("Error Close", tempFile.Name())
			}

			fmt.Println("Created", tempFile.Name())
			files[i] = tempFile.Name()
		}(i)
	}
	wg.Wait()
	fmt.Println("Total file", len(files))
	fmt.Println()

	t1 := time.Now()
	generateByWebkit(files, "pdf/"+file+".pdf")
	d1 := time.Since(t1)

	// Remove temp files
	fmt.Println()
	fmt.Println("Removing temp files")
	wg = sync.WaitGroup{}
	wg.Add(len(files))
	for _, path := range files {
		go func(path string) {
			wg.Done()
			if err := os.Remove(path); err != nil {
				log.Println(path, err)
			} else {
				fmt.Println("Removed", path)
			}
		}(path)
	}
	wg.Wait()

	fmt.Println()
	fmt.Println("Processing Time - generateByWebkit", d1)
}

func generateByCmd(files []string, pdfFile string) {
	var outb, errb bytes.Buffer
	cmd := exec.Command("/bin/sh", "-c", "docker-compose run --rm pdf --version")
	cmd.Stdout = &outb
	cmd.Stderr = &errb
	err := cmd.Run()
	if err != nil {
		log.Fatalln("cmd err", err)
	}
	log.Println("out:", outb.String(), "err:", errb.String())
}

func generateByGRPC(files []string, pdfFile string) {

	conn, err := grpc.Dial("localhost:50003", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	c := pb.NewWkhtmltopdfServiceClient(conn)

	// Contact the server and print out its response.
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	var data [][]byte
	for _, file := range files {
		content, _ := os.ReadFile(file)
		data = append(data, content)
	}
	r, err := c.Generate(ctx, &pb.GenerateRequest{
		Title: "Title",
		Data:  data,
	})
	if err != nil {
		log.Fatalf("could not greet: %v", err)
	}
	log.Printf("URL: %s", r.Url)
}

// https://github.com/SebastiaanKlippert/go-wkhtmltopdf
func generateByWebkit(files []string, pdfFile string) {
	fmt.Println("----------------")
	fmt.Println("generateByDirect")
	fmt.Println()

	wkhtmltopdf.SetPath("/bin/sh")
	pdfGenerator, err := wkhtmltopdf.NewPDFGenerator()
	if err != nil {
		log.Fatal(err)
	}

	// Set global options
	pdfGenerator.Title.Set("Shipping Labels")
	pdfGenerator.Dpi.Set(96)
	pdfGenerator.PageWidth.Set(100)
	pdfGenerator.PageHeight.Set(180)
	pdfGenerator.MarginTop.Set(0)
	pdfGenerator.MarginBottom.Set(0)
	pdfGenerator.MarginLeft.Set(0)
	pdfGenerator.MarginRight.Set(0)
	pdfGenerator.Orientation.Set(wkhtmltopdf.OrientationPortrait)

	//for _, path := range files {
	//	pdfGenerator.AddPage(wkhtmltopdf.NewPage(path))
	//}

	pdfGenerator.AddPage(wkhtmltopdf.NewPage("https://hub.docker.com/r/michaelperrin/wkhtmltopdf"))

	outFile, err := os.Create(pdfFile)
	if err != nil {
		fmt.Println("Error Create", err)
	}

	fmt.Println("Step 1")

	defer func(outFile *os.File) {
		if err := outFile.Close(); err != nil {
			log.Fatal(err)
		}
	}(outFile)

	fmt.Println("Step 2")

	pdfGenerator.SetOutput(outFile)

	fmt.Println()

	// Create PDF document in internal buffer
	if err := pdfGenerator.Create(); err != nil {
		log.Fatal(err)
	}

	// Write buffer contents to file on disk
	//if err := Generator.WriteFile(pdfFile); err != nil {
	//	log.Fatal(err)
	//}

	fmt.Println("Done")
}

func generateBarcode(str string) (string, error) {
	// https://www.socketloop.com/tutorials/golang-generate-code128-barcode
	encodedBarcode, err := code128.Encode(str)
	if err != nil {
		return "", err
	}

	resizedBarcode, err := barcode.Scale(encodedBarcode, 800, 80)
	if err != nil {
		return "", err
	}

	// encode the barcode as png
	var buf bytes.Buffer
	if err := png.Encode(&buf, resizedBarcode); err != nil {
		return "", err
	}

	pngBytes, err := ioutil.ReadAll(&buf)
	if err != nil {
		return "", err
	}

	return "data:image/png;base64," + base64.StdEncoding.EncodeToString(pngBytes), nil
}

func parseTemplate(templateFileName string, data interface{}) (string, error) {
	//https://www.codegrepper.com/code-examples/go/golang+template+loop
	t, err := template.ParseFiles(templateFileName)
	if err != nil {
		return "", err
	}
	buf := new(bytes.Buffer)
	if err = t.Execute(buf, data); err != nil {
		return "", err
	}
	return buf.String(), nil
}

func createSampleData(TrackingNumber string) map[string]interface{} {
	//fmt.Println(TrackingNumber)
	//TrackingNumber := "860003990742"
	barcodeBase64, err := generateBarcode(TrackingNumber)
	if err != nil {
		log.Fatal(err)
	}

	//fmt.Println(barcodeBase64)
	SortingCodeRand := "16B-" + strconv.Itoa(randNum(10000, 99999)) + "-06"
	//SortingCode := regexp.MustCompile(`(\w+)-(\w+)-(\w+)`).FindStringSubmatch(SortingCodeRand) + "-06")
	SortingCode := strings.Split(SortingCodeRand, "-")
	if len(SortingCode) < 3 {
		log.Fatal(errors.New("invalid SortingCode"))
	}

	SortingLineCode := string('A'+rune(rand.Intn(26))) + strconv.Itoa(randNum(10, 99))

	ReceiverPhone := "06138" + strconv.Itoa(randNum(10000, 99999))
	n := len(ReceiverPhone)
	if m := regexp.MustCompile(`^(\d{3})(\d+)(\d{3})$`).FindStringSubmatch(ReceiverPhone); len(m) > 3 {
		ReceiverPhone = m[1] + strings.Repeat("*", len(m[2])) + m[3]
	} else if n > 3 {
		ReceiverPhone = strings.Repeat("*", n-3) + ReceiverPhone[n-3:n]
	}

	return map[string]interface{}{
		"TrackingNumber":  TrackingNumber,
		"Barcode":         template.URL(barcodeBase64), // https://i.ibb.co/jLj941J/barcode.png
		"SortingCode1":    SortingCode[0],
		"SortingCode2":    SortingCode[1],
		"SortingCode3":    SortingCode[2],
		"SortingLineCode": SortingLineCode,
		"dstStoreName":    "PLP_SP-พลบัพลา",
		"SenderName":      "Suwicha Tawongsa",
		"SenderPhone":     "0613873349",
		"SenderAddress":   "875/22 Chaengwatthana Road, Thung Song Hong sub district, Laksi District, Bangkok, Thailand 42356",
		"ReceiverName":    "Pickyuak Valenciana",
		"ReceiverPhone":   ReceiverPhone,
		"ReceiverAddress": "875/22 Chaengwatthana Road, Thung Song Hong sub district, Laksi District, Bangkok, Thailand 42356",
		"Items": []interface{}{
			map[string]interface{}{
				"Company": "John Lewis",
				"Contact": "Maria Anders",
				"Country": "Germany",
			},
			map[string]interface{}{
				"Company": "John Lewis",
				"Contact": "Maria Anders",
				"Country": "Germany",
			},
			map[string]interface{}{
				"Company": "John Lewis",
				"Contact": "Maria Anders",
				"Country": "Germany",
			},
		},
	}
}

func randNum(min int, max int) int {
	return rand.Intn(max-min) + min
}
