package main

import (
	"container/list"
	"sync"
	"time"
)

const SessionTimeout = time.Minute * 30

type Session struct {
	sessionId  string
	accountId  string
	updateTime time.Time
}

var sessionManager = new(SessionManager)

type SessionManager struct {
	stopped       bool
	sessions      *list.List
	sessionLocker *sync.RWMutex
	sessionMap1   map[string]*Session
	sessionMap2   map[string]*Session
}

func (manager *SessionManager) Start() {
	if manager.sessionMap1 == nil {
		manager.sessionMap1 = make(map[string]*Session)
	}
	if manager.sessionMap2 == nil {
		manager.sessionMap2 = make(map[string]*Session)
	}
	if manager.sessions == nil {
		manager.sessions = list.New()
	}
	manager.sessionLocker = new(sync.RWMutex)
	manager.stopped = false
	go func() {
		for !manager.stopped {
			timeoutSessions := list.New()
			manager.sessionLocker.RLock()
			for e := manager.sessions.Front(); e != nil; e = e.Next() {
				if manager.stopped {
					break
				}
				session := e.Value.(*Session)
				now := time.Now()
				if now.Sub(session.updateTime) >= SessionTimeout {
					//Timeout
					timeoutSessions.PushBack(e)
				}
			}
			manager.sessionLocker.RUnlock()

			manager.sessionLocker.Lock()
			for e := timeoutSessions.Front(); e != nil; e = e.Next() {
				if manager.stopped {
					break
				}
				session := e.Value.(*Session)
				manager.sessions.Remove(e.Value.(*list.Element))
				manager.sessionMap1[session.sessionId] = nil
				manager.sessionMap2[session.accountId] = nil
			}
			manager.sessionLocker.Unlock()

			time.Sleep(1 * time.Second)
		}
	}()
	manager.stopped = false
}

func (manager *SessionManager) Stop() {
	manager.stopped = true
}

func (manager *SessionManager) ActivateSession(sessionId string) {
	session := manager.sessionMap1[sessionId]
	if session != nil {
		session.updateTime = time.Now()
	}
}

func (manager *SessionManager) AddSession(session *Session) {
	manager.sessionLocker.Lock()
	manager.sessionMap1[session.sessionId] = session
	manager.sessionMap2[session.accountId] = session
	manager.sessionLocker.Unlock()
}

func (manager *SessionManager) RemoveSessionBySessionId(sessionId string) {
	manager.sessionLocker.Lock()
	for e := manager.sessions.Front(); e != nil; e = e.Next() {
		if manager.stopped {
			break
		}
		session := e.Value.(*Session)
		if session.sessionId == sessionId {
			manager.sessions.Remove(e.Value.(*list.Element))
			manager.sessionMap1[session.sessionId] = nil
			manager.sessionMap2[session.accountId] = nil
		}
	}
	manager.sessionLocker.Unlock()
}

func (manager *SessionManager) RemoveSessionByAccountId(accountId string) {
	manager.sessionLocker.Lock()
	for e := manager.sessions.Front(); e != nil; e = e.Next() {
		if manager.stopped {
			break
		}
		session := e.Value.(*Session)
		if session.accountId == accountId {
			manager.sessions.Remove(e.Value.(*list.Element))
			manager.sessionMap1[session.sessionId] = nil
			manager.sessionMap2[session.accountId] = nil
		}
	}
	manager.sessionLocker.Unlock()
}

func (manager *SessionManager) GetSessionBySessionId(sessionId string) *Session {
	manager.sessionLocker.RLock()
	session := manager.sessionMap1[sessionId]
	manager.sessionLocker.RUnlock()
	return session
}

func (manager *SessionManager) GetSessionByAccountId(accountId string) *Session {
	manager.sessionLocker.RLock()
	session := manager.sessionMap2[accountId]
	manager.sessionLocker.RUnlock()
	return session
}
