/*
NAME
  Config.go

DESCRIPTION
  See Readme.md

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

LICENSE
  Config.go is Copyright (C) 2017 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 [GNU licenses](http://www.gnu.org/licenses).
*/

package revid

import (
	"errors"
	"strconv"

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

// Config provides parameters relevant to a revid instance. A new config must
// be passed to the constructor.
type Config struct {
	Input              uint8
	InputCodec         uint8
	Output             uint8
	RtmpMethod         uint8
	Packetization      uint8
	QuantizationMode   uint8
	Verbosity          uint8
	HorizontalFlip     uint8
	VerticalFlip       uint8
	FramesPerClip      string
	RtmpUrl            string
	Bitrate            string
	OutputFileName     string
	InputFileName      string
	Height             string
	Width              string
	FrameRate          string
	HttpAddress        string
	Quantization       string
	Timeout            string
	IntraRefreshPeriod string
	Logger             smartlogger.LogInstance
}

// Enums for config struct
const (
	NothingDefined  = 0
	Raspivid        = 1
	Rtp             = 2
	H264Codec       = 3
	File            = 4
	Http            = 5
	H264            = 6
	Mjpeg           = 7
	None            = 8
	Mpegts          = 9
	Ffmpeg          = 11
	Flv             = 13
	LibRtmp         = 14
	QuantizationOn  = 15
	QuantizationOff = 16
	Yes             = 17
	No              = 18
	NativeRtmp      = 19
	FfmpegRtmp      = 20
)

// Default config settings
const (
	defaultInput              = Raspivid
	defaultOutput             = NativeRtmp
	defaultPacketization      = Flv
	defaultRtmpUrl            = "rtmp://a.rtmp.youtube.com/live2/vq8y-wzxh-731t-7rtb"
	defaultFrameRate          = "25"
	defaultWidth              = "1280"
	defaultHeight             = "720"
	defaultIntraRefreshPeriod = "100"
	defaultTimeout            = "0"
	defaultQuantization       = "40"
	defaultBitrate            = "500000"
	defaultQuantizationMode   = QuantizationOff
	defaultFramesPerClip      = "1"
	defaultVerticalFlip       = No
	defaultHorizontalFlip     = No
)

// Validate checks for any errors in the config fields and defaults settings
// if particular parameters have not been defined.
func (config *Config) Validate(r *revid) error {
	switch config.Verbosity {
	case Yes:
	case No:
	case NothingDefined:
		config.Verbosity = Yes
		r.Log(Warning, "No verbosity mode defined, defaulting to no Verbosity!")
	default:
		return errors.New("Bad Verbosity defined in config!")
	}

	switch config.QuantizationMode {
	case QuantizationOn:
	case QuantizationOff:
	case NothingDefined:
		r.Log(Warning, "No quantization mode defined, defaulting to QuantizationOff!")
		config.QuantizationMode = QuantizationOff
	default:
		return errors.New("Bad QuantizationMode defined in config!")
	}

	switch config.Input {
	case Rtp:
	case Raspivid:
	case File:
	case NothingDefined:
		r.Log(Warning, "No input type defined, defaulting to raspivid!")
		config.Input = defaultInput
	default:
		return errors.New("Bad input type defined in config!")
	}

	switch config.InputCodec {
	case H264:
		if config.Bitrate != "" && config.Quantization != "" {
			bitrate, err := strconv.Atoi(config.Bitrate)
			if err != nil {
				return errors.New("Something is wrong with bitrate in conig!")
			}
			quantization, err := strconv.Atoi(config.Quantization)
			if err != nil {
				return errors.New("Something is wrong with quantization in config!")
			}
			if (bitrate > 0 && quantization > 0) || (bitrate == 0 && quantization == 0) {
				return errors.New("Bad bitrate and quantization combination for H264 input!")
			}
		}
	case Mjpeg:
		if config.Quantization != "" {
			quantization, err := strconv.Atoi(config.Quantization)
			if err != nil {
				return errors.New("Something is wrong with quantization in config!")
			}
			if quantization > 0 || config.Bitrate == "" {
				return errors.New("Bad bitrate or quantization for mjpeg input!")
			}
		}
	case NothingDefined:
		r.Log(Warning, "No input codec defined, defaulting to h264!")
		config.InputCodec = H264
		r.Log(Warning, "Defaulting bitrate to 0 and quantization to 35!")
		config.Quantization = defaultQuantization
	default:
		return errors.New("Bad input codec defined in config!")
	}

	switch config.Output {
	case Http:
	case File:
	case NativeRtmp:
		if config.RtmpUrl == "" {
			return errors.New("Bad RTMP URL")
		}
		r.Log(Info, "Defaulting frames per clip to 1 for rtmp output!")
		config.FramesPerClip = "1"
	case FfmpegRtmp:
		if config.RtmpUrl == "" {
			return errors.New("Bad RTMP URL")
		}
		r.Log(Info, "Defaulting frames per clip to 1 for rtmp output!")
		config.FramesPerClip = "1"
	case NothingDefined:
		r.Log(Warning, "No output defined, defaulting to httpOut!")
		config.Output = defaultOutput
	default:
		return errors.New("Bad output type defined in config!")
	}

	switch config.Packetization {
	case None:
	case Mpegts:
	case Flv:
	case NothingDefined:
		r.Log(Warning, "No packetization option defined, defaulting to none!")
		config.Packetization = Flv
	default:
		return errors.New("Bad packetization option defined in config!")
	}

	switch config.HorizontalFlip {
	case Yes:
	case No:
	case NothingDefined:
		r.Log(Warning, "No horizontal flip option defined, defaulting to not flipped!")
		config.HorizontalFlip = defaultHorizontalFlip
	default:
		return errors.New("Bad horizontal flip option defined in config!")
	}

	switch config.VerticalFlip {
	case Yes:
	case No:
	case NothingDefined:
		r.Log(Warning, "No vertical flip option defined, defaulting to not flipped!")
		config.VerticalFlip = defaultVerticalFlip
	default:
		return errors.New("Bad vertical flip option defined in config!")
	}

	if config.FramesPerClip == "" {
		r.Log(Warning, "No FramesPerClip defined defined, defaulting to 1!")
		config.Width = defaultFramesPerClip
	} else {
		if integer, err := strconv.Atoi(config.FramesPerClip); integer <= 0 || err != nil {
			return errors.New("Bad width defined in config!")
		}
	}

	if config.Width == "" {
		r.Log(Warning, "No width defined, defaulting to 1280!")
		config.Width = defaultWidth
	} else {
		if integer, err := strconv.Atoi(config.Width); integer < 0 || err != nil {
			return errors.New("Bad width defined in config!")
		}
	}

	if config.Height == "" {
		r.Log(Warning, "No height defined, defaulting to 720!")
		config.Height = defaultHeight
	} else {
		if integer, err := strconv.Atoi(config.Height); integer < 0 || err != nil {
			return errors.New("Bad height defined in config!")
		}
	}

	if config.FrameRate == "" {
		r.Log(Warning, "No frame rate defined, defaulting to 25!")
		config.FrameRate = defaultFrameRate
	} else {
		if integer, err := strconv.Atoi(config.FrameRate); integer < 0 || err != nil {
			return errors.New("Bad frame rate defined in config!")
		}
	}

	if config.Bitrate == "" {
		r.Log(Warning, "No bitrate defined, defaulting!")
		config.Bitrate = defaultBitrate
	} else {
		if integer, err := strconv.Atoi(config.Bitrate); integer < 0 || err != nil {
			return errors.New("Bad bitrate defined in config!")
		}
	}

	if config.Timeout == "" {
		r.Log(Warning, "No timeout defined, defaulting to 0!")
		config.Timeout = defaultTimeout
	} else {
		if integer, err := strconv.Atoi(config.Timeout); integer < 0 || err != nil {
			return errors.New("Bad timeout defined in config!")
		}
	}

	if config.IntraRefreshPeriod == "" {
		r.Log(Warning, "No intra refresh defined, defaulting to 100!")
		config.IntraRefreshPeriod = defaultIntraRefreshPeriod
	} else {
		if integer, err := strconv.Atoi(config.IntraRefreshPeriod); integer < 0 || err != nil {
			return errors.New("Bad intra refresh defined in config!")
		}
	}

	if config.Quantization == "" {
		r.Log(Warning, "No quantization defined, defaulting to 35!")
		config.Quantization = defaultQuantization
	} else {
		if integer, err := strconv.Atoi(config.Quantization); integer < 0 || integer > 51 || err != nil {
			return errors.New("Bad quantization defined in config!")
		}
	}
	return nil
}