package i18n

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/howeyc/fsnotify"
	"mongo.games.com/goserver/core"
	"mongo.games.com/goserver/core/basic"
	"mongo.games.com/goserver/core/logger"
)

var Config = Configuration{}

type Configuration struct {
	RootPath  string
	hashCodes map[string]string
	watcher   *fsnotify.Watcher
}

func (this *Configuration) Name() string {
	return "i18n"
}

func (this *Configuration) Init() error {
	var err error
	workDir, err := os.Getwd()
	if err != nil {
		return err
	}
	this.RootPath = filepath.Join(workDir, this.RootPath)
	this.watcher, err = fsnotify.NewWatcher()
	if err != nil {
		logger.Logger.Warn(" fsnotify.NewWatcher err:", err)
		return err
	}

	// Process events
	go func() {
		defer func() {
			if err := recover(); err != nil {
				logger.Logger.Warn("watch data director modify goroutine err:", err)
			}
		}()
		for {
			select {
			case ev := <-this.watcher.Event:
				if ev != nil && ev.IsModify() && filepath.Ext(ev.Name) == ".json" {
					core.CoreObject().SendCommand(&fileModifiedCommand{fileName: ev.Name}, false)
					logger.Logger.Trace("fsnotify event:", ev)
				}

			case err := <-this.watcher.Error:
				logger.Logger.Warn("fsnotify error:", err)
			}
		}
		logger.Logger.Warn(this.RootPath, " watcher quit!")
	}()
	this.watcher.Watch(this.RootPath)

	this.hashCodes = make(map[string]string)
	//获得配置目录中所有的数据文件
	var files = []string{}
	filepath.Walk(this.RootPath, func(path string, info os.FileInfo, err error) error {
		if info.IsDir() == false && filepath.Ext(info.Name()) == ".json" {
			files = append(files, info.Name())
		}
		return nil
	})
	//加载这些数据文件
	h := md5.New()
	for _, f := range files {
		logger.Logger.Trace("load file name ======", f)
		buf, err := ioutil.ReadFile(filepath.Join(this.RootPath, f))
		if err != nil {
			logger.Logger.Warn("i18n Config.Init ioutil.ReadFile error", err)
			return err
		}
		kv := make(map[string]string)
		err = json.Unmarshal(buf, &kv)
		if err != nil {
			logger.Logger.Warn("i18n Config.Init json.Unmarshal error", err)
			return err
		}
		nameAndExt := strings.SplitN(f, ".", 2)
		if len(nameAndExt) == 2 {
			lang := nameAndExt[0]
			loc := &locale{lang: lang, message: kv}
			if loc != nil {
				locales.Add(loc)
			}

			h.Reset()
			h.Write(buf)
			this.hashCodes[f] = hex.EncodeToString(h.Sum(nil))
		}
	}
	return nil
}

func (this *Configuration) Close() error {
	this.watcher.Close()
	return nil
}

type fileModifiedCommand struct {
	fileName string
}

func (fmc *fileModifiedCommand) Done(o *basic.Object) error {
	fn := filepath.Base(fmc.fileName)
	hashCode := Config.hashCodes[fn]
	buf, err := ioutil.ReadFile(filepath.Join(Config.RootPath, fn))
	if err != nil {
		logger.Logger.Warn("i18n fileModifiedCommand ioutil.ReadFile error", err)
		return err
	}
	if len(buf) == 0 {
		return nil
	}
	h := md5.New()
	h.Reset()
	h.Write(buf)
	newCode := hex.EncodeToString(h.Sum(nil))
	if newCode != hashCode {
		logger.Logger.Trace("modified file name ======", fn)
		kv := make(map[string]string)
		err = json.Unmarshal(buf, &kv)
		if err != nil {
			logger.Logger.Warn("i18n Config.Init json.Unmarshal error", err)
			return err
		}

		nameAndExt := strings.SplitN(fn, ".", 2)
		if len(nameAndExt) == 2 {
			lang := nameAndExt[0]
			loc, exist := locales.getLocale(lang)
			if exist {
				loc.message = kv
			} else {
				loc = &locale{lang: lang, message: kv}
				if loc != nil {
					locales.Add(loc)
				}
			}
			Config.hashCodes[fn] = newCode
		}
	}

	return nil
}

func init() {
	core.RegistePackage(&Config)
}
