package persistence

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
)

// dataPath global path for persistence
var dataPath = os.TempDir()

// SetPath sets the persistent path for the data storage
func SetPath(p string) error {
	if info, err := os.Stat(p); err != nil {
		return err
	} else if !info.IsDir() {
		return fmt.Errorf("path for persistence is not directory")
	}
	dataPath = p
	return nil
}

// Converts name to a full path
func jsonPath(name string) string {
	return path.Join(dataPath, fmt.Sprintf("rove-%s.json", name))
}

// Save will serialise the interface into a json file
func Save(name string, data interface{}) error {
	p := jsonPath(name)
	if b, err := json.MarshalIndent(data, "", "  "); err != nil {
		return err
	} else {
		if err := ioutil.WriteFile(p, b, os.ModePerm); err != nil {
			return err
		}
	}

	log.Printf("Saved %s\n", p)
	return nil
}

// Load will load the interface from the json file
func Load(name string, data interface{}) error {
	p := jsonPath(name)
	// Don't load anything if the file doesn't exist
	_, err := os.Stat(p)
	if os.IsNotExist(err) {
		log.Printf("File %s didn't exist, loading with fresh data\n", p)
		return nil
	}

	// Read and unmarshal the json
	if b, err := ioutil.ReadFile(p); err != nil {
		return err
	} else if len(b) == 0 {
		log.Printf("File %s was empty, loading with fresh data\n", p)
		return nil
	} else if err := json.Unmarshal(b, data); err != nil {
		return fmt.Errorf("failed to load file %s error: %s", p, err)
	}

	log.Printf("Loaded %s\n", p)
	return nil
}

// saveLoadFunc defines a type of function to save or load an interface
type saveLoadFunc func(string, interface{}) error

func doAll(f saveLoadFunc, args ...interface{}) error {
	var name string
	for i, a := range args {
		if i%2 == 0 {
			var ok bool
			name, ok = a.(string)
			if !ok {
				return fmt.Errorf("incorrect args")
			}
		} else {
			if err := f(name, a); err != nil {
				return err
			}
		}
	}
	return nil
}

// SaveAll allows for saving multiple structures in a single call
func SaveAll(args ...interface{}) error {
	return doAll(Save, args...)
}

// LoadAll allows for loading multiple structures in a single call
func LoadAll(args ...interface{}) error {
	return doAll(Load, args...)
}
