<commit_before>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;

namespace StackExchange.Opserver.Data.Elastic
{
    public partial class ElasticCluster
    {
        private Cache<ClusterNodesInfo> _nodes;
        public Cache<ClusterNodesInfo> Nodes => 
            _nodes ?? (_nodes = GetElasticCache(async () =>
            {
                var result = (await GetAsync<ClusterNodesInfo>("_nodes").ConfigureAwait(false))?.Prep();
                if (result == null) return null;

                var stats = await GetAsync<ClusterNodesStats>("_nodes/stats?all").ConfigureAwait(false);
                if (stats == null) return result;

                foreach (var s in stats.Nodes)
                {
                    var node = result.Nodes.FirstOrDefault(n => n.GUID == s.Key);
                    if (node != null)
                    {
                        node.Stats = s.Value;
                    }
                }
                return result;
            }));

        public string Name => Nodes.Data?.Name ?? SettingsName;
        public string ShortName(NodeInfo node) => node?.Name.TrimEnd("-" + Name);

        public class ClusterNodesInfo
        {
            public List<NodeInfo> Nodes { get; private set; }

            public ClusterNodesInfo Prep()
            {
                if (RawNodes != null)
                {
                    foreach (var n in RawNodes)
                    {
                        n.Value.GUID = n.Key;
                        n.Value.ShortName = n.Value.Name.Replace("-" + Name, "");
                    }
                }
                Nodes = RawNodes?.Values.OrderBy(n => n.Name).ToList() ?? new List<NodeInfo>();
                return this;
            }
            public NodeInfo Get(string nameOrGuid)
            {
                return Nodes.FirstOrDefault(
                    n => string.Equals(n.Name, nameOrGuid, StringComparison.InvariantCultureIgnoreCase)
                         || string.Equals(n.GUID, nameOrGuid, StringComparison.InvariantCultureIgnoreCase));
            }

            [DataMember(Name = "cluster_name")]
            public string Name { get; internal set; }
            [DataMember(Name = "nodes")]
            public Dictionary<string, NodeInfo> RawNodes { get; internal set; }
        }

        public class NodeInfo : IMonitorStatus
        {
            // TODO: Implement
            public MonitorStatus MonitorStatus => MonitorStatus.Good;
            public string MonitorStatusReason => null;

            private Version _version;
            [IgnoreDataMember]
            public Version Version => _version ?? (_version = VersionString.HasValue() ? Version.Parse(VersionString) : new Version("0.0"));

            public bool IsClient => GetAttribute("client") == "true";
            public bool IsDataNode => GetAttribute("data") == "true";

            private string GetAttribute(string key)
            {
                string val;
                return Attributes != null && Attributes.TryGetValue(key, out val) ? val : null;
            }

            public string GUID { get; internal set; }
            public string ShortName { get; internal set; }
            public ClusterNodesStats.NodeStats Stats { get; internal set; }

            [DataMember(Name = "name")]
            public string Name { get; internal set; }
            //[DataMember(Name = "transport_address")]
            //public string TransportAddress { get; internal set; }
            [DataMember(Name = "host")]
            public string Hostname { get; internal set; }
            [DataMember(Name = "version")]
            public string VersionString { get; internal set; }
            //[DataMember(Name = "build")]
            //public string Build { get; internal set; }
            [DataMember(Name = "attributes")]
            public Dictionary<string, string> Attributes { get; internal set; }
            //[DataMember(Name = "http_address")]
            //public string HttpAddress { get; internal set; }
            [DataMember(Name = "settings")]
            public Dictionary<string, dynamic> Settings { get; internal set; }
            //[DataMember(Name = "os")]
            //public OSInfo OS { get; internal set; }
            //[DataMember(Name = "process")]
            //public ProcessInfo Process { get; internal set; }
            [DataMember(Name = "jvm")]
            public JVMInfo JVM { get; internal set; }
            //[DataMember(Name = "thread_pool")]
            //public Dictionary<string, ThreadPoolThreadInfo> ThreadPool { get; internal set; }
            [DataMember(Name = "network")]
            public NetworkInfo Network { get; internal set; }
            //[DataMember(Name = "transport")]
            //public TransportInfo Transport { get; internal set; }
            [DataMember(Name = "http")]
            public HTTPInfo HTTP { get; internal set; }<commit_msg>Fix for half of #221<commit_after>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;

namespace StackExchange.Opserver.Data.Elastic
{
    public partial class ElasticCluster
    {
        private Cache<ClusterNodesInfo> _nodes;
        public Cache<ClusterNodesInfo> Nodes => 
            _nodes ?? (_nodes = GetElasticCache(async () =>
            {
                var result = (await GetAsync<ClusterNodesInfo>("_nodes").ConfigureAwait(false))?.Prep();
                if (result == null) return null;

                var stats = await GetAsync<ClusterNodesStats>("_nodes/stats?all").ConfigureAwait(false);
                if (stats == null) return result;

                foreach (var s in stats.Nodes)
                {
                    var node = result.Nodes.FirstOrDefault(n => n.GUID == s.Key);
                    if (node != null)
                    {
                        node.Stats = s.Value;
                    }
                }
                return result;
            }));

        public string Name => Nodes.Data?.Name ?? SettingsName;
        public string ShortName(NodeInfo node) => node?.Name.TrimEnd("-" + Name);

        public class ClusterNodesInfo
        {
            public List<NodeInfo> Nodes { get; private set; }

            public ClusterNodesInfo Prep()
            {
                if (RawNodes != null)
                {
                    foreach (var n in RawNodes)
                    {
                        n.Value.GUID = n.Key;
                        n.Value.ShortName = n.Value.Name.Replace("-" + Name, "");
                    }
                }
                Nodes = RawNodes?.Values.OrderBy(n => n.Name).ToList() ?? new List<NodeInfo>();
                return this;
            }
            public NodeInfo Get(string nameOrGuid)
            {
                return Nodes.FirstOrDefault(
                    n => string.Equals(n.Name, nameOrGuid, StringComparison.InvariantCultureIgnoreCase)
                         || string.Equals(n.GUID, nameOrGuid, StringComparison.InvariantCultureIgnoreCase));
            }

            [DataMember(Name = "cluster_name")]
            public string Name { get; internal set; }
            [DataMember(Name = "nodes")]
            public Dictionary<string, NodeInfo> RawNodes { get; internal set; }
        }

        public class NodeInfo : IMonitorStatus
        {
            // TODO: Implement
            public MonitorStatus MonitorStatus => MonitorStatus.Good;
            public string MonitorStatusReason => null;

            private Version _version;
            [IgnoreDataMember]
            public Version Version => _version ?? (_version = VersionString.HasValue() ? Version.Parse(VersionString) : new Version("0.0"));

            public bool IsClient => GetAttribute("client") == "true";
            public bool IsDataNode => GetAttribute("data") == "true";

            private string GetAttribute(string key)
            {
                string val;
                return Attributes != null && Attributes.TryGetValue(key, out val) ? val : null;
            }

            public string GUID { get; internal set; }
            public string ShortName { get; internal set; }
            public ClusterNodesStats.NodeStats Stats { get; internal set; }

            [DataMember(Name = "name")]
            public string Name { get; internal set; }
            //[DataMember(Name = "transport_address")]
            //public string TransportAddress { get; internal set; }

            // Elasticsearch 5.0 changed this, for reasons passing understanding.
            [DataMember(Name = "hostname")]
            public string Hostname { get; internal set; }
            [DataMember(Name = "host")]
            internal string ElasticSearchJustLovesChangingPropertiesWithNoClueOfImpactOnActualApplications
            {
                set { Hostname = value; }
            }

            [DataMember(Name = "version")]
            public string VersionString { get; internal set; }
            //[DataMember(Name = "build")]
            //public string Build { get; internal set; }
            [DataMember(Name = "attributes")]
            public Dictionary<string, string> Attributes { get; internal set; }
            //[DataMember(Name = "http_address")]
            //public string HttpAddress { get; internal set; }
            [DataMember(Name = "settings")]
            public Dictionary<string, dynamic> Settings { get; internal set; }
            //[DataMember(Name = "os")]
            //public OSInfo OS { get; internal set; }
            //[DataMember(Name = "process")]
            //public ProcessInfo Process { get; internal set; }
            [DataMember(Name = "jvm")]
            public JVMInfo JVM { get; internal set; }
            //[DataMember(Name = "thread_pool")]
            //public Dictionary<string, ThreadPoolThreadInfo> ThreadPool { get; internal set; }
            [DataMember(Name = "network")]
            public NetworkInfo Network { get; internal set; }
            //[DataMember(Name = "transport")]
            //public TransportInfo Transport { get; internal set; }
            [DataMember(Name = "http")]
            public HTTPInfo HTTP { get; internal set; }
{'fraction_non_alphanumeric': 0.10563792430745221, 'fraction_numerical': 0.0008778774873195475, 'mean_word_length': 1.719628647214854, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 47, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1556285', 'n_tokens_mistral': 2528, 'n_tokens_neox': 2444, 'n_words': 666}