package calls

import (
	"time"

	"github.com/mesos/mesos-go/api/v1/lib"
	"github.com/mesos/mesos-go/api/v1/lib/maintenance"
	"github.com/mesos/mesos-go/api/v1/lib/master"
	"github.com/mesos/mesos-go/api/v1/lib/quota"
)

// GetHealth retrieves the health status of master.
func GetHealth() *master.Call {
	return &master.Call{
		Type: master.Call_GET_HEALTH,
	}
}

// GetFlags retrieves the master's overall flag configuration.
func GetFlags() *master.Call {
	return &master.Call{
		Type: master.Call_GET_FLAGS,
	}
}

// GetVersion retrieves the master's version information.
func GetVersion() *master.Call {
	return &master.Call{
		Type: master.Call_GET_VERSION,
	}
}

// GetMetrics gives the snapshot of current metrics to the end user. If timeout is set in the call, it will be used to
// determine the maximum amount of time the API will take to respond. If the timeout is exceeded, some metrics may not
// be included in the response.
func GetMetrics(d *time.Duration) (call *master.Call) {
	call = &master.Call{
		Type:       master.Call_GET_METRICS,
		GetMetrics: &master.Call_GetMetrics{},
	}
	if d != nil {
		call.GetMetrics.Timeout = &mesos.DurationInfo{
			Nanoseconds: d.Nanoseconds(),
		}
	}
	return
}

// GetLoggingLevel retrieves the master's logging level.
func GetLoggingLevel() *master.Call {
	return &master.Call{
		Type: master.Call_GET_LOGGING_LEVEL,
	}
}

// SetLoggingLevel sets the logging verbosity level for a specified duration for master. Mesos uses glog for logging.
// The library only uses verbose logging which means nothing will be output unless the verbosity level is set (by
// default it's 0, libprocess uses levels 1, 2, and 3).
func SetLoggingLevel(level uint32, d time.Duration) *master.Call {
	return &master.Call{
		Type: master.Call_SET_LOGGING_LEVEL,
		SetLoggingLevel: &master.Call_SetLoggingLevel{
			Level: level,
			Duration: mesos.DurationInfo{
				Nanoseconds: d.Nanoseconds(),
			},
		},
	}
}

// ListFiles retrieves the file listing for a directory in master.
func ListFiles(path string) *master.Call {
	return &master.Call{
		Type: master.Call_LIST_FILES,
		ListFiles: &master.Call_ListFiles{
			Path: path,
		},
	}
}

// ReadFile reads data from a file on the master. This call takes the path of the file to be read and the offset to
// start reading.
func ReadFile(path string, offset uint64) *master.Call {
	return &master.Call{
		Type: master.Call_READ_FILE,
		ReadFile: &master.Call_ReadFile{
			Path:   path,
			Offset: offset,
		},
	}
}

// ReadFileWithLength is similar to ReadFile but species the maximum number of bytes to read from the file.
func ReadFileWithLength(path string, offset, length uint64) *master.Call {
	call := ReadFile(path, offset)
	call.ReadFile.Length = &length
	return call
}

// GetState retrieves the overall cluster state.
func GetState() *master.Call {
	return &master.Call{
		Type: master.Call_GET_STATE,
	}
}

// GetAgents retrieves information about all the agents known to the master.
func GetAgents() *master.Call {
	return &master.Call{
		Type: master.Call_GET_AGENTS,
	}
}

// GetFrameworks retrieves information about all the frameworks known to the master.
func GetFrameworks() *master.Call {
	return &master.Call{
		Type: master.Call_GET_FRAMEWORKS,
	}
}

// GetExecutors retrieves information about all the executors known to the master.
func GetExecutors() *master.Call {
	return &master.Call{
		Type: master.Call_GET_EXECUTORS,
	}
}

// GetTasks retrieves information about all the tasks known to the master.
func GetTasks() *master.Call {
	return &master.Call{
		Type: master.Call_GET_TASKS,
	}
}

// GetRoles retrieves information about all the roles known to the master.
func GetRoles() *master.Call {
	return &master.Call{
		Type: master.Call_GET_ROLES,
	}
}

// GetWeights retrieves the information about role weights.
func GetWeights() *master.Call {
	return &master.Call{
		Type: master.Call_GET_WEIGHTS,
	}
}

// UpdateWeights updates weights for specific roles.
func UpdateWeights(weights ...mesos.WeightInfo) *master.Call {
	return &master.Call{
		Type: master.Call_UPDATE_WEIGHTS,
		UpdateWeights: &master.Call_UpdateWeights{
			WeightInfos: weights,
		},
	}
}

// GetMaster retrieves information about the master.
func GetMaster() *master.Call {
	return &master.Call{
		Type: master.Call_GET_MASTER,
	}
}

// Subscribe results in a streaming response. The client is expected to keep a persistent connection open to the
// endpoint even after getting a SUBSCRIBED HTTP Response event. This is indicated by "Connection: keep-alive" and
// "Transfer-Encoding: chunked" headers with no "Content-Length" header set. All subsequent events generated by Mesos
// are streamed on this connection. The master encodes each Event in RecordIO format, i.e., string representation of
// length of the event in bytes followed by JSON or binary Protobuf encoded event.
func Subscribe() *master.Call {
	return &master.Call{
		Type: master.Call_SUBSCRIBE,
	}
}

// ReserveResources reserves resources dynamically on a specific agent.
func ReserveResources(a mesos.AgentID, r ...mesos.Resource) *master.Call {
	return &master.Call{
		Type: master.Call_RESERVE_RESOURCES,
		ReserveResources: &master.Call_ReserveResources{
			AgentID:   a,
			Resources: r,
		},
	}
}

// UnreserveResources unreserves resources dynamically on a specific agent.
func UnreserveResources(a mesos.AgentID, r ...mesos.Resource) *master.Call {
	return &master.Call{
		Type: master.Call_UNRESERVE_RESOURCES,
		UnreserveResources: &master.Call_UnreserveResources{
			AgentID:   a,
			Resources: r,
		},
	}
}

// CreateVolumes creates persistent volumes on reserved resources. The request is forwarded asynchronously to the Mesos
// agent where the reserved resources are located. That asynchronous message may not be delivered or creating the
// volumes at the agent might fail.
func CreateVolumes(a mesos.AgentID, v ...mesos.Resource) *master.Call {
	return &master.Call{
		Type: master.Call_CREATE_VOLUMES,
		CreateVolumes: &master.Call_CreateVolumes{
			AgentID: a,
			Volumes: v,
		},
	}
}

// DestroyVolumes destroys persistent volumes. The request is forwarded asynchronously to the Mesos agent where the
// reserved resources are located.
func DestroyVolumes(a mesos.AgentID, v ...mesos.Resource) *master.Call {
	return &master.Call{
		Type: master.Call_DESTROY_VOLUMES,
		DestroyVolumes: &master.Call_DestroyVolumes{
			AgentID: a,
			Volumes: v,
		},
	}
}

// GetMaintenanceStatus retrieves the cluster's maintenance status.
func GetMaintenanceStatus() *master.Call {
	return &master.Call{
		Type: master.Call_GET_MAINTENANCE_STATUS,
	}
}

//GetMaintenanceSchedule retrieves the cluster's maintenance schedule.
func GetMaintenanceSchedule() *master.Call {
	return &master.Call{
		Type: master.Call_GET_MAINTENANCE_SCHEDULE,
	}
}

// UpdateMaintenanceSchedule updates the cluster's maintenance schedule.
func UpdateMaintenanceSchedule(s maintenance.Schedule) *master.Call {
	return &master.Call{
		Type: master.Call_UPDATE_MAINTENANCE_SCHEDULE,
		UpdateMaintenanceSchedule: &master.Call_UpdateMaintenanceSchedule{
			Schedule: s,
		},
	}
}

// StartMaintenance starts the maintenance of the cluster, this would bring a set of machines down.
func StartMaintenance(m ...mesos.MachineID) *master.Call {
	return &master.Call{
		Type: master.Call_START_MAINTENANCE,
		StartMaintenance: &master.Call_StartMaintenance{
			Machines: m,
		},
	}
}

// StopMaintenance stops the maintenance of the cluster, this would bring a set of machines back up.
func StopMaintenance(m ...mesos.MachineID) *master.Call {
	return &master.Call{
		Type: master.Call_STOP_MAINTENANCE,
		StopMaintenance: &master.Call_StopMaintenance{
			Machines: m,
		},
	}
}

// GetQuota retrieves the cluster's configured quotas.
func GetQuota() *master.Call {
	return &master.Call{
		Type: master.Call_GET_QUOTA,
	}
}

// SetQuota sets the quota for resources to be used by a particular role.
func SetQuota(qr quota.QuotaRequest) *master.Call {
	return &master.Call{
		Type: master.Call_SET_QUOTA,
		SetQuota: &master.Call_SetQuota{
			QuotaRequest: qr,
		},
	}
}

// RemoveQuota removes the quota for a particular role.
func RemoveQuota(role string) *master.Call {
	return &master.Call{
		Type: master.Call_REMOVE_QUOTA,
		RemoveQuota: &master.Call_RemoveQuota{
			Role: role,
		},
	}
}
