package redis

import (
	

	
	
)

// TxFailedErr transaction redis failed.
const TxFailedErr = proto.RedisError("redis: transaction failed")

// Tx implements Redis transactions as described in
// http://redis.io/topics/transactions. It's NOT safe for concurrent use
// by multiple goroutines, because Exec resets list of watched keys.
//
// If you don't need WATCH, use Pipeline instead.
type Tx struct {
	baseClient
	cmdable
	statefulCmdable
	hooksMixin
}

func ( *Client) () *Tx {
	 := Tx{
		baseClient: baseClient{
			opt:      .opt,
			connPool: pool.NewStickyConnPool(.connPool),
		},
		hooksMixin: .hooksMixin.clone(),
	}
	.init()
	return &
}

func ( *Tx) () {
	.cmdable = .Process
	.statefulCmdable = .Process

	.initHooks(hooks{
		dial:       .baseClient.dial,
		process:    .baseClient.process,
		pipeline:   .baseClient.processPipeline,
		txPipeline: .baseClient.processTxPipeline,
	})
}

func ( *Tx) ( context.Context,  Cmder) error {
	 := .processHook(, )
	.SetErr()
	return 
}

// Watch prepares a transaction and marks the keys to be watched
// for conditional execution if there are any keys.
//
// The transaction is automatically closed when fn exits.
func ( *Client) ( context.Context,  func(*Tx) error,  ...string) error {
	 := .newTx()
	defer .Close()
	if len() > 0 {
		if  := .Watch(, ...).Err();  != nil {
			return 
		}
	}
	return ()
}

// Close closes the transaction, releasing any open resources.
func ( *Tx) ( context.Context) error {
	_ = .Unwatch().Err()
	return .baseClient.Close()
}

// Watch marks the keys to be watched for conditional execution
// of a transaction.
func ( *Tx) ( context.Context,  ...string) *StatusCmd {
	 := make([]interface{}, 1+len())
	[0] = "watch"
	for ,  := range  {
		[1+] = 
	}
	 := NewStatusCmd(, ...)
	_ = .Process(, )
	return 
}

// Unwatch flushes all the previously watched keys for a transaction.
func ( *Tx) ( context.Context,  ...string) *StatusCmd {
	 := make([]interface{}, 1+len())
	[0] = "unwatch"
	for ,  := range  {
		[1+] = 
	}
	 := NewStatusCmd(, ...)
	_ = .Process(, )
	return 
}

// Pipeline creates a pipeline. Usually it is more convenient to use Pipelined.
func ( *Tx) () Pipeliner {
	 := Pipeline{
		exec: func( context.Context,  []Cmder) error {
			return .processPipelineHook(, )
		},
	}
	.init()
	return &
}

// Pipelined executes commands queued in the fn outside of the transaction.
// Use TxPipelined if you need transactional behavior.
func ( *Tx) ( context.Context,  func(Pipeliner) error) ([]Cmder, error) {
	return .Pipeline().Pipelined(, )
}

// TxPipelined executes commands queued in the fn in the transaction.
//
// When using WATCH, EXEC will execute commands only if the watched keys
// were not modified, allowing for a check-and-set mechanism.
//
// Exec always returns list of commands. If transaction fails
// TxFailedErr is returned. Otherwise Exec returns an error of the first
// failed command or nil.
func ( *Tx) ( context.Context,  func(Pipeliner) error) ([]Cmder, error) {
	return .TxPipeline().Pipelined(, )
}

// TxPipeline creates a pipeline. Usually it is more convenient to use TxPipelined.
func ( *Tx) () Pipeliner {
	 := Pipeline{
		exec: func( context.Context,  []Cmder) error {
			 = wrapMultiExec(, )
			return .processTxPipelineHook(, )
		},
	}
	.init()
	return &
}

func wrapMultiExec( context.Context,  []Cmder) []Cmder {
	if len() == 0 {
		panic("not reached")
	}
	 := make([]Cmder, len()+2)
	[0] = NewStatusCmd(, "multi")
	copy([1:], )
	[len()-1] = NewSliceCmd(, "exec")
	return 
}