/**
 * @file evaluate.cpp
 */

/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 1992-2017 Jean-Pierre Charras <jp.charras at wanadoo.fr>
 * Copyright (C) 1992-2017 KiCad Developers, see change_log.txt for contributors.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you may find one here:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * or you may search the http://www.gnu.org website for the version 2 license,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

/* How to evaluate an arithmetic expression like those used in Aperture Macro Definition in Gerber?
 *
 *  See http://stackoverflow.com/questions/28256/equation-expression-parser-with-precedence
 *
 *  The shunting yard algorithm is the right tool for this.
 *  Wikipedia is really confusing about this, but basically the algorithm works like this:
 *
 *  Say, you want to evaluate 1 + 2 * 3 + 4. Intuitively, you "know" you have to do the 2 * 3 first,
 *  but how do you get this result?
 *  The key is to realize that when you're scanning the string from left to right, you will evaluate
 *  an operator when the operator that follows it has a lower (or equal to) precedence.
 *
 *  In the context of the example, here's what you want to do:
 *
 *   Look at: 1 + 2, don't do anything.
 *   Now look at 1 + 2 * 3, still don't do anything.
 *   Now look at 1 + 2 * 3 + 4, now you know that 2 * 3 has to to be evaluated because
 *  the next operator has lower precedence.
 *
 *  How do you implement this?
 *
 *  You want to have two stacks, one for numbers, and another for operators.
 *  You push numbers onto the stack all the time.
 *  You compare each new operator with the one at the top of the stack,
 *  if the one on top of the stack has higher priority, you pop it off the operator stack,
 *  pop the operands off the number stack, apply the operator and push the result onto the number stack.
 *
 *  Now you repeat the comparison with the top of stack operator.
 *
 *  Coming back to the example, it works like this:
 *
 *  N = [ ] Ops = [ ]
 *
 *   Read 1. N = [1], Ops = [ ]
 *   Read +. N = [1], Ops = [+]
 *   Read 2. N = [1 2], Ops = [+]
 *   Read *. N = [1 2], Ops = [+ *]
 *   Read 3. N = [1 2 3], Ops = [+ *]
 *   Read +. N = [1 2 3], Ops = [+ *]
 *      Pop 3, 2 and execute 2*3, and push result onto N. N = [1 6], Ops = [+]
 *      is left associative, so you want to pop 1, 6 off as well and execute the +. N = [7], Ops = [].
 *       Finally push the [+] onto the operator stack. N = [7], Ops = [+].
 *   Read 4. N = [7 4]. Ops = [+].
 *
 *  You're run out off input, so you want to empty the stacks now.
 *  Upon which you will get the result 11.
 */

#include <am_param.h>

/**
 * Evaluate an basic arithmetic expression (infix notation) with precedence
 * The expression is a sequence of numbers (double) and arith operators:
 * operators are + - x / ( and )
 * the expression is stored in a std::vector
 * each item is a AM_PARAM_EVAL (each item is an operator or a double)
 * @param aExp = the arithmetic expression to evaluate
 * @return the value
 */

 /*
 The instructions ( subset of parm_item_type)
----------------
NOP  : The no operation. the AM_PARAM_EVAL item stores a value.
ADD
SUB
MUL
DIV
OPEN_PAR  : Opening parenthesis: modify the precedence of operators inside ( and )
CLOSE_PAR : Closing parenthesis: modify the precedence of operators by closing the local block.
POPVALUE : used to initialize a sequence
*/

double Evaluate( AM_PARAM_EVAL_STACK& aExp )
{
    class OP_CODE   // A small class to store a operator and its priority
    {
    public:
        parm_item_type m_Optype;
        int m_Priority;

        OP_CODE( AM_PARAM_EVAL& aAmPrmEval )
            : m_Optype( aAmPrmEval.GetOperator() ),
              m_Priority( aAmPrmEval.GetPriority() )
        {}

        OP_CODE( parm_item_type aOptype )
            : m_Optype( aOptype ), m_Priority( 0 )
        {}
    };

    double result = 0.0;

    std::vector<double> values;     // the current list of values
    std::vector<OP_CODE> optype;    // the list of arith operators

    double curr_value = 0.0;
    int extra_priority = 0;

    for( unsigned ii = 0; ii < aExp.size(); ii++ )
    {
        AM_PARAM_EVAL& prm = aExp[ii];

        if( prm.IsOperator() )
        {
            if( prm.GetOperator() == OPEN_PAR )
            {
                extra_priority += AM_PARAM_EVAL::GetPriority( OPEN_PAR );
            }
            else if( prm.GetOperator() == CLOSE_PAR )
            {
                extra_priority -= AM_PARAM_EVAL::GetPriority( CLOSE_PAR );
            }
            else
            {
                optype.emplace_back( prm );
                optype.back().m_Priority += extra_priority;
            }
        }
        else    // we have a value:
        {
            values.push_back( prm.GetValue() );

            if( optype.size() < 2 )
                continue;

            OP_CODE& previous_optype = optype[optype.size() - 2];

            if( optype.back().m_Priority > previous_optype.m_Priority )
            {
                double op1 = 0.0;

                double op2 = values.back();
                values.pop_back();

                if( values.size() )
                {
                    op1 = values.back();
                    values.pop_back();
                }

                switch( optype.back().m_Optype )
                {
                    case ADD:
                        values.push_back( op1+op2 );
                        break;

                    case SUB:
                        values.push_back( op1-op2 );
                        break;

                    case MUL:
                        values.push_back( op1*op2 );
                        break;

                    case DIV:
                        values.push_back( op1/op2 );
                        break;

                    default:
                        break;
                }

                optype.pop_back();
            }
        }
    }

    // Now all operators have the same priority, or those having the higher priority
    // are before others, calculate the final result by combining initial values and/or
    // replaced values.
    if( values.size() > optype.size() )
        // If there are n values, the number of operator is n-1 or n if the first
        // item of the expression to evaluate is + or - (like -$1/2)
        // If the number of operator is n-1 the first value is just copied to result
        optype.insert( optype.begin(), OP_CODE( POPVALUE ) );

    wxASSERT( values.size() == optype.size() );

    for( unsigned idx = 0; idx < values.size(); idx++ )
    {
        curr_value = values[idx];

        switch( optype[idx].m_Optype )
        {
            case POPVALUE:
                result = curr_value;
                break;

            case ADD:
                result += curr_value;
                break;

            case SUB:
                result -= curr_value;
                break;

            case MUL:
                result *= curr_value;
                break;

            case DIV:
                result /= curr_value;
                break;

            default:
                break;
        }
    }

    return result;
}