/*
 * 由SharpDevelop创建。
 * 用户： Administrator
 * 日期: 2018-10-24
 * 时间: 9:56
 * 
 * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件
 */
using System;
using System.Threading;
using MrWu.Debug;
using Server.DB.Redis;
using StackExchange.Redis;
using System.Threading.Tasks;

namespace Server.DB.Redis
{
	/// <summary>
	/// 分布式锁
	/// </summary>
	public class DistributedLock{
		
		/// <summary>
		/// 最大等待数量
		/// </summary>
		private readonly int maxWaitCnt;
		
		/// <summary>
		/// 锁的钥匙
		/// </summary>
		private readonly string[] lockKey;
		
		/// <summary>
		/// 下一个锁的位置
		/// </summary>
		private int nextid = -1;
		
		/// <summary>
		/// 读写锁
		/// </summary>
		private readonly ReaderWriterLockSlim rwls = new ReaderWriterLockSlim();
		
		private volatile IDatabaseProxy m_db;
		
		/// <summary>
		/// 操作线程锁数据库
		/// </summary>
		private IDatabaseProxy db{
			get{
				if(m_db == null)
					m_db = redisManager.db;
				return m_db;
			}
		}
		
		/// <summary>
		/// 创建分布式锁
		/// </summary>
		/// <param name="waitMaxCnt">最大等待数量</param>
		public DistributedLock(int waitMaxCnt){
			maxWaitCnt = waitMaxCnt;
			lockKey = new string[maxWaitCnt];
		}
		
		/// <summary>
		/// 添加锁
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		private int AddKey(string key){
			while(true){
				rwls.EnterWriteLock();
				try{
					nextid++;
					if(nextid >= maxWaitCnt)
						nextid = 0;
					if(lockKey[nextid] == null){
						lockKey[nextid] = key;
						return nextid;
					}
				}finally{
					rwls.ExitWriteLock();
				}
				//SpinWait sw = new SpinWait();
				//sw.SpinOnce();
                Thread.Sleep(1);
			}
		}
		
		/// <summary>
		/// 删除锁
		/// </summary>
		/// <param name="idx"></param>
		/// <returns></returns>
		private void RemoveKey(int idx){
			rwls.EnterWriteLock();
			try{
				lockKey[idx] = null;
			}finally{
				rwls.ExitWriteLock();
			}
		}
		
		/// <summary>
		/// 获取钥匙
		/// </summary>
		/// <param name="idx"></param>
		/// <returns></returns>
		private string GetKey(int idx){
			rwls.EnterReadLock();
			try{
				return lockKey[idx];
			}finally{
				rwls.ExitReadLock();
			}
		}
		
		/// <summary>
		/// 为锁续航
		/// </summary>
		/// <param name="state"></param>
		private void AddLockLife(object state){
			Task.Factory.StartNew(()=>{
			                      	int idx = (int)state;
			                      	string key = GetKey(idx);
			                      	
			                      	while(true){
			                      		Thread.Sleep(1000);
			                      		if(key == GetKey(idx) && key != null){
			                      			string strtimeOut = db.StringGet(key);
			                      			if(strtimeOut != null){
			                      				int timeOut;
			                      				if(int.TryParse(strtimeOut,out timeOut))
			                      					db.KeyExpire(key,new TimeSpan(0,0,timeOut));
			                      				else
			                      					Debug.Error("不可能的错误存储的值不是数字anckjnsakjbdnja,"+strtimeOut + "," + key);
			                      			}else{
			                      				break;
			                      			}
			                      		}else
			                      			break;
			                      	}
			                      });
		}
		
		/// <summary>
		/// 尝试运行
		/// </summary>
		/// <param name="key">key</param>
		/// <param name="action">事件</param>
		/// <param name="timeOut">key超时时间</param>
		/// <param name="tryTimeOut">尝试超时时间</param>
		/// <returns>是否运行</returns>
		public bool TryLockRun(string key,Action action,int timeOut,int tryTimeOut = 0){
			if(key == null){
				Debug.Error("key不能为null");
				return false;
			}
			key = key+".lock";
			TimeSpan tm = new TimeSpan(0,0,timeOut);
			DateTime lastTime = DateTime.Now;
			//SpinWait sw = new SpinWait();
			while(!db.StringSet(key,timeOut,tm,When.NotExists)){
				if((DateTime.Now - lastTime).TotalMilliseconds >= tryTimeOut)
					return false;
				//sw.SpinOnce();
                Thread.Sleep(1);
			}
			
			//Debug.Log("运算开始:" + key + "," + DateTime.Now.Millisecond.ToString());
			Exception ex = null;
			int idx = AddKey(key);
			try{
				AddLockLife(idx);
				if(action != null)
					action();
			}catch(Exception e){
				Debug.Error("处理出错:" + e.ToString());
				ex = e;
			}finally{
				RemoveKey(idx);
				//Debug.Log("运算结束:" + key + "," + DateTime.Now.Millisecond.ToString());
				db.KeyDelete(key);
				if(ex != null)
					throw ex;
			}
			return true;
			
		}
		
		/// <summary>
		/// 尝试运行
		/// </summary>
		/// <param name="key">key</param>
		/// <param name="func">事件</param>
		/// <param name="timeOut">timeOut</param>
		/// <param name="result">返回结果</param>
		/// <param name="tryTimeOut">尝试超时时间</param>
		/// <returns>是否运行</returns>
		public bool TryLockRun<T>(string key,Func<T> func,int timeOut,out T result,int tryTimeOut = 0){
			result = default(T);
			if(key == null){
				Debug.Error("key不能为null");
				return false;
			}
			
			key = key + ".lock";
			TimeSpan tm = new TimeSpan(0,0,timeOut);
			DateTime lastTime = DateTime.Now;
			//SpinWait sw = new SpinWait();
			while(!db.StringSet(key,timeOut,tm,When.NotExists)){
				if((DateTime.Now - lastTime).TotalMilliseconds >= tryTimeOut)
					return false;
				//sw.SpinOnce();
                Thread.Sleep(1);
			}
			
			Exception ex = null;
			int idx = AddKey(key);
			try{
				AddLockLife(idx);
				if(func != null)
					result = func();
			}catch(Exception e){
				ex = e;
				Debug.Error("处理出错:" + e.ToString());
			}finally{
				RemoveKey(idx);
				Debug.Log("运算结束:" + key + "," + DateTime.Now.Millisecond.ToString());
				db.KeyDelete(key);
				if(ex != null)
					throw ex;
			}
			return true;
		}
		
		/// <summary>
		/// 加锁运行
		/// </summary>
		public void LockRun(string key,Action action,int timeOut = 3){
			if(key == null){
				Debug.Error("key不能为null");
				return;
			}
			key = key+".lock";
			TimeSpan tm = new TimeSpan(0,0,timeOut);
			//SpinWait sw = new SpinWait();
			while(!db.StringSet(key,timeOut,tm,When.NotExists)){
				//sw.SpinOnce();
                Thread.Sleep(1);
			}
			
			Exception ex = null;
			
			int idx = AddKey(key);
			try{
				AddLockLife(idx);
				
				if(action != null)
					action();
				
			}catch(Exception e){
				ex = e;
			}finally{
				RemoveKey(idx);
				db.KeyDelete(key);
				if(ex != null)
					throw ex;
			}
		}
		
		/// <summary>
		/// 加锁运行
		/// </summary>
		/// <param name="key"></param>
		/// <param name="func"></param>
		/// <param name="timeOut">超时时间</param>
		/// <returns></returns>
		public T LockRun<T>(string key,Func<T> func,int timeOut = 3){
			T result = default(T);
			if(key == null){
				Debug.Error("key不能为null");
				return result;
			}
			
			key = key + ".lock";
			TimeSpan tm = new TimeSpan(0,0,timeOut);
			//SpinWait sw = new SpinWait();
			
			while(!db.StringSet(key,timeOut,tm,When.NotExists)){
				//sw.SpinOnce();
                Thread.Sleep(1);
			}
			
			Exception ex = null;
			int idx = AddKey(key);
			try{
				AddLockLife(idx);
				
				if(func != null)
					result = func();
				
			}catch(Exception e){
				ex = new Exception("lockRun error :" , e);
            }finally{
				RemoveKey(idx);
				db.KeyDelete(key);
				if(ex != null)
					throw ex;
            }
			return result;
		}
	}
}
