//go:build !cgo && upgrade && ignore
// +build !cgo,upgrade,ignore

package main

import (
	"archive/zip"
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
)

func download(prefix string) (url string, content []byte, err error) {
	resp, err := http.Get("https://www.sqlite.org/download.html")
	if err != nil {
		return "", nil, err
	}
	defer resp.Body.Close()

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", nil, err
	}
	html := string(b)
	start := strings.Index(html, `<!-- Download product data for scripts to read`)
	if start == -1 {
		return "", nil, errors.New("Unable to find download section on sqlite.org")
	}
	html = html[start:]
	end := strings.Index(html, `-->`)
	if end == -1 {
		return "", nil, errors.New("Unable to find download section on sqlite.org")
	}
	html = html[:end]
	for _, line := range strings.Split(html, "\n") {
		if strings.Contains(line, prefix) {
			if tok := strings.Split(line, ","); len(tok) >= 5 {
				url = fmt.Sprintf("https://www.sqlite.org/%s", tok[2])
				break
			}
		}
	}

	if url == "" {
		return "", nil, fmt.Errorf("Unable to find prefix '%s' on sqlite.org", prefix)
	}

	fmt.Printf("Downloading %v\n", url)
	resp, err = http.Get(url)
	if err != nil {
		return "", nil, err
	}

	// Ready Body Content
	content, err = ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		return "", nil, err
	}

	return url, content, nil
}

func mergeFile(src string, dst string) error {
	defer func() error {
		fmt.Printf("Removing: %s\n", src)
		err := os.Remove(src)

		if err != nil {
			return err
		}

		return nil
	}()

	// Open destination
	fdst, err := os.OpenFile(dst, os.O_APPEND|os.O_WRONLY, 0666)
	if err != nil {
		return err
	}
	defer fdst.Close()

	// Read source content
	content, err := ioutil.ReadFile(src)
	if err != nil {
		return err
	}

	// Add Additional newline
	if _, err := fdst.WriteString("\n"); err != nil {
		return err
	}

	fmt.Printf("Merging: %s into %s\n", src, dst)
	if _, err = fdst.Write(content); err != nil {
		return err
	}

	return nil
}

func main() {
	fmt.Println("Go-SQLite3 Upgrade Tool")

	_, file, _, ok := runtime.Caller(0)
	if !ok {
		log.Fatal("could not get current file info")
	}
	basedir := filepath.Dir(filepath.Dir(file))

	// Download Amalgamation
	_, amalgamation, err := download("sqlite-amalgamation-")
	if err != nil {
		log.Fatalf("Failed to download: sqlite-amalgamation; %s", err)
	}

	// Download Source
	//_, source, err := download("sqlite-src-")
	//if err != nil {
	//	log.Fatalf("Failed to download: sqlite-src; %s", err)
	//}

	// Create Amalgamation Zip Reader
	rAmalgamation, err := zip.NewReader(bytes.NewReader(amalgamation), int64(len(amalgamation)))
	if err != nil {
		log.Fatal(err)
	}

	// Create Source Zip Reader
	//rSource, err := zip.NewReader(bytes.NewReader(source), int64(len(source)))
	//if err != nil {
	//	log.Fatal(err)
	//}

	// Extract Amalgamation
	for _, zf := range rAmalgamation.File {
		var f *os.File
		switch path.Base(zf.Name) {
		case "sqlite3.c":
			f, err = os.Create(filepath.Join(basedir, "sqlite3-binding.c"))
		case "sqlite3.h":
			f, err = os.Create(filepath.Join(basedir, "sqlite3-binding.h"))
		case "sqlite3ext.h":
			f, err = os.Create(filepath.Join(basedir, "sqlite3ext.h"))
		default:
			continue
		}
		if err != nil {
			log.Fatal(err)
		}
		zr, err := zf.Open()
		if err != nil {
			log.Fatal(err)
		}

		_, err = io.WriteString(f, "#ifndef USE_LIBSQLITE3\n")
		if err != nil {
			zr.Close()
			f.Close()
			log.Fatal(err)
		}
		scanner := bufio.NewScanner(zr)
		for scanner.Scan() {
			text := scanner.Text()
			if text == `#include "sqlite3.h"` {
				text = `#include "sqlite3-binding.h"
#ifdef __clang__
#define assert(condition) ((void)0)
#endif
`
			}
			_, err = fmt.Fprintln(f, text)
			if err != nil {
				break
			}
		}
		err = scanner.Err()
		if err != nil {
			zr.Close()
			f.Close()
			log.Fatal(err)
		}
		_, err = io.WriteString(f, "#else // USE_LIBSQLITE3\n // If users really want to link against the system sqlite3 we\n// need to make this file a noop.\n #endif")
		if err != nil {
			zr.Close()
			f.Close()
			log.Fatal(err)
		}
		zr.Close()
		f.Close()
		fmt.Printf("Extracted: %v\n", filepath.Base(f.Name()))
	}

	//Extract Source
	//for _, zf := range rSource.File {
	//	var f *os.File
	//	switch path.Base(zf.Name) {
	//	case "userauth.c":
	//		f, err = os.Create("../userauth.c")
	//	case "sqlite3userauth.h":
	//		f, err = os.Create("../userauth.h")
	//	default:
	//		continue
	//	}
	//	if err != nil {
	//		log.Fatal(err)
	//	}
	//	zr, err := zf.Open()
	//	if err != nil {
	//		log.Fatal(err)
	//	}

	//	_, err = io.Copy(f, zr)
	//	if err != nil {
	//		log.Fatal(err)
	//	}

	//	zr.Close()
	//	f.Close()
	//	fmt.Printf("extracted %v\n", filepath.Base(f.Name()))
	//}

	// Merge SQLite User Authentication into amalgamation
	//if err := mergeFile("../userauth.c", "../sqlite3-binding.c"); err != nil {
	//	log.Fatal(err)
	//}
	//if err := mergeFile("../userauth.h", "../sqlite3-binding.h"); err != nil {
	//	log.Fatal(err)
	//}

	os.Exit(0)
}
