﻿// Copyright 2014-2015 Aaron Viviano
// MIT License

using System.Collections.Generic;

namespace Solar.Trees
{
    /// <summary>
    /// A tree where every branch represents a single character in a lexeme.
    /// Each node is its own self contained tree.
    /// </summary>
    public class CharacterTree
    {
        /// <summary>
        /// The character represented by this branch.
        /// </summary>
        char Character { get; set; }

        /// <summary>
        /// The branches owned by this tree.
        /// </summary>
        Dictionary<char, CharacterTree> Branches;

        /// <summary>
        /// Default constructor.
        /// </summary>
        public CharacterTree()
        {
            Branches = new Dictionary<char, CharacterTree>();
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="nodeCharacter">The character represented by this branch.</param>
        public CharacterTree(char nodeCharacter)
        {
            Character = nodeCharacter;
            Branches = new Dictionary<char, CharacterTree>();
        }

        /// <summary>
        /// Adds a new branch to the tree via a char.
        /// </summary>
        /// <param name="character">The character for the new branch.</param>
        /// <returns>A reference to the new branch.</returns>
        public CharacterTree AddBranch(char character)
        {
            CharacterTree branch = new CharacterTree(character);
            Branches.Add(character, branch);
            return branch;
        }

        /// <summary>
        /// Adds an existing branch to the tree. Note that infinite loops
        /// are not prevented at this time.
        /// </summary>
        /// <param name="CharacterTree">The new branch to add.</param>
        /// <returns>A reference to the added branch.</returns>
        public CharacterTree AddBranch(CharacterTree branch)
        {
            Branches.Add(branch.Character, branch);
            return branch;
        }

        /// <summary>
        /// Add an entire string to a tree. This method will check for an existing
        /// branch for creating a new branch to a tree.
        /// </summary>
        /// <param name="addition">The string to add to the tree.</param>
        /// <param name="index">The current character index.</param>
        public void AddString(string addition, int index = 0)
        {
            if (index < addition.Length)
            {
                if (Branches.ContainsKey(addition[index]))
                {
                    Branches[addition[index]].AddString(addition, ++index);
                }
                else
                {
                    CharacterTree branch = AddBranch(addition[index]);
                    branch.AddString(addition, ++index);
                }
            }
        }

        public void AddCaseInsensitive(string addition, int index = 0)
        {
            if (index < addition.Length)
            {
                char currentCharacter = addition[index];
                if (Branches.ContainsKey(currentCharacter))
                {
                    Branches[currentCharacter].AddCaseInsensitive(addition, index + 1);
                }
                else
                {
                    CharacterTree branch = AddBranch(addition[index]);
                    branch.AddCaseInsensitive(addition, index + 1);
                }
                // Check if the character is a letter.
                if (char.IsLetter(currentCharacter))
                {
                    if (char.IsLower(currentCharacter))
                    {
                        currentCharacter = char.ToUpperInvariant(currentCharacter);
                    }
                    else
                    {
                        currentCharacter = char.ToLowerInvariant(currentCharacter);
                    }
                    if (Branches.ContainsKey(currentCharacter))
                    {
                        Branches[currentCharacter].AddCaseInsensitive(addition, index + 1);
                    }
                    else
                    {
                        CharacterTree branch = AddBranch(currentCharacter);
                        branch.AddCaseInsensitive(addition, index + 1);
                    }
                }
            }
        }

        /// <summary>
        /// Checks to see if a string already exists in the character tree.
        /// </summary>
        /// <param name="query">The string to find.</param>
        /// <param name="index">The current character index.</param>
        /// <returns>True if the string exists in the tree.</returns>
        public bool FindString(string query, int index = 0)
        {
            bool StringFound = false;
            if (index < query.Length)
            {
                if (Branches.ContainsKey(query[index]))
                {
                    StringFound = Branches[query[index]].FindString(query, ++index);
                }
            }
            else
            {
                StringFound = true;
            }
            return StringFound;
        }

        /// <summary>
        /// Returns a CharacterTree branch that matches the character. 
        /// </summary>
        /// <param name="character">The character to check for.</param>
        /// <returns>Null if no character is found.</returns>
        public CharacterTree FindCharacter(char character)
        {
            if(Branches.ContainsKey(character))
            {
                return Branches[character];
            }
            return null;
        }
    }
}
