package main

import (
	"database/sql"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"html/template"
	"log"
	"net"
	"net/http"
	"os"
	"io"
	"strings"
	"bytes"
	"errors"
	_ "modernc.org/sqlite"
)

type Config struct {
	Port uint
	SiteName string
}

type page struct {
	Title string
}

type xmlDomain struct {
	authority string
	registryType string
	entityClass string
	entityName string
}

var C = Config{}
var db *sql.DB

func main() {	
	var err error

	domain := "snuff.fi"
	av, err := checkDomainAvailability(domain)
	if err != nil {
		log.Fatal(err)
		os.Exit(1)
	}
	fmt.Printf("Domain '%s' available: %t\n", domain, av)
	return
	
	// Load config
	fmt.Println("Loading config.json")
	cfgfile, err := os.Open("config.json")
	if err != nil {
		log.Fatal("Error reading config.json!")
		os.Exit(1)
	}
	defer cfgfile.Close()
	
	decoder := json.NewDecoder(cfgfile)
	//Config := Config{}
	err = decoder.Decode(&C)
	if err != nil {
		log.Fatal("Can't decode config.json!")
		os.Exit(1)
	}



	
	db, err = sql.Open("sqlite", "database.db")
	if err != nil {
		log.Fatal(err)
		os.Exit(1)
	}

	fmt.Printf("Starting listener on :%v", C.Port)
	http.Handle("/static/", http.FileServer(http.Dir("./")))
	http.HandleFunc("/", httpRootHandler)
	err = http.ListenAndServe(fmt.Sprintf(":%v", C.Port), nil)
	if err != nil {
		log.Fatal(err)
	}

	db.Close()
}

func httpRootHandler(w http.ResponseWriter, r *http.Request) {
	t, _ := template.ParseFiles("index.html")
	// var err := do_get_stuff()
	// if err != nil {
	// 	log.Fatal(err)
	// 	http.Error(w, http.StatusText(500), 500)
	// 	return
	// }

	
	
	var p page
	p.Title = C.SiteName;
	t.Execute(w, p)
}

func checkDomainAvailability(domain string) (bool, error) {
	dasHost := "das.domain.fi";
	dasPort := 715
	dasAddr := fmt.Sprintf("%s:%d", dasHost, dasPort)
	request := fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?>
<iris1:request xmlns:iris1="urn:ietf:params:xml:ns:iris1">
<iris1:searchSet>
<iris1:lookupEntity registryType="dchk1" entityClass="domain-name" entityName="%s"/>
</iris1:searchSet>
</iris1:request>`, domain)
	
	conn, err := net.Dial("udp", dasAddr)
	if err != nil {
		log.Fatal(err)
		return false, err
	}
	defer conn.Close()

	fmt.Fprintf(conn, request)
	response := make([]byte, 1024)
	_, err = conn.Read(response)
	if err != nil {
		log.Fatal(err)
	}

	response = bytes.Trim(response, "\x00")

	// Response can be:
	// - active: already in use
	// - available: go ahead and grab it while you can
	// - invalid: malformed request: maybe check domain has .fi in the end
	isAvailable, err := xmlCheckTag(string(response), "available")
	if err != nil {
		log.Fatal(err)
		return false, err
	} else if isAvailable {
		// Domain is available
		return true, nil
	}
	
	isActive, err := xmlCheckTag(string(response), "active")
	if err != nil {
		log.Fatal(err)
		return false, err
	} else if isActive {
		// Domain is already registered taken
		return false, nil
	}
	
	isInvalid, err := xmlCheckTag(string(response), "invalid")
	if err != nil {
		log.Fatal(err)
		return false, err
	} else if isInvalid {
		// We got bonked for our request
		return false, errors.New("Request or domain invalid")
	}

	// Execution should not lead here but hey might as well error it out
	return false, errors.New("Error: Status field not found")
}

func xmlCheckTag(src, tag string) (bool, error) {
	// Thanks icza for your stackoverflow
	// https://stackoverflow.com/a/51649834
	decoder := xml.NewDecoder(strings.NewReader(src))
	for {
		t, err := decoder.Token()
		if err != nil {
			if err == io.EOF {
				return false, nil
			}
			return false, err
		}
		if se, ok := t.(xml.StartElement); ok {
			if se.Name.Local == tag {
				return true, nil
			}
		}
	}
}