//
// LogConfiguration.cs
//
// Author:
//       Carsten Sonne Larsen <cs@innolan.dk>
//
// Copyright (c) 2013-2016 Carsten Sonne Larsen
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using System;
using System.Collections.Generic;
using Ntp.Config;

namespace Ntp.Log
{
    public sealed class LogConfiguration : ConfigurationBase
    {
        private LogConfiguration(
            string configName,
            int indentLevel,
            LogType logType,
            Severity treshold,
            bool showTimestamp,
            bool showSeverity,
            string timeFormat,
            string file
            )
            : base(configName, indentLevel)
        {
            this.logType = logType;
            this.treshold = treshold;
            this.file = file;
            this.showTimestamp = showTimestamp;
            this.showSeverity = showSeverity;
            this.timeFormat = timeFormat;
        }

        public const string Identifier = LogConfigurationKeyword.Log;

        private const string DefaultLogTypeText = LogConfigurationKeyword.TypeFile;
        private const string DefaultTresholdTypeText = LogConfigurationKeyword.TresholdInfo;
        private const string DefaultTimeFormat = "yyyy-MM-dd HH:mm:ss";
        private const bool DefaultShowTimestamp = true;
        private const bool DefaultShowSeverity = true;

        private static readonly String[] Params =
        {
            Identifier,
            LogConfigurationKeyword.Type,
            LogConfigurationKeyword.Severity,
            LogConfigurationKeyword.ShowTimestamp,
            LogConfigurationKeyword.ShowSeverity,
            LogConfigurationKeyword.TimeFormat,
            LogConfigurationKeyword.File
        };

        private readonly string file;
        private readonly LogType logType;
        private readonly bool showSeverity;
        private readonly bool showTimestamp;
        private readonly string timeFormat;
        private readonly Severity treshold;

        public string File
        {
            get { return file; }
        }

        public Severity Treshold
        {
            get { return treshold; }
        }

        public LogType LogType
        {
            get { return logType; }
        }

        public bool ShowTimestamp
        {
            get { return showTimestamp; }
        }

        public bool ShowSeverity
        {
            get { return showSeverity; }
        }

        public string TimeFormat
        {
            get { return timeFormat; }
        }

        protected override IEnumerable<Object> Items
        {
            get
            {
                var items = new List<object> {ConfigName};

                if (ShowDefault || (!ShowDefault && LogType != LogType.FileLog))
                    items.Add(GetLogTypeText());

                if (ShowDefault || (!ShowDefault && Treshold != Severity.Info))
                    items.Add(ConvertFromSeverity(Treshold));

                if (ShowDefault || (!ShowDefault && ShowTimestamp != DefaultShowTimestamp))
                    items.Add(ShowTimestamp);

                if (ShowDefault || (!ShowDefault && ShowSeverity != DefaultShowSeverity))
                    items.Add(ShowSeverity);

                if (ShowDefault || (!ShowDefault && TimeFormat != DefaultTimeFormat))
                    items.Add(TimeFormat);

                if (File != null)
                    items.Add(File);

                return items.ToArray();
            }
        }

        public override string ToString()
        {
            var list = new List<string> {Identifier};

            if (ShowDefault || (!ShowDefault && LogType != LogType.FileLog))
                list.Add(LogConfigurationKeyword.Type);

            if (ShowDefault || (!ShowDefault && Treshold != Severity.Info))
                list.Add(LogConfigurationKeyword.Severity);

            if (ShowDefault || (!ShowDefault && ShowTimestamp != DefaultShowTimestamp))
                list.Add(LogConfigurationKeyword.ShowTimestamp);

            if (ShowDefault || (!ShowDefault && ShowSeverity != DefaultShowSeverity))
                list.Add(LogConfigurationKeyword.ShowSeverity);

            if (ShowDefault || (!ShowDefault && TimeFormat != DefaultTimeFormat))
                list.Add(LogConfigurationKeyword.TimeFormat);

            if (File != null)
                list.Add(LogConfigurationKeyword.File);

            return ConfigText(list.ToArray(), Items);
        }

        private string GetLogTypeText()
        {
            switch (logType)
            {
                case LogType.ConsoleLog:
                    return LogConfigurationKeyword.TypeConsole;
                case LogType.SysLog:
                    return LogConfigurationKeyword.TypeSyslog;
                case LogType.FileLog:
                    return LogConfigurationKeyword.TypeFile;
                default:
                    return LogConfigurationKeyword.TypeUnknown;
            }
        }

        private static LogType GetLogType(string text)
        {
            if (text == null)
                return LogType.UnknownLog;

            string typeText = text.ToLower();

            if (typeText == LogConfigurationKeyword.TypeConsole.ToLower())
            {
                return LogType.ConsoleLog;
            }

            if (typeText == LogConfigurationKeyword.TypeSyslog.ToLower())
            {
                return LogType.SysLog;
            }

            if (typeText == LogConfigurationKeyword.TypeFile.ToLower())
            {
                return LogType.FileLog;
            }

            return LogType.UnknownLog;
        }

        private static Severity ConvertToSeverity(string text)
        {
            switch (text.ToLower())
            {
                case LogConfigurationKeyword.TresholdError:
                    return Severity.Error;
                case LogConfigurationKeyword.TresholdWarn:
                    return Severity.Warn;
                case LogConfigurationKeyword.TresholdNotice:
                    return Severity.Notice;
                case LogConfigurationKeyword.TresholdInfo:
                    return Severity.Info;
                case LogConfigurationKeyword.TresholdDebug:
                    return Severity.Debug;
                case LogConfigurationKeyword.TresholdTrace:
                    return Severity.Trace;
                default:
                    throw new ArgumentException("Unknown severity type: " + text);
            }
        }

        private static string ConvertFromSeverity(Severity severity)
        {
            switch (severity)
            {
                case Severity.Error:
                    return LogConfigurationKeyword.TresholdError;
                case Severity.Warn:
                    return LogConfigurationKeyword.TresholdWarn;
                case Severity.Notice:
                    return LogConfigurationKeyword.TresholdNotice;
                case Severity.Info:
                    return LogConfigurationKeyword.TresholdInfo;
                case Severity.Debug:
                    return LogConfigurationKeyword.TresholdDebug;
                case Severity.Trace:
                    return LogConfigurationKeyword.TresholdTrace;
                default:
                    throw new ArgumentException("Unknown severity type: " + severity);
            }
        }

        public static LogConfiguration Create(ConfigurationBlock config)
        {
            Dictionary<String, String> options = GetOptions(Params, config);

            string logType = GetOptionString(
                options, LogConfigurationKeyword.Type, Identifier, false, DefaultLogTypeText);

            string treshold = GetOptionString(
                options, LogConfigurationKeyword.Severity, Identifier, false, DefaultTresholdTypeText);

            string file = GetOptionString(options, LogConfigurationKeyword.File, Identifier, false);

            if (logType.ToLower() == LogConfigurationKeyword.TypeFile.ToLower() && file == null)
            {
                ConfigError("Log file name is missing.");
            }

            return new LogConfiguration(
                GetOptionConfigName(options, Identifier, String.Empty, false),
                config.IndentLevel,
                GetLogType(logType),
                ConvertToSeverity(treshold),
                GetOptionBoolean(options, LogConfigurationKeyword.ShowTimestamp, Identifier, false, DefaultShowTimestamp),
                GetOptionBoolean(options, LogConfigurationKeyword.ShowSeverity, Identifier, false, DefaultShowSeverity),
                GetOptionString(options, LogConfigurationKeyword.TimeFormat, Identifier, false, DefaultTimeFormat),
                file
                );
        }
    }
}