/// <summary>
        /// This is the A* function. This will be called multiple times until
        /// the path is calculated. All the states values are storred in Ticket.
        /// To calcualte the path this function must be called multiple times until
        /// will return true!
        /// </summary>
        /// <param name="ticket">the request</param>
        /// <param name="token">the cancelation token</param>
        /// <returns>true if the processing finished.</returns>
        private bool ProcessTicket(Ticket ticket, CancellationToken token)
        {
            /// Check the cancelation token.
            if (token.IsCancellationRequested)
            {
                /// In case the Cancel was requested from external,
                /// the function will return.
                ticket.State = Ticket.STATE.CANCELLED;
                token.ThrowIfCancellationRequested();
            }

            /// Switch the state to Processing.
            ticket.State = Ticket.STATE.PROCESSING;

            ticket.Steps++;

            if (NavMesh.GetNodeType(ticket.GoalIndex) == NodeType.INVALID)
            {
                ticket.State = Ticket.STATE.INVALID_GOAL;
                return(true);
            }

            /// Chekc if the StartIndex is the same with GoalIndex
            if (ticket.StartIndex == ticket.GoalIndex)
            {
                ticket.Path.Add(ticket.StartIndex);

                /// Search is stopped because the goal si the same with start node
                ticket.State = Ticket.STATE.COMPLETED;
                return(true);
            }


            /// This is the first step -> closed list is empty.
            /// Add the start node to the closed list.
            if (ticket.ClosedList.Count == 0)
            {
                Node startNode = new Node(ticket.StartIndex);

                /// calculate the distance to target
                startNode.DistanceToTarget = NavMesh.ComputeDistanceToGoal(ticket.GoalIndex, ticket.StartIndex);

                /// Calculate the "F" value
                startNode.F = startNode.DistanceToTarget;

                /// Add the start node to the open list
                ticket.OpenList[ticket.StartIndex] = startNode;

                /// Set the current node to be the start node
                ticket.CurrentNode = startNode;
            }

            //Console.WriteLine(this.GetType().FullName + ".ProcessTicket ticket.CurrentNode.Index=" + ticket.CurrentNode.Index);

            /// If the start node does not have valid neighbors, try to GetNeighbors and add them to the open list
            if (ticket.CurrentNode.Neighbors.Count == 0)
            {
                ticket.CurrentNode.Neighbors = NavMesh.GetNeighbors(ticket.CurrentNode.Index);                // ticket->m_current->GetNeighbors();
            }

            foreach (ulong neighborIndex in ticket.CurrentNode.Neighbors)
            {
                /// Check if the goal is one of the neighbors
                if (ticket.GoalIndex == neighborIndex)
                {
                    ticket.Path.Add(ticket.GoalIndex);

                    Node node = ticket.CurrentNode;
                    do
                    {
                        ticket.Path.Add(node.Index);
                        //std::cout << "result " << node << " " << node->m_index << " " << node->m_f << std::endl;
                        node = node.Parent;
                    }while (node != null);

                    ticket.State = Ticket.STATE.COMPLETED;
                    return(true);
                }

                bool addedAlready = false;
                Node neighborNode = null;


                /// check if is in open list
                if (ticket.OpenList.ContainsKey(neighborIndex))
                {
                    addedAlready = true;
                    neighborNode = ticket.OpenList[neighborIndex];
                }
                else if (ticket.ClosedList.ContainsKey(neighborIndex))
                {
                    addedAlready = true;
                    neighborNode = ticket.ClosedList[neighborIndex];
                }

                if (!addedAlready)
                {
                    neighborNode        = new Node(neighborIndex);
                    neighborNode.Parent = ticket.CurrentNode;
                }



                /// calculate the distance to target
                neighborNode.DistanceToTarget = NavMesh.ComputeDistanceToGoal(ticket.GoalIndex, neighborIndex);

                /// calculate the cost to travel from startIndex node to the neighbor note
                neighborNode.Cost = NavMesh.ComputeCostToNeighbor(neighborIndex, ticket.CurrentNode.Index);

                /// Calculate the "F" value
                double _f = neighborNode.DistanceToTarget + neighborNode.Cost;

                if (!addedAlready)
                {
                    neighborNode.F = _f;

                    /// Add the neighbor node to the open list
                    ticket.OpenList[neighborIndex] = neighborNode;
                }
                else if (neighborNode.F > _f)
                {
                    neighborNode.F = _f;
                }
            }


            /// Chekc if there are some nodes in Open list
            if (ticket.OpenList.Count == 0)
            {
                Node node = ticket.CurrentNode;
                do
                {
                    ticket.Path.Add(node.Index);

                    node = node.Parent;
                }while (node != null);

                /// If no nodes -> search is stopped due to no path to goal.
                ticket.State = Ticket.STATE.STOPPED;
                return(true);
            }



            /// Get the object with the minimal "F"
            double f = Double.MaxValue;

            foreach (ulong index in ticket.OpenList.Keys)
            {
                if (ticket.OpenList[index].F < f)
                {
                    f = ticket.OpenList[index].F;
                    ticket.CurrentNode = ticket.OpenList[index];
                }
            }


            ticket.OpenList.Remove(ticket.CurrentNode.Index);

            ticket.ClosedList[ticket.CurrentNode.Index] = ticket.CurrentNode;

            return(false);
        }