package main

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"strings"
	"unicode"
)

type Submodule struct {
	Name string
	Path string
	Url  string

	Update  string
	Branch  string
	Ignore  string
	Shallow string
}

var SyntaxError error = errors.New("Syntax error")

func (sub *Submodule) parseSubmoduleName(line string) error {
	line = strings.TrimSpace(line)
	if line[0:10] != "submodule " {
		return SyntaxError
	}

	r := strings.NewReader(line[10:])

	ch, _, err := r.ReadRune()
	for ; ; ch, _, err = r.ReadRune() {
		if err != nil {
			return fmt.Errorf("%c %d", ch, 32)
		}
		if !unicode.IsSpace(ch) {
			break
		}
	}
	if ch != '"' {
		return fmt.Errorf("%c %d", ch, 36)
		//		return SyntaxError
	}

	var b strings.Builder
	for ch, _, err = r.ReadRune(); ch != '"'; ch, _, err = r.ReadRune() {
		if err != nil {
			return fmt.Errorf("%c %d", ch, 32)
		}
		b.WriteRune(ch)
	}

	if ch != '"' {
		return fmt.Errorf("%c %d", ch, 45)
		//		return SyntaxError
	}

	for ch, _, err = r.ReadRune(); unicode.IsSpace(ch); ch, _, err = r.ReadRune() {
		if err != nil {
			return fmt.Errorf("%d %w", 38, err)
		}
	}

	if ch != ']' || err != nil {
		return fmt.Errorf("%c %d, %w", ch, 50, err)
		//		return SyntaxError
	}

	sub.Name = b.String()

	for ch, _, err = r.ReadRune(); ; ch, _, _ = r.ReadRune() {
		if err == io.EOF {
			return nil
		}
		if !unicode.IsSpace(ch) {
			return SyntaxError
		}
	}
}

func (s *Submodule) parseKeyValue(line string) error {
	eqLoc := strings.Index(line, "=")
	if eqLoc < 0 || eqLoc == len(line)-1 {
		return SyntaxError
	}

	key := strings.ToLower(strings.TrimSpace(line[0:eqLoc]))
	val := strings.TrimSpace(line[eqLoc+1:])

	if len(val) == 0 {
		return SyntaxError
	}

	switch key {
	case "path":
		s.Path = val
	case "url":
		s.Url = val
	case "shallow":
		s.Shallow = val
	case "ignore":
		s.Ignore = val
	case "branch":
		s.Branch = val
	case "update":
		s.Update = val
	default:
		return SyntaxError
	}

	return nil
}

func ParseSubmodulesFile(reader io.Reader) ([]Submodule, error) {
	data, err := io.ReadAll(reader)
	if err != nil {
		return nil, err
	}

	var sub *Submodule
	ret := []Submodule{}

	lines := bytes.Split(data, []byte("\n"))
	for _, l := range lines {
		line := string(bytes.TrimSpace(l))
		if len(line) == 0 {
			continue
		}
		if line[0] == '[' {
			if sub != nil {
				ret = append(ret, *sub)
			}
			sub = &Submodule{}
			if err := sub.parseSubmoduleName(line[1:]); err != nil {
				return nil, err
			}
		} else if sub == nil {
			return nil, SyntaxError
		} else {
			if err := sub.parseKeyValue(line); err != nil {
				return nil, err
			}
		}
	}
	if sub != nil {
		ret = append(ret, *sub)
	}
	return ret, nil
}

func writeValue(out io.Writer, key, value string) {
	if len(value) > 0 {
		out.Write([]byte(fmt.Sprintf("\t%s = %s\n", key, value)))
	}
}

func WriteSubmodules(subs []Submodule, out io.Writer) error {
	for _, sub := range subs {
		if len(sub.Name) < 1 {
			return fmt.Errorf("Submodule with no name.")
		}
		out.Write([]byte(fmt.Sprintf("[submodule \"%s\"]\n", sub.Name)))
		writeValue(out, "path", sub.Path)
		writeValue(out, "url", sub.Url)
		writeValue(out, "branch", sub.Branch)
		writeValue(out, "ignore", sub.Ignore)
		writeValue(out, "shallow", sub.Shallow)
		writeValue(out, "update", sub.Update)
	}
	return nil
}
