using System.Globalization;
using System.Text;

namespace Unity.VisualScripting.Dependencies.NCalc
{
    public class SerializationVisitor : LogicalExpressionVisitor
    {
        public SerializationVisitor()
        {
            Result = new StringBuilder();
            _numberFormatInfo = new NumberFormatInfo { NumberDecimalSeparator = "." };
        }

        private readonly NumberFormatInfo _numberFormatInfo;

        public StringBuilder Result { get; protected set; }

        public override void Visit(TernaryExpression ternary)
        {
            EncapsulateNoValue(ternary.LeftExpression);

            Result.Append("? ");

            EncapsulateNoValue(ternary.MiddleExpression);

            Result.Append(": ");

            EncapsulateNoValue(ternary.RightExpression);
        }

        public override void Visit(BinaryExpression binary)
        {
            EncapsulateNoValue(binary.LeftExpression);

            switch (binary.Type)
            {
                case BinaryExpressionType.And:
                    Result.Append("and ");
                    break;

                case BinaryExpressionType.Or:
                    Result.Append("or ");
                    break;

                case BinaryExpressionType.Div:
                    Result.Append("/ ");
                    break;

                case BinaryExpressionType.Equal:
                    Result.Append("= ");
                    break;

                case BinaryExpressionType.Greater:
                    Result.Append("> ");
                    break;

                case BinaryExpressionType.GreaterOrEqual:
                    Result.Append(">= ");
                    break;

                case BinaryExpressionType.Lesser:
                    Result.Append("< ");
                    break;

                case BinaryExpressionType.LesserOrEqual:
                    Result.Append("<= ");
                    break;

                case BinaryExpressionType.Minus:
                    Result.Append("- ");
                    break;

                case BinaryExpressionType.Modulo:
                    Result.Append("% ");
                    break;

                case BinaryExpressionType.NotEqual:
                    Result.Append("!= ");
                    break;

                case BinaryExpressionType.Plus:
                    Result.Append("+ ");
                    break;

                case BinaryExpressionType.Times:
                    Result.Append("* ");
                    break;

                case BinaryExpressionType.BitwiseAnd:
                    Result.Append("& ");
                    break;

                case BinaryExpressionType.BitwiseOr:
                    Result.Append("| ");
                    break;

                case BinaryExpressionType.BitwiseXOr:
                    Result.Append("~ ");
                    break;

                case BinaryExpressionType.LeftShift:
                    Result.Append("<< ");
                    break;

                case BinaryExpressionType.RightShift:
                    Result.Append(">> ");
                    break;
            }

            EncapsulateNoValue(binary.RightExpression);
        }

        public override void Visit(UnaryExpression unary)
        {
            switch (unary.Type)
            {
                case UnaryExpressionType.Not:
                    Result.Append("!");
                    break;

                case UnaryExpressionType.Negate:
                    Result.Append("-");
                    break;

                case UnaryExpressionType.BitwiseNot:
                    Result.Append("~");
                    break;
            }

            EncapsulateNoValue(unary.Expression);
        }

        public override void Visit(ValueExpression value)
        {
            switch (value.Type)
            {
                case ValueType.Boolean:
                    Result.Append(value.Value).Append(" ");
                    break;

                case ValueType.DateTime:
                    Result.Append("#").Append(value.Value).Append("#").Append(" ");
                    break;

                case ValueType.Float:
                    Result.Append(decimal.Parse(value.Value.ToString()).ToString(_numberFormatInfo)).Append(" ");
                    break;

                case ValueType.Integer:
                    Result.Append(value.Value).Append(" ");
                    break;

                case ValueType.String:
                    Result.Append("'").Append(value.Value).Append("'").Append(" ");
                    break;
            }
        }

        public override void Visit(FunctionExpression function)
        {
            Result.Append(function.Identifier.Name);

            Result.Append("(");

            for (var i = 0; i < function.Expressions.Length; i++)
            {
                function.Expressions[i].Accept(this);

                if (i < function.Expressions.Length - 1)
                {
                    Result.Remove(Result.Length - 1, 1);
                    Result.Append(", ");
                }
            }

            // Trim spaces before adding a closing parenthesis
            while (Result[Result.Length - 1] == ' ')
            {
                Result.Remove(Result.Length - 1, 1);
            }

            Result.Append(") ");
        }

        public override void Visit(IdentifierExpression identifier)
        {
            Result.Append("[").Append(identifier.Name).Append("] ");
        }

        protected void EncapsulateNoValue(LogicalExpression expression)
        {
            if (expression is ValueExpression)
            {
                expression.Accept(this);
            }
            else
            {
                Result.Append("(");
                expression.Accept(this);

                // Trim spaces before adding a closing parenthesis
                while (Result[Result.Length - 1] == ' ')
                {
                    Result.Remove(Result.Length - 1, 1);
                }

                Result.Append(") ");
            }
        }
    }
}