context: //
  // https://github.com/mythz/ServiceStack.Redis
  // ServiceStack.Redis: ECMA CLI Binding to the Redis key-value storage system
  //
  // Authors:
  //   Demis Bellot (demis.bellot@gmail.com)
  //
  // Copyright 2010 Liquidbit Ltd.
  //
  // Licensed under the same terms of Redis and ServiceStack: new BSD license.
  //
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using ServiceStack.Common.Extensions;
  using ServiceStack.Common.Utils;
  using ServiceStack.DesignPatterns.Model;
  using ServiceStack.Redis.Pipeline;
  using ServiceStack.Text;
  
  namespace ServiceStack.Redis.Generic
  {
  	/// <summary>
  	/// Allows you to get Redis value operations to operate against POCO types.
  	/// </summary>
  	/// <typeparam name="T"></typeparam>
  	public partial class RedisTypedClient<T>
  		: IRedisTypedClient<T>
  	{
  		readonly ITypeSerializer<T> serializer = new JsonSerializer<T>();
  		private readonly RedisClient client;
  
          public IRedisClient RedisClient
          {
              get { return client; }
          }
  
  		public IRedisNativeClient NativeClient
  		{
  			get { return client; }
  		}
  
  		/// <summary>
  		/// Use this to share the same redis connection with another
  		/// </summary>
  		/// <param name="client">The client.</param>
  		public RedisTypedClient(RedisClient client)
  		{
  			this.client = client;
  			this.Lists = new RedisClientLists(this);
  			this.Sets = new RedisClientSets(this);
  			this.SortedSets = new RedisClientSortedSets(this);
  
  			this.SequenceKey = client.GetTypeSequenceKey<T>();
  			this.TypeIdsSetKey = client.GetTypeIdsSetKey<T>();
  			this.TypeLockKey = string.Concat(client.NamespacePrefix, "lock:", typeof(T).Name);
              this.RecentSortedSetKey = string.Concat(client.NamespacePrefix, "recent:", typeof(T).Name);
  		}
  
          private readonly string RecentSortedSetKey;
  		public string TypeIdsSetKey { get; set; }
  		public string TypeLockKey { get; set; }
  
  		public IRedisTypedTransaction<T> CreateTransaction()
  		{
  			return new RedisTypedTransaction<T>(this);
  		}
  
          public IRedisTypedPipeline<T> CreatePipeline()
          {
              return new RedisTypedPipeline<T>(this);
          }
  		public IDisposable AcquireLock()
  		{
  			return client.AcquireLock(this.TypeLockKey);
  		}
  
  		public IDisposable AcquireLock(TimeSpan timeOut)
  		{
  			return client.AcquireLock(this.TypeLockKey, timeOut);
  		}
  
  		public IRedisTransactionBase Transaction
  		{
  			get
  			{
  				return client.Transaction;
  			}
  			set
  			{
  				client.Transaction = value;
  			}
  		}
  
          public IRedisPipelineShared Pipeline
          {
              get
              {
                  return client.Pipeline;
              }
              set
              {
                  client.Pipeline = value;
              }
          }
  
          public void Watch(params string[] keys)
          {
              client.Watch(keys);
          }
  
          public void UnWatch()
          {
              client.UnWatch();
          }
  
  		public void Multi()
  		{
  			this.client.Multi();
  		}
  
  		public void Discard()
  		{
  			this.client.Discard();
  		}
  
  		public void Exec()
  		{
  			client.Exec();
  		}
  
  		internal void AddTypeIdsRegisteredDuringPipeline()
  		{
  			client.AddTypeIdsRegisteredDuringPipeline();
  		}
  
  		internal void ClearTypeIdsRegisteredDuringPipeline()
  		{
  			client.ClearTypeIdsRegisteredDuringPipeline();
  		}
  
  		public List<string> GetAllKeys()
  		{
  			return client.GetAllKeys();
  		}
  
  	    public IRedisSet TypeIdsSet
  		{
  			get
  			{
  				return new RedisClientSet(client, client.GetTypeIdsSetKey<T>());
  			}
  		}
  
  		public T this[string key]
  		{
  			get { return GetValue(key); }
  			set { SetEntry(key, value); }
  		}
  
  		public byte[] SerializeValue(T value)
  		{
  			var strValue = serializer.SerializeToString(value);
  			return Encoding.UTF8.GetBytes(strValue);
  		}
  
  		public T DeserializeValue(byte[] value)
  		{
  			var strValue = value != null ? Encoding.UTF8.GetString(value) : null;
  			return serializer.DeserializeFromString(strValue);
  		}
  
  		public void SetEntry(string key, T value)
  		{
  			if (key == null)
  				throw new ArgumentNullException("key");
  
  			client.Set(key, SerializeValue(value));
  			client.RegisterTypeId(value);
  		}
  
  		public void SetEntry(string key, T value, TimeSpan expireIn)
  		{
  			if (key == null)
  				throw new ArgumentNullException("key");
  
  			client.Set(key, SerializeValue(value), expireIn);
  			client.RegisterTypeId(value);
  		}
  
  		public bool SetEntryIfNotExists(string key, T value)
  		{
  			var success = client.SetNX(key, SerializeValue(value)) == RedisNativeClient.Success;
  			if (success) client.RegisterTypeId(value);
  			return success;
  		}
  
  		public T GetValue(string key)
  		{
  			return DeserializeValue(client.Get(key));
  		}
  
  		public T GetAndSetValue(string key, T value)
  		{
  			return DeserializeValue(client.GetSet(key, SerializeValue(value)));
  		}
  
  		public bool ContainsKey(string key)
  		{
  			return client.Exists(key) == RedisNativeClient.Success;
  		}
  
  		public bool RemoveEntry(string key)
  		{
  			return client.Del(key) == RedisNativeClient.Success;
  		}
  
  		public bool RemoveEntry(params string[] keys)
  		{
  			return client.Del(keys) == RedisNativeClient.Success;
  		}
  
  		public bool RemoveEntry(params IHasStringId[] entities)
  		{
  			var ids = entities.ConvertAll(x => x.Id);
  			var success = client.Del(ids.ToArray()) == RedisNativeClient.Success;
  			if (success) client.RemoveTypeIds(ids.ToArray());
  			return success;
  		}
  
  		public long IncrementValue(string key)
  		{
  			return client.Incr(key);
  		}
  
  		public long IncrementValueBy(string key, int count)
  		{
  			return client.IncrBy(key, count);
  		}
  
  		public long DecrementValue(string key)
  		{
  			return client.Decr(key);
  		}
  
  		public long DecrementValueBy(string key, int count)
  		{
  			return client.DecrBy(key, count);
  		}
  
  		public string SequenceKey { get; set; }
  
  		public void SetSequence(int value)
  		{
  			client.GetSet(SequenceKey, Encoding.UTF8.GetBytes(value.ToString()));
  		}
  
  		public long GetNextSequence()
  		{
  			return IncrementValue(SequenceKey);
  		}
  
  		public long GetNextSequence(int incrBy)
  		{
  			return IncrementValueBy(SequenceKey, incrBy);
  		}
  
  		public RedisKeyType GetEntryType(string key)
  		{
  			return client.GetEntryType(key);
  		}
  
  		public string GetRandomKey()
  		{
  			return client.RandomKey();
  		}
  
  		public bool ExpireEntryIn(string key, TimeSpan expireIn)
  		{
  			return client.Expire(key, (int)expireIn.TotalSeconds);
  		}
  
  		public bool ExpireEntryAt(string key, DateTime expireAt)
  		{
  			return client.ExpireAt(key, expireAt.ToUnixTime());
  		}
  
  		public bool ExpireIn(object id, TimeSpan expireIn)
  		{
  			var key = client.UrnKey<T>(id);
  			return client.Expire(key, (int)expireIn.TotalSeconds);
  		}
  
  		public bool ExpireAt(object id, DateTime expireAt)
  		{
  			var key = client.UrnKey<T>(id);
  
gt: 			return client.ExpireAt(key, expireAt.ToUnixTime());