// WS-POS version 1.3 参照実装
// 
// 実装 version 0.6 

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.Reflection;

namespace WSPOSInitiative.Common
{
    /// <summary>
    /// セッションマネージャの参照実装
    /// Reference implementations for session manager
    /// </summary>
    public class SessionManagerDefaultImpl : ISessionManager
    {

        /// <summary>
        /// ログ用インスタンス
        /// log instance
        /// </summary>
        ILog _log;

        /// <summary>
        /// セッション管理の辞書
        /// The dictionary of session manager.
        /// </summary>
        IDictionary<string, ISessionContext> _sessions = new Dictionary<string, ISessionContext>();

        /// <summary>
        /// 呼び出せないようにprivate宣言します
        /// A private declaration is made so that it cannot call. 
        /// </summary>
        private SessionManagerDefaultImpl()
        {
        }

        /// <summary>
        /// コンストラクタ
        /// Constructor
        /// </summary>
        /// <param name="log">ログ用インスタンス | log instance</param>
        public SessionManagerDefaultImpl(ILog log)
        {
            _log = log;
        }

        /// <summary>
        /// セッションコンテキストの取得
        /// Retrieving session context
        /// </summary>
        /// <param name="sessionID">セッションID | Session ID</param>
        /// <returns>セッションIDに関連付けられたセッションコンテキスト | Session context associated with the session ID</returns>
        public virtual ISessionContext GetSessionContext(string sessionID)
        {
            lock (_sessions)
            {
                if (sessionID == null)
                {
                    throw new ArgumentNullException("sessionID");
                }

                if (!_sessions.ContainsKey(sessionID))
                {
                    throw new KeyNotFoundException(string.Format("sessionID {0} is not found in session manager.", sessionID));
                }

                ISessionContext sessionContext = _sessions[sessionID];
                sessionContext.LastAccessDateTime = DateTime.Now;
                return _sessions[sessionID];
            }
        }

        /// <summary>
        /// セッションコンテキストの設定
        /// Setting the session context
        /// </summary>
        /// <param name="sessionID">セッションID | Session ID</param>
        /// <param name="context">セッションIDに関連付けるセッションコンテキスト | Session context associated with a session ID </param>
        public virtual void SetSessionContext(string sessionID, ISessionContext context)
        {
            lock (_sessions)
            {
                if (sessionID == null)
                {
                    throw new ArgumentNullException("sessionID");
                }
                _sessions[sessionID] = context;
            }
        }

        /// <summary>
        /// セッションコンテキストの削除
        /// Delete the session context
        /// </summary>
        /// <param name="sessionID">削除するセッションコンテキストが関連付けられたセッションID | Session ID for delete.</param>
        public virtual void RemoveSessionContext(string sessionID)
        {
            lock (_sessions)
            {
                if (sessionID == null)
                {
                    throw new ArgumentNullException("sessionID");
                }

                if (!_sessions.ContainsKey(sessionID))
                {
                    throw new KeyNotFoundException(string.Format("sessionID {0} is not found in session manager.", sessionID));
                }

                _sessions.Remove(sessionID);
            }
        }

        /// <summary>
        /// セッションIDがすでに管理下に存在するか否かを返す
        /// Returns whether a session ID already exists under the control
        /// </summary>
        /// <param name="sessionID">セッションID | Session ID</param>
        /// <returns>セッションIDがすでに管理下に存在するか否か | Session ID already exists under control or not</returns>
        public virtual bool IsExistSession(string sessionID)
        {
            lock (_sessions)
            {
                if (sessionID == null)
                {
                    throw new ArgumentNullException("sessionID");
                }
                return _sessions.ContainsKey(sessionID);
            }
        }

        /// <summary>
        /// セッションIDが受け入れ可能か否かを返す
        /// Returns whether the session ID is acceptable
        /// </summary>
        /// <param name="sessionID">セッションID | Session ID</param>
        /// <returns>セッションIDが受け入れ可能か否か | The session ID is acceptable or not </returns>
        public virtual bool IsValidSessionID(string sessionID)
        {
            if (sessionID == null)
            {
                return false;
            }
            if (sessionID == string.Empty)
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// セッションコンテキストを追加する 
        /// Add a session context
        /// </summary>
        /// <param name="sessionID">セッションID | Session ID</param>
        /// <param name="context">セッションIDに関連付けるセッションコンテキスト | Session context associated with a session ID</param>
        public virtual void AddSession(string sessionID, ISessionContext context)
        {
            lock (_sessions)
            {
                if (sessionID == null)
                {
                    throw new ArgumentNullException("sessionID");
                }

                if (_sessions.ContainsKey(sessionID))
                {
                    throw new ArgumentException("duplicate sessionID");
                }

                _sessions.Add(sessionID, context);
            }
        }

        /// <summary>
        /// タイムアウトに達したセッションコンテキストを削除する
        /// Delete the session context that the timeout is reached
        /// </summary>
        /// <param name="all">true時にはすべてのコンテキストを削除 | If true is specified as this, all the contexts will be deleted. </param>
        /// <returns>削除したセッションコンテキストの列挙 | Enumeration of session context deleted</returns>
        public virtual IEnumerable<ISessionContext> RemoveTimeoutSessionContext(bool all)
        {
            DateTime now = DateTime.Now;

            lock (_sessions)
            {
                IList<ISessionContext> sessionsForRemove = new List<ISessionContext>();
                sessionsForRemove.Clear();

                if (all)
                {
                    foreach (ISessionContext sessionContext in _sessions.Values)
                    {
                        sessionsForRemove.Add(sessionContext);
                        _log.LogWriteLineMessage(MethodBase.GetCurrentMethod(), "remove session ID (all):" + sessionContext.ConsumerID + ":LastAccess=" + sessionContext.LastAccessDateTime.ToString());
                    }
                }
                else
                {
                    foreach (ISessionContext sessionContext in _sessions.Values)
                    {
                        TimeSpan timeoutSpan = new TimeSpan(0, 0, sessionContext.ProviderSessionTimeout);

                        if (now - sessionContext.LastAccessDateTime > timeoutSpan)
                        {
                            sessionsForRemove.Add(sessionContext);
                            _log.LogWriteLineMessage(MethodBase.GetCurrentMethod(), "timeout remove session ID:" + sessionContext.ConsumerID + ":LastAccess=" + sessionContext.LastAccessDateTime.ToString());
                        }
                    }
                }
                foreach (ISessionContext sessionContext in sessionsForRemove)
                {
                    RemoveSessionContext(sessionContext.ConsumerID);
                }

                return sessionsForRemove;
            }
        }

        public virtual ICollection<string> GetAllSessionIDs()
        {
            return _sessions.Keys;
        }

    }
}
