package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"strings"
)

// XXX:
// This struct is temporarily not in use while an issue with marshalling
// []ToRead in the Settings struct gets worked out. In the meantime a
// []string is being used with the string in each containing a 'magic
// string' that gets split to produce the values.
type ToRead struct {
	url string `json:"ToReadURL"`
	title string `json:"ToReadTitle"`
}

type VisitedPage struct {
	count int
	u string
}

type Settings struct {
	OpenWebLinks      bool
	OpenGopherLinks   bool
	OpenFileLinks     bool

	KeepHistory       bool
	History           []string

	UseCustomHome     bool
	CustomHomeHTML    string

	SearchURL         string
	BaseFontSize      int
	TimeOutSeconds    int
	ReadingList       []string
}

var settingsPath = ""

func (s Settings) FontSize() int {
	return s.BaseFontSize
}

func (s *Settings) IncreaseFontSize() int {
	s.BaseFontSize += 1
	return s.BaseFontSize
}

func (s *Settings) DecreaseFontSize() int {
	s.BaseFontSize -= 1
	return s.BaseFontSize
}

func (s *Settings) AddToHistory(u string) error {
	if !s.KeepHistory {
		s.History = make([]string, 0)
		return fmt.Errorf("No history is available")
	}
	if len(s.History) == 0 {
		s.History = make([]string, 0, 10)
		s.History = append(s.History, u)
		return nil
	}
	s.History = append([]string{u}, s.History...)
	if len(s.History) > 100 {
		s.History = s.History[:101]
	}
	return nil
}

func (s *Settings) AddToReadingList(title, u string) (string, error) {
	// FIXME: The double-tab string is being used because json.Marshall
	// would not marshall the slice of ToRead structs that was being
	// used prior. It resulted in empty objects in the json. Using a
	// string slice with a 'magic string' for splitting is a temporary
	// solution until the json.Marshall issue can be solved
	stringified := u + "\t\t" + title
	s.ReadingList = append(s.ReadingList, stringified)
	return s.ReadingListHTML()
}

func (s Settings) ReadingListHTML() (string, error) {
	var out strings.Builder
	linkFormat := `<a href="%s" id="rl-%d">%s</a> <button aria-label="Remove from reading list" class="reading-list-delete" data-index="%d">&#10060;</button><br>`
	out.WriteString("<h1 tabindex=\"-1\" id=\"rl-header\">Reading List</h1>\n<hr>\n")
	for i, li := range s.ReadingList{
		// FIXME: The double-tab string is being used because json.Marshall
		// would not marshall the slice of ToRead structs that was being
		// used prior. It resulted in empty objects in the json. Using a
		// string slice with a 'magic string' for splitting is a temporary
		// solution until the json.Marshall issue can be solved
		sLn := strings.Split(li, "\t\t")
		if len(sLn) < 2 {
			continue
		}
		out.WriteString(fmt.Sprintf(linkFormat, sLn[0], i, sLn[1], i))
	}
	return out.String(), nil
}

func (s *Settings) RemoveFromReadingList(index int) (string, error) {
	s.ReadingList = append(s.ReadingList[:index], s.ReadingList[index+1:]...)
	return s.ReadingListHTML()
}

func (s Settings) GenerateConfig() {
	err := os.MkdirAll(filepath.Dir(settingsPath), 0755)
	if err != nil {
		fmt.Fprintf(os.Stderr, "[SETTINGS] Unable to create directories for write operation")
		return
	}

	s.Save()
}

func (s *Settings) Save() {
	j, err := json.MarshalIndent(s, "", "\t")
	if err != nil {
		fmt.Fprintf(os.Stderr, "[SETTINGS] Unable to marshall json for write opperation")
		return
	}

	var perms os.FileMode = 0644
	fstats, err := os.Stat(settingsPath)
	if err == nil {
		perms = fstats.Mode()
	}
	f, err := os.OpenFile(settingsPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, perms)
	if err != nil {
		fmt.Fprintf(os.Stderr, "[SETTINGS] Unable to open file for writing")
		return
	}
	defer f.Close()
	f.Write(j)

}

func (s *Settings) Load() {
	configPath := os.Getenv("XDG_CONFIG_HOME")
	if configPath == "" {
		userinfo, _ := user.Current()
		settingsPath = filepath.Join(userinfo.HomeDir, ".config", "goldberry", "goldberry.json")
	} else {
		settingsPath = filepath.Join(configPath, ".config", "goldberry.json")
	}

	_, err := os.Stat(settingsPath)
	if err != nil {
		s.GenerateConfig()
	}
	b, err := ioutil.ReadFile(settingsPath)
	if err != nil {
		return
	}
	json.Unmarshal(b, s)
}

func NewSettings() *Settings {
	return &Settings{false, false, true, false, make([]string, 0, 10), false, "", "gemini://geminispace.info:1965/search?%s", 12, 5, make([]string, 0, 10)}
}

