/*
NAME
  Config.go

DESCRIPTION
  See Readme.md

AUTHORS
  Saxon A. Nelson-Milton <saxon@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 revid

import (
	"errors"

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

// Config provides parameters relevant to a revid instance. A new config must
// be passed to the constructor.
type Config struct {
	LogLevel int8

	Input         uint8
	InputCodec    uint8
	Output1       uint8
	Output2       uint8
	RtmpMethod    uint8
	Packetization uint8

	// Quantize specifies whether the input to
	// revid will have constant or variable
	// bitrate.
	Quantize bool

	// FlipHorizonatla and FlipVertical specify
	// whether video frames should be flipped.
	FlipHorizontal bool
	FlipVertical   bool

	FramesPerClip      uint
	RtmpUrl            string
	Bitrate            uint
	OutputFileName     string
	InputFileName      string
	Height             uint
	Width              uint
	FrameRate          uint
	HttpAddress        string
	Quantization       uint
	IntraRefreshPeriod uint
	RtpAddress         string
	Logger             Logger
	SendRetry          bool
}

// Enums for config struct
const (
	NothingDefined = iota
	Raspivid
	V4L
	H264Codec
	File
	Http
	H264
	Mjpeg
	None
	Mpegts
	Ffmpeg
	Flv
	LibRtmp
	QuantizationOn
	QuantizationOff
	Yes
	No
	Rtmp
	FfmpegRtmp
	Udp
	MpegtsRtp
	Rtp
)

// Default config settings
const (
	defaultInput              = Raspivid
	defaultOutput             = Http
	defaultPacketization      = Flv
	defaultFrameRate          = 25
	defaultWidth              = 1280
	defaultHeight             = 720
	defaultIntraRefreshPeriod = 100
	defaultTimeout            = 0
	defaultQuantization       = 40
	defaultBitrate            = 400000
	defaultQuantizationMode   = QuantizationOff
	defaultFramesPerClip      = 1
	httpFramesPerClip         = 560
	defaultInputCodec         = H264
	defaultVerbosity          = No // FIXME(kortschak): This makes no sense whatsoever. No is currently 15.
	defaultRtpAddr            = "localhost:6970"
)

// Validate checks for any errors in the config fields and defaults settings
// if particular parameters have not been defined.
func (c *Config) Validate(r *Revid) error {
	switch c.LogLevel {
	case Yes:
	case No:
	case NothingDefined:
		c.LogLevel = defaultVerbosity
		c.Logger.Log(logger.Warning, pkg+"no LogLevel mode defined, defaulting",
			"LogLevel", defaultVerbosity)
	default:
		return errors.New("bad LogLevel defined in config")
	}

	switch c.Input {
	case Raspivid, V4L, File:
	case NothingDefined:
		c.Logger.Log(logger.Warning, pkg+"no input type defined, defaulting", "input",
			defaultInput)
		c.Input = defaultInput
	default:
		return errors.New("bad input type defined in config")
	}

	switch c.InputCodec {
	case H264:
		// FIXME(kortschak): This is not really what we want.
		// Configuration really needs to be rethought here.
		if c.Quantize && c.Quantization == 0 {
			c.Quantization = defaultQuantization
		} else {
			c.Bitrate = defaultBitrate
		}

		if (c.Bitrate > 0 && c.Quantization > 0) || (c.Bitrate == 0 && c.Quantization == 0) {
			return errors.New("bad bitrate and quantization combination for H264 input")
		}

	case Mjpeg:
		if c.Quantization > 0 || c.Bitrate == 0 {
			return errors.New("bad bitrate or quantization for mjpeg input")
		}

	case NothingDefined:
		c.Logger.Log(logger.Warning, pkg+"no input codec defined, defaulting",
			"inputCodec", defaultInputCodec)
		c.InputCodec = defaultInputCodec
		c.Logger.Log(logger.Warning, pkg+"defaulting quantization", "quantization",
			defaultQuantization)
		c.Quantization = defaultQuantization

	default:
		return errors.New("bad input codec defined in config")
	}

	switch c.Output1 {
	case File:
	case Udp:
	case Rtmp, FfmpegRtmp:
		if c.RtmpUrl == "" {
			c.Logger.Log(logger.Info, pkg+"no RTMP URL: falling back to HTTP")
			c.Output1 = Http
			break
		}
		c.Logger.Log(logger.Info, pkg+"defaulting frames per clip for rtmp out",
			"framesPerClip", defaultFramesPerClip)
		c.FramesPerClip = defaultFramesPerClip
	case NothingDefined:
		c.Logger.Log(logger.Warning, pkg+"no output defined, defaulting", "output",
			defaultOutput)
		c.Output1 = defaultOutput
		fallthrough
	case Http, Rtp:
		c.Logger.Log(logger.Info, pkg+"defaulting frames per clip for http out",
			"framesPerClip", httpFramesPerClip)
		c.FramesPerClip = httpFramesPerClip
	default:
		return errors.New("bad output type defined in config")
	}

	switch c.Output2 {
	case File:
	case Rtp:
	case Udp:
	case Rtmp, FfmpegRtmp:
		if c.RtmpUrl == "" {
			c.Logger.Log(logger.Info, pkg+"no RTMP URL: falling back to HTTP")
			c.Output2 = Http
			break
		}
	case NothingDefined:
	case Http:
	default:
		return errors.New("bad output2 type defined in config")
	}

	if c.FramesPerClip < 1 {
		c.Logger.Log(logger.Warning, pkg+"no FramesPerClip defined, defaulting",
			"framesPerClip", defaultFramesPerClip)
		c.FramesPerClip = defaultFramesPerClip
	}

	if c.Width == 0 {
		c.Logger.Log(logger.Warning, pkg+"no width defined, defaulting", "width", defaultWidth)
		c.Width = defaultWidth
	}

	if c.Height == 0 {
		c.Logger.Log(logger.Warning, pkg+"no height defined, defaulting", "height", defaultHeight)
		c.Height = defaultHeight
	}

	if c.FrameRate == 0 {
		c.Logger.Log(logger.Warning, pkg+"no frame rate defined, defaulting", "fps", defaultFrameRate)
		c.FrameRate = defaultFrameRate
	}

	if c.Bitrate == 0 {
		c.Logger.Log(logger.Warning, pkg+"no bitrate defined, defaulting", "bitrate", defaultBitrate)
		c.Bitrate = defaultBitrate
	}

	if c.IntraRefreshPeriod == 0 {
		c.Logger.Log(logger.Warning, pkg+"no intra refresh defined, defaulting", "intraRefresh", defaultIntraRefreshPeriod)
		c.IntraRefreshPeriod = defaultIntraRefreshPeriod
	}

	if c.Quantization == 0 {
		c.Logger.Log(logger.Warning, pkg+"no quantization defined, defaulting", "quantization", defaultQuantization)
		c.Quantization = defaultQuantization
	} else if c.Quantization > 51 {
		return errors.New("quantisation is over threshold")
	}

	if c.RtpAddress == "" {
		c.RtpAddress = defaultRtpAddr
	}
	return nil
}