/* Copyright 2015-present MongoDB Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* 
*/

using System;
using System.Linq.Expressions;
using MongoDB.Driver.Linq.Expressions;

namespace MongoDB.Driver.Linq
{
    internal class FieldExpressionFlattener : ExtensionExpressionVisitor
    {
        public static Expression FlattenFields(Expression node)
        {
            var visitor = new FieldExpressionFlattener();
            return visitor.Visit(node);
        }

        public FieldExpressionFlattener()
        {
        }

        protected internal override Expression VisitArrayIndex(ArrayIndexExpression node)
        {
            var field = Visit(node.Array) as IFieldExpression;
            if (field != null)
            {
                var constantIndex = node.Index as ConstantExpression;
                if (constantIndex == null)
                {
                    throw new NotSupportedException($"Only a constant index is supported in the expression {node}.");
                }

                var index = constantIndex.Value.ToString();
                if (index == "-1")
                {
                    // We've treated -1 as meaning $ operator. We can't break this now,
                    // so, specifically when we are flattening fields names, this is 
                    // how we'll continue to treat -1.
                    index = "$";
                }

                return new FieldExpression(
                    field.AppendFieldName(index),
                    node.Serializer);
            }

            return node;
        }

        protected internal override Expression VisitDocumentWrappedField(FieldAsDocumentExpression node)
        {
            var field = Visit(node.Document) as IFieldExpression;
            if (field != null)
            {
                return new FieldExpression(
                    node.PrependFieldName(field.FieldName),
                    node.Serializer);
            }

            return node;
        }

        protected internal override Expression VisitField(FieldExpression node)
        {
            var document = Visit(node.Document) as IFieldExpression;
            if (document != null)
            {
                return new FieldExpression(
                    node.PrependFieldName(document.FieldName),
                    node.Serializer,
                    node.Original);
            }

            return node;
        }
    }
}
