using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GenericLinkedListCollection
{
    class Program
    {
        static void Main(string[] args)
        {
            #region First Example
            //// Create the link list. 
            //string[] words = { "the", "fox", "jumped", "over", "the", "dog" };
            //LinkedList<string> sentence = new LinkedList<string>(words);
            //Display(sentence, "The linked list values:");
            //Console.WriteLine("sentence.Contains(\"jumped\") = {0}",
            //    sentence.Contains("jumped"));

            //// Add the word 'today' to the beginning of the linked list.
            //sentence.AddFirst("today");
            //Display(sentence, "Test 1: Add 'today' to beginning of the list:");

            //// Move the first node to be the last node.
            //LinkedListNode<string> mark1 = sentence.First;
            //sentence.RemoveFirst();
            //sentence.AddLast(mark1);
            //Display(sentence, "Test 2: Move first node to be last node:");

            //// Change the last node be 'yesterday'.
            //sentence.RemoveLast();
            //sentence.AddLast("yesterday");
            //Display(sentence, "Test 3: Change the last node to 'yesterday':");

            //// Move the last node to be the first node.
            //mark1 = sentence.Last;
            //sentence.RemoveLast();
            //sentence.AddFirst(mark1);
            //Display(sentence, "Test 4: Move last node to be first node:");


            //// Indicate, by using parentheisis, the last occurence of 'the'.
            //sentence.RemoveFirst();
            //LinkedListNode<string> current = sentence.FindLast("the");
            //IndicateNode(current, "Test 5: Indicate last occurence of 'the':");
            
            //// Add 'lazy' and 'old' after 'the' (the LinkedListNode named current).
            //sentence.AddAfter(current, "old");
            //sentence.AddAfter(current, "lazy");
            //IndicateNode(current, "Test 6: Add 'lazy' and 'old' after 'the':");
            
            //// Indicate 'fox' node.
            //current = sentence.Find("fox");
            //IndicateNode(current, "Test 7: Indicate the 'fox' node:");

            //// Add 'quick' and 'brown' before 'fox':
            //sentence.AddBefore(current, "quick");
            //sentence.AddBefore(current, "brown");
            //IndicateNode(current, "Test 8: Add 'quick' and 'brown' before 'fox':");

            //// Keep a reference to the current node, 'fox', 
            //// and to the previous node in the list. Indicate the 'dog' node.
            //mark1 = current;
            //LinkedListNode<string> mark2 = current.Previous;
            //current = sentence.Find("dog");
            //IndicateNode(current, "Test 9: Indicate the 'dog' node:");

            //// The AddBefore method throws an InvalidOperationException 
            //// if you try to add a node that already belongs to a list.
            //Console.WriteLine("Test 10: Throw exception by adding node (fox) already in the list:");
            //try
            //{
            //    sentence.AddBefore(current, mark1);
            //}
            //catch (InvalidOperationException ex)
            //{
            //    Console.WriteLine("Exception message: {0}", ex.Message);
            //}
            //Console.WriteLine();

            //// Remove the node referred to by mark1, and then add it 
            //// before the node referred to by current. 
            //// Indicate the node referred to by current.
            //sentence.Remove(mark1);
            //sentence.AddBefore(current, mark1);
            //IndicateNode(current, "Test 11: Move a referenced node (fox) before the current node (dog):");

            //// Remove the node referred to by current.
            //sentence.Remove(current);
            //IndicateNode(current, "Test 12: Remove current node (dog) and attempt to indicate it:");

            //// Add the node after the node referred to by mark2.
            //sentence.AddAfter(mark2, current);
            //IndicateNode(current, "Test 13: Add node removed in test 11 after a referenced node (brown):");

            //// The Remove method finds and removes the 
            //// first node that that has the specified value.
            //sentence.Remove("old");
            //Display(sentence, "Test 14: Remove node that has the value 'old':");

            //// When the linked list is cast to ICollection(Of String), 
            //// the Add method adds a node to the end of the list.
            //sentence.RemoveLast();
            //ICollection<string> icoll = sentence;
            //icoll.Add("rhinoceros");
            //Display(sentence, "Test 15: Remove last node, cast to ICollection, and add 'rhinoceros':");

            //Console.WriteLine("Test 16: Copy the list to an array:");
            //// Create an array with the same number of 
            //// elements as the inked list. 
            //string[] sArray = new string[sentence.Count];
            //sentence.CopyTo(sArray, 0);

            //foreach (string s in sArray)
            //{
            //    Console.WriteLine(s);
            //}

            //// Release all the nodes.
            //sentence.Clear();

            //Console.WriteLine();
            //Console.WriteLine("Test 17: Clear linked list. Contains 'jumped' = {0}",
            //    sentence.Contains("jumped"));

            //Console.ReadLine(); 
            #endregion

            #region Properties
            LinkedListNode<String> lln = new LinkedListNode<String>("orange");
            Console.WriteLine("After creating the node ....");
            DisplayProperties(lln);
            
            // Create a new LinkedList.
            LinkedList<String> ll = new LinkedList<String>();

            // Add the "orange" node and display its properties.
            ll.AddLast(lln);
            Console.WriteLine("After adding the node to the empty LinkedList ....");
            DisplayProperties(lln);
           
            // Add nodes before and after the "orange" node and display the "orange" node's properties.
            ll.AddFirst("red");
            ll.AddLast("yellow");
            Console.WriteLine("After adding red and yellow ....");
            DisplayProperties(lln); 
            #endregion
        }

        private static void Display(LinkedList<string> words, string test)
        {
            Console.WriteLine(test);
            foreach (string word in words)
            {
                Console.Write(word + " ");
            }
            Console.WriteLine();
            Console.WriteLine();
        }

        private static void IndicateNode(LinkedListNode<string> node, string test)
        {
            Console.WriteLine(test);
            if (node.List == null)
            {
                Console.WriteLine("Node '{0}' is not in the list.\n",
                    node.Value);
                return;
            }

            StringBuilder result = new StringBuilder("(" + node.Value + ")");
            LinkedListNode<string> nodeP = node.Previous;

            while (nodeP != null)
            {
                result.Insert(0, nodeP.Value + " ");
                nodeP = nodeP.Previous;
            }

            node = node.Next;
            while (node != null)
            {
                result.Append(" " + node.Value);
                node = node.Next;
            }

            Console.WriteLine(result);
            Console.WriteLine();
        }

        public static void DisplayProperties(LinkedListNode<String> lln)
        {
            if (lln.List == null)
                Console.WriteLine("   Node is not linked.");
            else
                Console.WriteLine("   Node belongs to a linked list with {0} elements.", lln.List.Count);

            if (lln.Previous == null)
                Console.WriteLine("   Previous node is null.");
            else
                Console.WriteLine("   Value of previous node: {0}", lln.Previous.Value);

            Console.WriteLine("   Value of current node:  {0}", lln.Value);

            if (lln.Next == null)
                Console.WriteLine("   Next node is null.");
            else
                Console.WriteLine("   Value of next node:     {0}", lln.Next.Value);

            Console.WriteLine();
            /*

                This code produces the following output.

                After creating the node ....
                   Node is not linked.
                   Previous node is null.
                   Value of current node:  orange
                   Next node is null.

                After adding the node to the empty LinkedList ....
                   Node belongs to a linked list with 1 elements.
                   Previous node is null.
                   Value of current node:  orange
                   Next node is null.

                After adding red and yellow ....
                   Node belongs to a linked list with 3 elements.
                   Value of previous node: red
                   Value of current node:  orange
                   Value of next node:     yellow

                */
        }
    }
}
