/*
NAME
  Config.go

AUTHORS
  Saxon A. Nelson-Milton <saxon@ausocean.org>
  Trek Hopton <trek@ausocean.org>

LICENSE
  Config.go is Copyright (C) 2017-2018 the Australian Ocean Lab (AusOcean)

  It is free software: you can redistribute it and/or modify them
  under the terms of the GNU General Public License as published by the
  Free Software Foundation, either version 3 of the License, or (at your
  option) any later version.

  It is distributed in the hope that it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 for more details.

  You should have received a copy of the GNU General Public License
  along with revid in gpl.txt.  If not, see http://www.gnu.org/licenses.
*/

// Package config contains the configuration settings for revid.
package config

import (
	"time"

	"bitbucket.org/ausocean/utils/logger"
)

type Logger interface {
	SetLevel(int8)
	Log(level int8, message string, params ...interface{})
}

// Enums to define inputs, outputs and codecs.
const (
	// Indicates no option has been set.
	NothingDefined = iota

	// Input/Output.
	InputFile
	InputRaspivid
	InputV4L
	InputRTSP
	InputAudio

	// Outputs.
	OutputRTMP
	OutputRTP
	OutputHTTP
	OutputMPEGTS
	OutputFile

	// Codecs.
	H264
	H265
	MJPEG
)

// Quality represents video quality.
type Quality int

// The different video qualities that can be used for variable bitrate when
// using the GeoVision camera.
const (
	QualityStandard Quality = iota
	QualityFair
	QualityGood
	QualityGreat
	QualityExcellent
)

// The different media filters.
const (
	FilterNoOp = iota
	FilterMOG
	FilterVariableFPS
	FilterKNN
	FilterDiff
	FilterBasic
)

// Config provides parameters relevant to a revid instance. A new config must
// be passed to the constructor. Default values for these fields are defined
// as consts above.
type Config struct {
	// AutoWhiteBalance defines the auto white balance mode used by Raspivid input.
	// Valid modes are defined in the exported []string AutoWhiteBalanceModes
	// defined at the start of the file.
	AutoWhiteBalance string

	BitDepth    uint // Sample bit depth.
	Bitrate     uint // Bitrate specifies the bitrate for constant bitrate in kbps.
	Brightness  uint
	BurstPeriod uint  // BurstPeriod defines the revid burst period in seconds.
	CameraChan  uint8 // This is the channel we're using for the GeoVision camera.

	// CameraIP is the IP address of the camera in the case of the input camera
	// being an IP camera.
	CameraIP string

	// CBR indicates whether we wish to use constant or variable bitrate. If CBR
	// is true then we will use constant bitrate, and variable bitrate otherwise.
	// In the case of the Pi camera, variable bitrate quality is controlled by
	// the Quantization parameter below. In the case of the GeoVision camera,
	// variable bitrate quality is controlled by firstly the VBRQuality parameter
	// and second the VBRBitrate parameter.
	CBR bool

	Channels uint // Number of audio channels, 1 for mono, 2 for stereo.

	// ClipDuration is the duration of MTS data that is sent using HTTP or RTP
	// output. This defaults to 0, therefore MinFrames will determine the length of
	// clips by default.
	ClipDuration time.Duration

	// Exposure defines the exposure mode used by the Raspivid input. Valid modes
	// are defined in the exported []string ExposureModes defined at the start
	// of the file.
	Exposure string

	FileFPS uint   // Defines the rate at which frames from a file source are processed.
	Filters []uint // Defines the methods of filtering to be used in between lexing and encoding.

	// FrameRate defines the input frame rate if configurable by the chosen input.
	// Raspivid input supports custom framerate.
	FrameRate uint

	Height         uint // Height defines the input video height Raspivid input.
	HorizontalFlip bool // HorizontalFlip flips video horizontally for Raspivid input.

	// HTTPAddress defines a custom HTTP destination if we do not wish to use that
	// defined in /etc/netsender.conf.
	HTTPAddress string

	// Input defines the input data source.
	//
	// Valid values are defined by enums:
	// InputRaspivid:
	//		Read data from a Raspberry Pi Camera.
	// InputV4l:
	//		Read from webcam.
	// InputFile:
	//		Read h.264 bytestream from a file.
	// 		Location must be specified in InputPath field.
	// InputRTSP:
	//		Read from a camera supporting RTSP communication.
	//		CameraIP should also be defined.
	// InputAudio:
	//		Read from a ALSA audio source.
	Input uint8

	// InputCodec defines the input codec we wish to use, and therefore defines the
	// lexer for use in the pipeline. This defaults to H264, but H265 is also a
	// valid option if we expect this from the input.
	InputCodec uint8

	// InputPath defines the input file location for File Input. This must be
	// defined if File input is to be used.
	InputPath string

	// Logger holds an implementation of the Logger interface as defined in revid.go.
	// This must be set for revid to work correctly.
	Logger Logger

	// LogLevel is the revid logging verbosity level.
	// Valid values are defined by enums from the logger package: logger.Debug,
	// logger.Info, logger.Warning logger.Error, logger.Fatal.
	LogLevel int8

	Loop   bool // If true will restart reading of input after an io.EOF.
	MinFPS uint // The reduced framerate of the video when there is no motion.

	// MinFrames defines the frequency of key NAL units SPS, PPS and IDR in
	// number of NAL units. This will also determine the frequency of PSI if the
	// output container is MPEG-TS. If ClipDuration is less than MinFrames,
	// ClipDuration will default to MinFrames.
	MinFrames uint

	MotionDownscaling uint    // Downscaling factor of frames used for motion detection.
	MotionHistory     uint    // Length of filter's history (KNN & MOG only).
	MotionInterval    uint    // Sets the number of frames that are held before the filter is used (on the nth frame).
	MotionKernel      uint    // Size of kernel used for filling holes and removing noise (KNN only).
	MotionMinArea     float64 // Used to ignore small areas of motion detection (KNN & MOG only).
	MotionPadding     uint    // Number of frames to keep before and after motion detected.
	MotionPixels      uint    // Number of pixels with motion that is needed for a whole frame to be considered as moving (Basic only).
	MotionThreshold   float64 // Intensity value that is considered motion.

	// OutputPath defines the output destination for File output. This must be
	// defined if File output is to be used.
	OutputPath string

	// Outputs define the outputs we wish to output data too.
	//
	// Valid outputs are defined by enums:
	// OutputFile:
	// 		Location must be defined by the OutputPath field. MPEG-TS packetization
	//		is used.
	// OutputHTTP:
	// 		Destination is defined by the sh field located in /etc/netsender.conf.
	// 		MPEGT-TS packetization is used.
	// OutputRTMP:
	// 		Destination URL must be defined in the RtmpUrl field. FLV packetization
	//		is used.
	// OutputRTP:
	// 		Destination is defined by RtpAddr field, otherwise it will default to
	//		localhost:6970. MPEGT-TS packetization is used.
	Outputs []uint8

	PSITime        uint    // Sets the time between a packet being sent.
	Quantization   uint    // Quantization defines the quantization level, which will determine variable bitrate quality in the case of input from the Pi Camera.
	RBCapacity     uint    // The number of bytes the ring buffer will occupy.
	RBWriteTimeout uint    // The ringbuffer write timeout in seconds.
	RecPeriod      float64 // How many seconds to record at a time.
	Rotation       uint    // Rotation defines the video rotation angle in degrees Raspivid input.
	RTMPURL        string  // RTMPURL specifies the Rtmp output destination URL. This must be defined if RTMP is to be used as an output.
	RTPAddress     string  // RTPAddress defines the RTP output destination.
	SampleRate     uint    // Samples a second (Hz).
	Saturation     int
	Suppress       bool // Holds logger suppression state.
	VBRBitrate     uint // VBRBitrate describes maximal variable bitrate.

	// VBRQuality describes the general quality of video from the GeoVision camera
	// under variable bitrate. VBRQuality can be one 5 consts defined:
	// qualityStandard, qualityFair, qualityGood, qualityGreat and qualityExcellent.
	VBRQuality Quality

	VerticalFlip bool // VerticalFlip flips video vertically for Raspivid input.
	Width        uint // Width defines the input video width Raspivid input.
}

// Validate checks for any errors in the config fields and defaults settings
// if particular parameters have not been defined.
func (c *Config) Validate() error {
	for _, v := range Variables {
		if v.Validate != nil {
			v.Validate(c)
		}
	}
	return nil
}

// Update takes a map of configuration variable names and their corresponding
// values, parses the string values and converting into correct type, and then
// sets the config struct fields as appropriate.
func (c *Config) Update(vars map[string]string) {
	for _, value := range Variables {
		if v, ok := vars[value.Name]; ok && value.Update != nil {
			value.Update(c, v)
		}
	}
}

func (c *Config) LogInvalidField(name string, def interface{}) {
	c.Logger.Log(logger.Info, name+" bad or unset, defaulting", name, def)
}

func stringInSlice(want string, slice []string) bool {
	for _, s := range slice {
		if s == want {
			return true
		}
	}
	return false
}