//
// HostConfiguration.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 System.Globalization;
using Ntp.Analyzer.Config.Graph;
using Ntp.Analyzer.Config.Navigation;
using Ntp.Analyzer.Config.Page;
using Ntp.Analyzer.Config.Root;
using Ntp.Analyzer.Config.Stats;
using Ntp.Analyzer.Import;
using Ntp.Analyzer.Localize;
using Ntp.Config;

namespace Ntp.Analyzer.Config
{
    public sealed class HostConfiguration : ConfigurationBase
    {
        private HostConfiguration(
            string configName,
            int indentLevel,
            int hostId,
            string serverName,
            ServerType serverType,
            string filePath,
            string webPath)
            : base(configName, indentLevel)
        {
            this.hostId = hostId;
            this.serverName = serverName;
            this.serverType = serverType;
            this.filePath = filePath;
            this.webPath = webPath;

            hostPages = new List<HostPageConfiguration>();
            hostGraphs = new List<HostGraphConfiguration>();
            trafficGraphs = new List<TrafficGraphConfiguration>();
            peerSummaryPages = new List<PeerSummaryPageConfiguration>();
            peerPages = new List<PeerPageConfiguration>();
            peerGraphs = new List<PeerGraphConfiguration>();
        }

        public const string Identifier = ConfigurationKeyword.Server;
        private const string DefaultWebPath = "/";

        private static readonly String[] Params =
        {
            ConfigurationKeyword.Server,
            ConfigurationKeyword.ServerHostId,
            ConfigurationKeyword.ServerHostAddress,
            ConfigurationKeyword.ServerHostType,
            ConfigurationKeyword.ServerFilePath,
            ConfigurationKeyword.ServerWebPath,
            MenuConfiguration.Identifier,
            AboutPageConfiguration.Identifier,
            HostPageConfiguration.Identifier,
            HostGraphConfiguration.Identifier,
            TrafficGraphConfiguration.Identifier,
            PeerPageConfiguration.Identifier,
            PeerGraphConfiguration.Identifier,
            PeerSummaryPageConfiguration.Identifier,
            HostGraphPageConfiguration.Identifier,
            PeerGraphPageConfiguration.Identifier,
            HostStatConfiguration.Identifier,
            HostIoStatConfiguration.Identifier,
            PeerStatConfiguration.Identifier
        };

        private readonly string filePath;
        private readonly List<HostGraphConfiguration> hostGraphs;
        private readonly int hostId;
        private readonly List<HostPageConfiguration> hostPages;
        private readonly List<PeerGraphConfiguration> peerGraphs;
        private readonly List<PeerPageConfiguration> peerPages;
        private readonly List<PeerSummaryPageConfiguration> peerSummaryPages;
        private readonly string serverName;
        private readonly ServerType serverType;
        private readonly List<TrafficGraphConfiguration> trafficGraphs;
        private readonly string webPath;
        private AboutPageConfiguration aboutPage;
        private HostGraphPageConfiguration hostGraphPage;
        private HostIoStatConfiguration hostIoStats;
        private HostStatConfiguration hostStats;
        private MenuConfiguration menu;
        private PeerGraphPageConfiguration peerGraphPage;
        private PeerStatConfiguration peerStats;

        public int HostId
        {
            get { return hostId; }
        }

        public string ServerName
        {
            get { return serverName ?? HostId.ToString(CultureInfo.InvariantCulture); }
        }

        public ServerType ServerType
        {
            get { return serverType; }
        }

        public string FilePath
        {
            get { return filePath; }
        }

        public string WebPath
        {
            get { return webPath; }
        }

        public MenuConfiguration Menu
        {
            get { return menu; }
        }

        public AboutPageConfiguration AboutPage
        {
            get { return aboutPage; }
        }

        public IEnumerable<HostPageConfiguration> HostPages
        {
            get { return hostPages; }
        }

        public IEnumerable<HostGraphConfiguration> HostGraphs
        {
            get { return hostGraphs; }
        }

        public List<TrafficGraphConfiguration> TrafficGraphs
        {
            get { return trafficGraphs; }
        }

        public IEnumerable<PeerPageConfiguration> PeerPages
        {
            get { return peerPages; }
        }

        public IEnumerable<PeerGraphConfiguration> PeerGraphs
        {
            get { return peerGraphs; }
        }

        public IEnumerable<PeerSummaryPageConfiguration> PeerSummaryPages
        {
            get { return peerSummaryPages; }
        }

        public HostGraphPageConfiguration HostGraphPage
        {
            get { return hostGraphPage; }
        }

        public PeerGraphPageConfiguration PeerGraphPage
        {
            get { return peerGraphPage; }
        }

        public HostStatConfiguration HostStats
        {
            get { return hostStats; }
        }

        public HostIoStatConfiguration HostIoStats
        {
            get { return hostIoStats; }
        }

        public PeerStatConfiguration PeerStats
        {
            get { return peerStats; }
        }

        protected override IEnumerable<Object> Items
        {
            get
            {
                var items = new List<object>
                {
                    ConfigName,
                    HostId,
                    ServerName,
                    GetServerTypeString(ServerType),
                    FilePath
                };

                if (ShowDefault || (!ShowDefault && WebPath != DefaultWebPath))
                    items.Add(WebPath);

                items.AddRange(new List<object>
                {
                    Menu,
                    AboutPage,
                    HostPages,
                    HostGraphs,
                    TrafficGraphs,
                    PeerPages,
                    PeerGraphs,
                    PeerSummaryPages,
                    HostGraphPage,
                    PeerGraphPage,
                    HostStats,
                    HostIoStats,
                    PeerStats
                });

                return items.ToArray();
            }
        }

        private string GetServerTypeString(ServerType type)
        {
            switch (type)
            {
                case ServerType.Ntpdc:
                    return ConfigurationKeyword.ServerTypeNtpdc;
                case ServerType.Ntpq:
                    return ConfigurationKeyword.ServerTypeNtpq;
                case ServerType.OpenNtpd:
                    return ConfigurationKeyword.ServerTypeOpenNtpd;
                default:
                    return ConfigurationKeyword.TypeUnknown;
            }
        }

        private static ServerType GetServerTypeString(
            Dictionary<String, String> options,
            string type,
            string configPath)
        {
            if (!options.ContainsKey(type) || options[type].Trim() == String.Empty)
            {
                ConfigError(String.Format(ConfigurationMessage.NoValueForSetting, type, configPath));
                return ServerType.Unknown;
            }

            switch (options[type].Trim().ToLower())
            {
                case ConfigurationKeyword.ServerTypeNtpdc:
                    return ServerType.Ntpdc;
                case ConfigurationKeyword.ServerTypeNtpq:
                    return ServerType.Ntpq;
                case ConfigurationKeyword.ServerTypeOpenNtpd:
                    return ServerType.OpenNtpd;
                default:
                    ConfigError(String.Format(ConfigurationMessage.InvalidValue, type, configPath));
                    return ServerType.Unknown;
            }
        }

        public override string ToString()
        {
            var list = new List<String>
            {
                ConfigurationKeyword.Server,
                ConfigurationKeyword.ServerHostId,
                ConfigurationKeyword.ServerHostAddress,
                ConfigurationKeyword.ServerHostType,
                ConfigurationKeyword.ServerFilePath
            };

            if (ShowDefault || (!ShowDefault && WebPath != DefaultWebPath))
                list.Add(ConfigurationKeyword.ServerWebPath);

            list.AddRange(new List<String>
            {
                MenuConfiguration.Identifier,
                AboutPageConfiguration.Identifier,
                HostPageConfiguration.Identifier,
                HostGraphConfiguration.Identifier,
                TrafficGraphConfiguration.Identifier,
                PeerPageConfiguration.Identifier,
                PeerGraphConfiguration.Identifier,
                PeerSummaryPageConfiguration.Identifier,
                HostGraphPageConfiguration.Identifier,
                PeerGraphPageConfiguration.Identifier,
                HostStatConfiguration.Identifier,
                HostIoStatConfiguration.Identifier,
                PeerStatConfiguration.Identifier
            });

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

        public static HostConfiguration Create(ReadingBulkConfiguration[] bulks, ConfigurationBlock configBlock)
        {
            Dictionary<String, String> options = GetOptions(Params, configBlock);

            var config = new HostConfiguration(
                GetOptionConfigName(options, Identifier, Identifier, true),
                configBlock.IndentLevel,
                GetOptionInteger(options, ConfigurationKeyword.ServerHostId, Identifier, true),
                GetOptionString(options, ConfigurationKeyword.ServerHostAddress, Identifier),
                GetServerTypeString(options, ConfigurationKeyword.ServerHostType, Identifier),
                GetOptionString(options, ConfigurationKeyword.ServerFilePath, Identifier, false, "/"),
                GetOptionString(options, ConfigurationKeyword.ServerWebPath, Identifier, false, DefaultWebPath)
                );

            ConfigurationBlock block = FindSingleBlock(HostStatConfiguration.Identifier, configBlock);
            config.hostStats = block != null ? HostStatConfiguration.Create(config, bulks, block) : null;

            block = FindSingleBlock(HostIoStatConfiguration.Identifier, configBlock);
            config.hostIoStats = block != null ? HostIoStatConfiguration.Create(config, bulks, block) : null;

            block = FindSingleBlock(PeerStatConfiguration.Identifier, configBlock);
            config.peerStats = block != null ? PeerStatConfiguration.Create(config, bulks, block) : null;

            block = FindSingleBlock(AboutPageConfiguration.Identifier, configBlock);
            config.aboutPage = block != null ? AboutPageConfiguration.Create(config, block) : null;

            ConfigurationBlock[] hostGraphBlock = FindBlocks(
                HostGraphConfiguration.Identifier, configBlock);
            foreach (ConfigurationBlock hostBlock in hostGraphBlock)
            {
                config.hostGraphs.Add(HostGraphConfiguration.Create(config, hostBlock));
            }

            ConfigurationBlock[] trafficGraphBlocks = FindBlocks(
                TrafficGraphConfiguration.Identifier, configBlock);
            foreach (ConfigurationBlock trafficBlock in trafficGraphBlocks)
            {
                config.trafficGraphs.Add(TrafficGraphConfiguration.Create(config, trafficBlock));
            }

            ConfigurationBlock[] peerGraphBlock = FindBlocks(
                PeerGraphConfiguration.Identifier, configBlock);
            foreach (ConfigurationBlock peerBlock in peerGraphBlock)
            {
                config.peerGraphs.Add(PeerGraphConfiguration.Create(config, peerBlock));
            }

            ConfigurationBlock[] peerBlocks = FindBlocks(
                PeerPageConfiguration.Identifier, configBlock);
            foreach (ConfigurationBlock peerBlock in peerBlocks)
            {
                config.peerPages.Add(PeerPageConfiguration.Create(
                    config,
                    peerBlock,
                    config.peerGraphs));
            }

            ConfigurationBlock[] peerSummaryBlocks = FindBlocks(
                PeerSummaryPageConfiguration.Identifier, configBlock);
            foreach (ConfigurationBlock peerSummaryBlock in peerSummaryBlocks)
            {
                config.peerSummaryPages.Add(PeerSummaryPageConfiguration.Create(
                    config, peerSummaryBlock,
                    config.peerPages,
                    config.peerGraphs));
            }

            var graphs = new List<GraphBaseConfiguration>();
            graphs.AddRange(config.hostGraphs);
            graphs.AddRange(config.trafficGraphs);

            ConfigurationBlock[] hostBlocks = FindBlocks(HostPageConfiguration.Identifier, configBlock);
            foreach (ConfigurationBlock hostBlock in hostBlocks)
            {
                config.hostPages.Add(HostPageConfiguration.Create(
                    config,
                    hostBlock,
                    config.peerPages,
                    graphs,
                    config.PeerSummaryPages));
            }

            block = FindSingleBlock(HostGraphPageConfiguration.Identifier, configBlock);
            config.hostGraphPage =
                block != null
                    ? HostGraphPageConfiguration.Create(
                        config,
                        block,
                        config.hostPages)
                    : null;

            block = FindSingleBlock(PeerGraphPageConfiguration.Identifier, configBlock);
            config.peerGraphPage =
                block != null
                    ? PeerGraphPageConfiguration.Create(
                        config,
                        block,
                        config.peerPages)
                    : null;

            var linkables = new List<ILinkable>();
            if (config.aboutPage != null)
                linkables.Add(config.aboutPage);

            linkables.AddRange(config.hostPages);
            linkables.AddRange(config.peerPages);
            linkables.AddRange(config.PeerSummaryPages);

            block = FindSingleBlock(MenuConfiguration.Identifier, configBlock);
            config.menu = block != null ? MenuConfiguration.Create(block, linkables) : null;

            return config;
        }
    }
}