package main

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"os"
	"time"
)

//chat state that we might be use for collecting infomation from user
type chatState struct {
	//back pointer style information, managed by general process
	OpenID    string `json:"OpenID"`    //whom is this belongs to
	Procedure string `json:"Procedure"` //which procedure this belongs to

	//below is managed by procedure only
	//real state information
	Name     string `json:"Name"`     //state name
	CreateAt int32  `json:"CreateAt"` //unix timestamp , when its created
	Expire   int32  `json:"Expire"`   //unix timestamp  when this state expire

	//persistant sate
	Data map[string]string `json:"Data"` //the state save some data for later usage

	//runtime memory only
	response string //被动回复消息的内容
}

func createEmptyState(openID, procedure string, expireInSeconds int32) chatState {
	r := chatState{}
	r.Name = ""
	r.OpenID = openID
	r.Procedure = procedure
	r.CreateAt = int32(time.Now().Unix())
	r.Expire = r.CreateAt + expireInSeconds
	r.Data = map[string]string{}
	return r
}

func (m *chatState) Load(openID, procedure string) (result chatState, err error) {
	result, err = getCurrentState(openID, procedure)
	*m = result
	return
}

//for individual state
func getCurrentState(openID string, procedure string) (result chatState, err error) {
	path := getProcedurePath(openID, procedure)
	body, err := ioutil.ReadFile(path)
	if err != nil { //read file error
		if isFileExist(path) {
			log.Println("Error session reading " + path)
		}
		return //empty and expired session
	}
	err = json.Unmarshal(body, &result)
	if err != nil {
		log.Printf("Session Content [path=%s] not correct: ", path)
		log.Println(err)
	}
	result.OpenID = openID
	result.Procedure = procedure
	return
}

func (m *chatState) Save() (err error) {
	if m.isEndingState() {
		return m.Delete()
	}
	//save to disk
	j, err := json.Marshal(*m)
	if err != nil {
		return
	}
	path := getProcedurePath(m.OpenID, m.Procedure)
	err = ioutil.WriteFile(path, j, 0600)
	if err != nil {
		log.Println("write state error" + path)
		log.Println(err)
		return
	}
	return
}

func (m *chatState) Delete() (err error) {
	path := getProcedurePath(m.OpenID, m.Procedure)
	if isFileExist(path) {
		err = os.Remove(path)
	}
	return
}

func (m *chatState) isEndingState() bool {
	if isExpired(m.Expire) || m.Name == "" {
		return true
	}
	return false
}
