package redis

import (
	"fmt"

	"github.com/garyburd/redigo/redis"
	//"math/rand"

	"strconv"
	//"sync"
	"time"
	"errors"
)

type RedisClient struct {
	m_pool *redis.Pool
}

func NewRedisClient(host string, password string, db_index uint32) *RedisClient {
	client := new(RedisClient)
	client.m_pool = &redis.Pool{
		MaxIdle:     64,
		IdleTimeout: 60 * time.Second,
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")

			return err
		},
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", host)
			if err != nil {
				return nil, err
			}
			if password != "" {
				if _, err := c.Do("AUTH", password); err != nil {
					c.Close()
					return nil, err
				}
			}
			_, err = c.Do("SELECT", db_index)
			if err != nil {
				return nil, err
			}

			return c, err
		},
	}
	return client
}

// 写入值永不过期
func (client *RedisClient) SetString(key, value string) error {
	conn := client.m_pool.Get()
	defer conn.Close()
	_, err := conn.Do("SET", key, value)
	if err != nil {
		//fmt.Println("redis set failed:", err)
	}
	return err
}

func (client *RedisClient) SetBytes(key string, bys []byte) error {
	conn := client.m_pool.Get()
	defer conn.Close()
	_, err := conn.Do("SET", key, bys)
	if err != nil {
		//fmt.Println("redis set failed:", err)
	}
	return err
}

func (client *RedisClient) SetExString(key, value string, expireTime int /* seconds*/) error {
	conn := client.m_pool.Get()
	defer conn.Close()
	expStr := strconv.Itoa(expireTime)
	_, err := conn.Do("SETEX", key, expStr, value)
	if err != nil {
		//fmt.Println("redis set failed:", err)
	}
	return err
}

func (client *RedisClient) Delete(key string) error {
	conn := client.m_pool.Get()
	defer conn.Close()
	_, err := conn.Do("DEL", key)
	if err != nil {
		//fmt.Println("redis set failed:", err)
	}
	return err
}

func (client *RedisClient) Expire(key string, expireTime int /* seconds*/) error {
	conn := client.m_pool.Get()
	defer conn.Close()
	expStr := strconv.Itoa(expireTime)
	_, err := conn.Do("EXPIRE", key, expStr)
	if err != nil {
		//fmt.Println("redis set failed:", err)
	}
	return err
}

func (client *RedisClient) GetKeys(pattern string) ([]string, error) {
	conn := client.m_pool.Get()
	defer conn.Close()
	value, err := redis.Strings(conn.Do("keys", pattern))
	if err != nil {
		//fmt.Println("redis get failed:", err)
	}
	return value, err
}

func (client *RedisClient) GetString(key string) (string, error) {
	conn := client.m_pool.Get()
	defer conn.Close()
	value, err := redis.String(conn.Do("GET", key))
	if err != nil {
		//fmt.Println("redis get failed:", err)
	}
	return value, err
}
func (client *RedisClient) GetBytes(key string) ([]byte, error) {
	conn := client.m_pool.Get()
	defer conn.Close()
	value, err := conn.Do("GET", key)
	if err != nil {
		fmt.Println("redis get failed:", err)
	}
	return value.([]byte), err
}

func (client *RedisClient) GetInt64(key string) (int64, error) {
	conn := client.m_pool.Get()
	defer conn.Close()
	value, err := redis.Int64(conn.Do("GET", key))
	if err != nil {
		//fmt.Println("redis get failed:", err)
	}
	return value, err
}

func (client *RedisClient) GetUint64(key string) (uint64, error) {
	conn := client.m_pool.Get()
	defer conn.Close()
	value, err := redis.Uint64(conn.Do("GET", key))
	if err != nil {
		//fmt.Println("redis get failed:", err)
	}
	return value, err
}

func (client *RedisClient) GetInt(key string) (int, error) {
	conn := client.m_pool.Get()
	defer conn.Close()
	value, err := redis.Int(conn.Do("GET", key))
	if err != nil {
		//fmt.Println("redis get failed:", err)
	}
	return value, err
}

func (client *RedisClient) GetBool(key string) (bool, error) {
	conn := client.m_pool.Get()
	defer conn.Close()
	value, err := redis.Bool(conn.Do("GET", key))
	if err != nil {
		//fmt.Println("redis get failed:", err)
	}
	return value, err
}

func (client *RedisClient) Exists(key string) (bool, error) {
	conn := client.m_pool.Get()
	defer conn.Close()
	value, err := redis.Bool(conn.Do("EXISTS", key))
	if err != nil {
		//fmt.Println("redis get failed:", err)
	}
	return value, err
}

func (client *RedisClient) HMSet(key string, data interface{}) error {
	conn := client.m_pool.Get()
	defer conn.Close()
	if _, err := conn.Do("HMSET", redis.Args{}.Add(key).AddFlat(data)...); err != nil {
		//fmt.Println("redis set failed:", err)
		return err
	}

	return nil
}

func (client *RedisClient) 	HSet(key, field, value string) error {
	conn := client.m_pool.Get()
	defer conn.Close()
	if _, err := conn.Do("HSET", key, field, value); err != nil {
		//fmt.Println("redis set failed:", err)
		return err
	}

	return nil
}
func (client *RedisClient) HSetBytes(key, field string,  value []byte) error {
	conn := client.m_pool.Get()
	defer conn.Close()
	if _, err := conn.Do("HSET", key, field, value); err != nil {
		//fmt.Println("redis set failed:", err)
		return err
	}

	return nil
}

func (client *RedisClient) HDel(key, field string) error {
	conn := client.m_pool.Get()
	defer conn.Close()
	if _, err := conn.Do("HDEL", key, field); err != nil {
		return err
	}

	return nil
}

func (client *RedisClient) HGetAll(key string, data interface{}) error {
	conn := client.m_pool.Get()
	defer conn.Close()
	v, err := redis.Values(conn.Do("HGETALL", key))
	if err != nil {
		//fmt.Println("redis set failed:", err)
		return err
	}
	if err := redis.ScanStruct(v, data); err != nil {
		return err
	}
	return nil
}

func (client *RedisClient) HGetAllValues(key string) ([]interface{}, error) {
	conn := client.m_pool.Get()
	defer conn.Close()
	v, err := redis.Values(conn.Do("HGETALL", key))
	return v, err
}

func (client *RedisClient) HGetAllKeys(key string) ([]interface{}, error) {
	conn := client.m_pool.Get()
	defer conn.Close()
	v, err := redis.Values(conn.Do("hkeys", key))
	return v, err
}


func (client *RedisClient) HGet(key string, field string) (string, error) {
	conn := client.m_pool.Get()
	defer conn.Close()
	value, err := redis.String(conn.Do("HGET", key, field))
	if err != nil {
		//fmt.Println("redis set failed:", err)
		return "", err
	}
	return value, nil
}
func (client *RedisClient) HGetBytes(key, field string) ([]byte, error) {
	conn := client.m_pool.Get()
	defer conn.Close()
	value, err := conn.Do("HGET", key, field)
	if err != nil {
		fmt.Println("redis hget failed:", err)
		return []byte{}, err
	}
	if value == nil {
		return []byte{}, errors.New("value nil")
	}
	return value.([]byte), err
}

func (client *RedisClient) HGetInt(key string, field string) (int, error) {
	conn := client.m_pool.Get()
	defer conn.Close()
	value, err := redis.Int(conn.Do("HGET", key, field))
	if err != nil {
		//fmt.Println("redis set failed:", err)
		return 0, err
	}
	return value, nil
}

func (client *RedisClient) LPop(listName string) (string, error) {
	conn := client.m_pool.Get()
	defer conn.Close()
	value, err := redis.String(conn.Do("LPOP", listName))
	if err != nil {
		//fmt.Println("LPop redis get failed:", err)
	}
	return value, err
}

func (client *RedisClient) LRem(listName string, count int, value string) error {
	conn := client.m_pool.Get()
	defer conn.Close()
	cntStr := strconv.Itoa(count)
	_, err := redis.Int(conn.Do("LREM", listName, cntStr, value))
	if err != nil {
		//fmt.Println("LPop redis get failed:", err)
	}
	return err
}

func (client *RedisClient) RPush(listName string, element string) error {
	conn := client.m_pool.Get()
	defer conn.Close()
	_, err := redis.Int(conn.Do("RPUSH", listName, element))
	if err != nil {
		//fmt.Println("RPush redis get failed:", err)
	}
	return err
}

func (client *RedisClient) LLen(listName string) (int, error) {
	conn := client.m_pool.Get()
	defer conn.Close()
	value, err := redis.Int(conn.Do("LLEN", listName))
	if err != nil {
		//fmt.Println("LLEN redis get failed:", err)
	}
	return value, err
}

func (client *RedisClient) LIndex(listName string, index int) (string, error) {
	conn := client.m_pool.Get()
	defer conn.Close()
	idxStr := strconv.Itoa(index)
	value, err := redis.String(conn.Do("LINDEX", listName, idxStr))
	if err != nil {
		//fmt.Println("LIndex redis get failed:", err)
	}
	return value, err
}

func (client *RedisClient) LRange(listName string, start int, stop int) ([]string, error) {
	conn := client.m_pool.Get()
	defer conn.Close()
	value, err := redis.Strings(conn.Do("LRANGE", listName, start, stop))
	if err != nil {
		//fmt.Println("redis get failed:", err)
	}
	return value, err
}

func (client *RedisClient) Incr(key string) (uint64, error) {
	conn := client.m_pool.Get()
	defer conn.Close()
	value, err := redis.Uint64(conn.Do("INCR", key))
	if err != nil {
		//fmt.Println("RPush redis get failed:", err)
	}
	return value, err
}

func (client *RedisClient) IncrBy(key string, incr int) (uint64, error) {
	conn := client.m_pool.Get()
	defer conn.Close()
	value, err := redis.Uint64(conn.Do("INCRBY", key, incr))
	if err != nil {
		fmt.Println("redis get failed:", err)
	}
	return value, err
}

func (client *RedisClient) HMGets(key string) ([]interface{}, error) {
	conn := client.m_pool.Get()
	defer conn.Close()
	v, err := redis.Values(conn.Do("HGETALL", key))
	if err != nil {
		fmt.Println("redis get failed:", err)
		return nil, err
	}
	return v, err
}
