//
// ServerConfiguration.cs
//
// Author:
//       Carsten Sonne Larsen <cs@innolan.dk>
//
// Copyright (c) 2013 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;
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;

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

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

        public const string Identifier = "Server";
        private static readonly String[] Params = new[] {
            Identifier,
            "HostID",
            "HostAddress",
            "HostType",
            "ConfigFile",
            "FilePath",
            "WebPath",
            "Menu",
            "AboutPage",
            "HostPage",
            "HostGraph",
            "TrafficGraph",
            "PeerPages",
            "PeerGraphs",
            "PeerSummeryPage",
            "HostGraphPage",
            "PeerGraphPage",
            "HostStats",
            "HostIOStats",
            "PeerStats"
        };
        private readonly string configFile;
        private readonly string filePath;
        private readonly List<HostGraphConfiguration> hostGraphs;
        private readonly List<TrafficGraphConfiguration> trafficGraphs;
        private readonly int hostId;
        private readonly List<HostPageConfiguration> hostPages;
        private readonly List<PeerGraphConfiguration> peerGraphs;
        private readonly List<PeerPageConfiguration> peerPages;
        private readonly List<PeerSummeryPageConfiguration> peerSummeryPages;
        private readonly string serverName;
        private readonly ServerType serverType;
        private readonly string webPath;
        private AboutPageConfiguration aboutPage;
        private HostGraphPageConfiguration hostGraphPage;
        private HostStatConfiguration hostStats;
        private HostIOStatConfiguration hostIoStats;
        private MenuConfiguration menu;
        private PeerGraphPageConfiguration peerGraphPage;
        private PeerStatConfiguration peerStats;

        public int HostId {
            get { return hostId; }
        }

        public string ServerName {
            get { return serverName != null ? serverName : HostId.ToString(); }
        }

        public ServerType ServerType {
            get { return serverType; }
        }

        public string ConfigFile {
            get { return configFile; }
        }

        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<PeerSummeryPageConfiguration> PeerSummeryPages {
            get { return peerSummeryPages; }
        }

        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 {
                return new object[] {
                    ConfigName,
                    hostId,
                    serverName,
                    GetServerTypeString(serverType),
                    configFile,
                    filePath,
                    webPath,
                    menu,
                    aboutPage,
                    hostPages,
                    hostGraphs,
                    trafficGraphs,
                    peerPages,
                    peerGraphs,
                    peerSummeryPages,
                    hostGraphPage,
                    peerGraphPage,
                    hostStats,
                    hostIoStats,
                    peerStats
                };
            }
        }

        private string GetServerTypeString(ServerType type)
        {
            switch (type) {
            case ServerType.Ntpdc:
                return "ntpdc";
            case ServerType.Ntpq:
                return "ntpq";
            default:
                return "unknown";
            }
        }

        private static ServerType GetServerTypeString(
            Dictionary<String, String> options,
            string type,
            string configPath)
        {
            if (!options.ContainsKey (type) || options [type].Trim () == String.Empty) {
                ConfigError (String.Format (
                    "No value for setting {0} in section {1} was specified.",
                    type, configPath));
                return ServerType.Unknown;
            }

            switch (options [type].Trim ()) {
            case "ntpdc":
                return ServerType.Ntpdc;
            case "ntpq":
                return ServerType.Ntpq;
            default:
                ConfigError (String.Format (
                    "Invalid value for setting {0} in section {1}. Specify either ntpq or ntpdc.",
                    type, configPath));
                return ServerType.Unknown;
            }
        }

        public override string ToString ()
        {
            return ConfigText (Params, Items);
        }

        public static ServerConfiguration Create (ReadingBulkConfiguration bulk, ConfigurationBlock configBlock)
        {
            Dictionary<String, String> options = GetOptions (Params, configBlock);

            ServerConfiguration config = new ServerConfiguration (
                                             GetOptionConfigName (options, Params [0], Params [0], true),
                                             configBlock.IndentLevel,
                                             GetOptionInteger (options, Params [1], Params [0], true),
                                             GetOptionString (options, Params [2], Params [0], true),
                                             GetServerTypeString (options, Params [3], Params [0]),
                                             GetOptionString (options, Params [4], Params [0], false),
                                             GetOptionString (options, Params [5], Params [0], false),
                                             GetOptionString (options, Params [6], Params [0], false)
                                         );

            // Set sub configuration blocks
            ConfigurationBlock block;

            block = FindSingleBlock (Params [17], configBlock);
            config.hostStats = block != null ? HostStatConfiguration.Create (config, bulk, block) : null;

            block = FindSingleBlock (Params [18], configBlock);
            config.hostIoStats = block != null ? HostIOStatConfiguration.Create (config, bulk, block) : null;

            block = FindSingleBlock (Params [19], configBlock);
            config.peerStats = block != null ? PeerStatConfiguration.Create (config, bulk, block) : null;

            block = FindSingleBlock (Params [8], configBlock);
            config.aboutPage = block != null ? AboutPageConfiguration.Create (config, block) : null;

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

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

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

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

            ConfigurationBlock[] peerSummeryBlocks = FindBlocks (Params [14], configBlock);
            foreach (ConfigurationBlock peerSummeryBlock in peerSummeryBlocks) {
                config.peerSummeryPages.Add (PeerSummeryPageConfiguration.Create (
                    config, peerSummeryBlock,
                    config.peerPages,
                    config.peerGraphs));
            }

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

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

            block = FindSingleBlock (Params [15], configBlock);
            config.hostGraphPage =
                block != null ? HostGraphPageConfiguration.Create (
                config,
                block,
                config.hostPages) : null;

            block = FindSingleBlock (Params [16], configBlock);
            config.peerGraphPage =
                block != null ? PeerGraphPageConfiguration.Create (
                config,
                block,
                config.peerPages) : null;

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

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

            block = FindSingleBlock (Params [7], configBlock);
            config.menu = block != null ? MenuConfiguration.Create (block, linkables) : null;

            return config;
        }
    }
}