package redis

import (
	"time"

	"github.com/beyondblog/k8s-web-terminal/Godeps/_workspace/src/github.com/kataras/iris/sessions/store"
	"github.com/beyondblog/k8s-web-terminal/Godeps/_workspace/src/github.com/kataras/iris/utils"
)

/*Notes only for me
--------
Here we are setting a structure which keeps the current session's values setted by store.Set(key,value)
this is the RedisValue struct.
if noexists
	RedisValue := RedisValue{sessionid,values)

RedisValue.values[thekey]=thevalue


service.Set(store.sid,RedisValue)

because we are using the same redis service for all sessions, and this is the best way to separate them,
without prefix and all that which I tried and failed to deserialize them correctly if the value is string...
so again we will keep the current server's sessions into memory
and fetch them(the sessions) from the redis at each first session run. Yes this is the fastest way to get/set a session
and at the same time they are keep saved to the redis and the GC will cleanup the memory after a while like we are doing
with the memory provider. Or just have a values field inside the Store and use just it, yes better simpler approach.
Ok then, let's convert it again.
*/

// Values is just a type of a map[interface{}]interface{}
type Values map[interface{}]interface{}

// Store the redis session store
type Store struct {
	sid                string
	lastAccessedTime   time.Time
	values             Values
	cookieLifeDuration time.Duration //used on .Set-> SETEX on redis
}

var _ store.IStore = &Store{}

// NewStore creates and returns a new store based on the session id(string) and the cookie life duration (time.Duration)
func NewStore(sid string, cookieLifeDuration time.Duration) *Store {
	s := &Store{sid: sid, lastAccessedTime: time.Now(), cookieLifeDuration: cookieLifeDuration}
	//fetch the values from this session id and copy-> store them
	val, err := redis.GetBytes(sid)
	if err == nil {
		err = utils.DeserializeBytes(val, &s.values)
		if err != nil {
			//if deserialization failed
			s.values = Values{}
		}

	}
	if s.values == nil {
		//if key/sid wasn't found or was found but no entries in it(L72)
		s.values = Values{}
	}

	return s
}

// serialize the values to be stored as strings inside the Redis, we panic at any serialization error here
func serialize(values Values) []byte {
	val, err := utils.SerializeBytes(values)
	if err != nil {
		panic("On redisstore.serialize: " + err.Error())
	}

	return val
}

// update updates the real redis store
func (s *Store) update() {
	go redis.Set(s.sid, serialize(s.values), s.cookieLifeDuration.Seconds()) //set/update all the values, in goroutine
}

// GetAll returns all values
func (s *Store) GetAll() map[interface{}]interface{} {
	return s.values
}

// VisitAll loop each one entry and calls the callback function func(key,value)
func (s *Store) VisitAll(cb func(k interface{}, v interface{})) {
	for key := range s.values {
		cb(key, s.values[key])
	}
}

// Get returns the value of an entry by its key
func (s *Store) Get(key interface{}) interface{} {
	Provider.Update(s.sid)

	if value, found := s.values[key]; found {
		return value
	}

	return nil
}

// GetString same as Get but returns as string, if nil then returns an empty string
func (s *Store) GetString(key interface{}) string {
	if value := s.Get(key); value != nil {
		if v, ok := value.(string); ok {
			return v
		}
	}

	return ""
}

// GetInt same as Get but returns as int, if nil then returns -1
func (s *Store) GetInt(key interface{}) int {
	if value := s.Get(key); value != nil {
		if v, ok := value.(int); ok {
			return v
		}
	}

	return -1
}

// Set fills the session with an entry, it receives a key and a value
// returns an error, which is always nil
func (s *Store) Set(key interface{}, value interface{}) error {
	s.values[key] = value
	Provider.Update(s.sid)

	s.update()
	return nil
}

// Delete removes an entry by its key
// returns an error, which is always nil
func (s *Store) Delete(key interface{}) error {
	delete(s.values, key)
	Provider.Update(s.sid)
	s.update()
	return nil
}

// Clear removes all entries
// returns an error, which is always nil
func (s *Store) Clear() error {
	//we are not using the Redis.Delete, I made so work for nothing.. we wanted only the .Set at the end...
	for key := range s.values {
		delete(s.values, key)
	}

	Provider.Update(s.sid)
	s.update()
	return nil
}

// ID returns the session id
func (s *Store) ID() string {
	return s.sid
}

// LastAccessedTime returns the last time this session has been used
func (s *Store) LastAccessedTime() time.Time {
	return s.lastAccessedTime
}

// SetLastAccessedTime updates the last accessed time
func (s *Store) SetLastAccessedTime(lastacc time.Time) {
	s.lastAccessedTime = lastacc
}

// Destroy deletes entirely the session, from the memory, the client's cookie and the store
func (s *Store) Destroy() {
	// remove the whole  value which is the s.values from real redis
	redis.Delete(s.sid)
}
