/*
 * Minio Cloud Storage, (C) 2015 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package tasker

import (
	"container/list"
	"sync"
)

// NOTE: Task is a private entity. It is created and managed by TaskCtl
//      entirely. Only TaskCtl and Handle objects are exposed outside.

// taskRef is a unique reference ID to a task. It is assigned by the
// TaskCtl during the creation of a task. All tasfRef variables are
// named "this".
type taskRef *list.Element

// Task is an abstract concept built on top of Go routines and
// channels. Tasks themselves are expected to co-operate and comply with
// the TaskCtl commands.

type task struct {
	mutex *sync.Mutex

	this     taskRef      // Refence to task entry in the TaskCtl's task list.
	name     string       // Free form name.
	priority Command      // Current priority.
	cmdCh    chan Command // Channel to receive commands from TaskCtl.
	statusCh chan status  // Channel to send completion status and error (if any) to TaskCtl.
	closeCh  chan taskRef // Channel to notify the TaskCtl about ending this task.
}

// NewTask creates a new task structure and returns a handle to
// it. Only the task controller has access to the task structure. The
// caller routine only receives a handle to its task structure. Task
// handle is like a reference to task self. Caller is expected to listen
// for commands from the task controller and comply with it co-operatively.
// this: Task reference is unique identifier assigned by the TaskCtl.
// name: Free form name of the task. Eg. "Late Night Disk Scrubber".
func newTask(name string) task {
	return task{
		// this: Is set by the TaskCtl's NewTask function.
		mutex:    &sync.Mutex{},
		name:     name,
		priority: CmdPriorityMedium,
		cmdCh:    make(chan Command),
		statusCh: make(chan status),
		closeCh:  make(chan taskRef),
	}
}

// getHandle returns a handle to the task. Handle has limited access to the task structure and it is safe to be exposed.
func (t task) getHandle() Handle {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	// Make a handle with limited access to channels (only send or receive).
	return Handle{
		cmdCh:    t.cmdCh,
		statusCh: t.statusCh,
		closeCh:  t.closeCh,
	}
}

// command method sends a command code to the task and returns its completion status.
func (t task) command(cmd Command) status {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	t.cmdCh <- cmd
	return <-t.statusCh
}

// close releases all the resources held by this task.
func (t task) close() {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	// Task can be ended in 2 ways.
	// 1) Calling application invokes Handle.Close().
	// 2) TaskCtl.Shutdown() ending the task's life.
	// In either case, task.close() is invoked only via the
	// TaskCtl. Handle.Close() only sends a message to the TaskCtl to
	// initiate a close call.

	close(t.cmdCh)
	close(t.statusCh)
	close(t.closeCh)
}