package raftNode

import (
	"github.com/hashicorp/raft"
	"io"
	"io/ioutil"
	"sync"
)

func NewRaftFsm() *raftFsm {
	return &raftFsm{
		dataTreeMp: map[string]*TreeData{},
	}
}

//raftFsm 跟随者
type raftFsm struct {
	lock       sync.Mutex   //互斥锁
	rwLock     sync.RWMutex //读写锁
	dataTreeMp map[string]*TreeData
	Data       database
}

func (that *raftFsm) getTreeData(treeName ...string) *TreeData {
	that.lock.Lock()
	defer that.lock.Unlock()
	_treeName := "default"
	if len(treeName) > 0 {
		_treeName = treeName[0]
	}
	_dataTree, _ok := that.dataTreeMp[_treeName]
	if !_ok {
		_dataTree = &TreeData{
			Data: map[string]interface{}{},
			lock: new(sync.RWMutex),
		}
		that.dataTreeMp[_treeName] = _dataTree
	}
	return _dataTree
}

func (that *raftFsm) List() []interface{} {
	that.rwLock.RLock()
	defer that.rwLock.RUnlock()
	_slice := []interface{}{}
	for _, _v := range that.dataTreeMp {
		_slice = append(_slice, _v.List()...)
	}
	return _slice
}

func (that *raftFsm) Find(tree, key string) interface{} {
	_treeData := that.getTreeData(tree).Get(key)
	return _treeData
}

//Apply flower同步写入
func (that *raftFsm) Apply(l *raft.Log) interface{} {
	that.rwLock.RLock()
	defer that.rwLock.RUnlock()
	//
	_data := l.Data
	if len(_data) < 16 {
		return nil
	}
	var _action string
	var _actionData []byte
	_sliceFunc := []func(){}
	for {
		if len(_data) <= 8 {
			break
		}
		_action, _actionData, _data = decodeData(_data)
		switch _action {
		case "put":
			_func, _err := that.set(_actionData)
			if _err != nil {
				return _err
			}
			_sliceFunc = append(_sliceFunc, _func)
		case "del":
			_func, _err := that.del(_actionData)
			if _err != nil {
				return _err
			}
			_sliceFunc = append(_sliceFunc, _func)
		}
	}
	//执行
	if len(_sliceFunc) > 0 {
		for _, _func := range _sliceFunc {
			_func()
		}
	}
	return nil
}

func (that *raftFsm) del(data []byte) (func(), error) {
	_key := string(data)
	_entryKey, _err := parseEntryKey(string(data))
	if _err != nil {
		return nil, _err
	}
	return func() {
		_treeName := _entryKey.GetTree()
		//fmt.Println("===== fmt del ====", _treeName, _key)
		_treeData := that.getTreeData(_treeName)
		_treeData.Del(_key)
	}, nil
}

func (that *raftFsm) set(data []byte) (func(), error) {
	//
	_entry, _err := parseEntry(data)
	if _err != nil {
		log.Errorf("raftFsm.Apply parseEntry err:%s", _err.Error())
		return nil, _err
	}
	//
	_key := _entry.GetKey()
	_val := _entry.GetVal()
	//
	_entryKey, _err := _entry.GetEntryKey()
	if _err != nil {
		log.Errorf("raftFsm.Apply GetEntryKey err:%s", _err.Error())
		return nil, _err
	}
	_tree := _entryKey.GetTree()
	_treeName := "default"
	if _tree != "" {
		_treeName = _entryKey.GetTree()
	}
	//
	return func() {
		//fmt.Println("===== fmt set ====", _treeName, _key)
		_treeData := that.getTreeData(_treeName)
		_treeData.Set(_key, _val)
	}, nil
}

//Snapshot 返回快照接口类型
func (that *raftFsm) Snapshot() (raft.FSMSnapshot, error) {
	that.rwLock.Lock()
	defer that.rwLock.Unlock()
	that.Data = []byte{}
	for _k, _v := range that.dataTreeMp {
		_data, _err := _v.Copy()
		if _err != nil {
			return nil, _err
		}
		if _data == nil {
			continue
		}
		that.Data = append(that.Data, encodeData(_k, _data)...)
	}
	return that.Data, nil
}

//Restore 从快照中恢复到内存
func (that *raftFsm) Restore(r io.ReadCloser) error {
	that.rwLock.Lock()
	defer that.rwLock.Unlock()
	_data, _err := ioutil.ReadAll(r)
	if _err != nil {
		log.Errorf("Restore err:%s", _err.Error())
		return _err
	}
	var _treeName string
	var _val []byte
	for {
		if len(_data) <= 8 {
			return nil
		}
		_treeName, _val, _data = decodeData(_data)
		_dataTree := that.getTreeData(_treeName)
		if _err := _dataTree.Restore(_val); _err != nil {
			log.Errorf("Restore data tree err:%s", _err.Error())
			return _err
		}
	}
	return nil
}

type database []byte

//Persist 生成快照
func (that database) Persist(sink raft.SnapshotSink) error {
	if _, _err := sink.Write(that); _err != nil {
		log.Errorf("Persist Write err:%s", _err.Error())
		return _err
	}
	if _err := sink.Close(); _err != nil {
		log.Errorf("Persist Close err:%s", _err.Error())
		return _err
	}
	return nil
}

func (that database) Release() {
}
