// ------------------------------------------------------------
//  Copyright (C) 2002-2003 Michael Zeilfelder
//  e-mail: info@michaelzeilfelder.de
//
//  This program is published under the terms of the GNU
//  General Public License version 2 as published by the Free
//  Software Foundation.
// ------------------------------------------------------------
#include "LispFuncs.h"
#include "Lisp.h"


// ------------------------------------------------------------
LispNode* LispAdd( LispInterpreter &lisp_, LispNode *node_)
{
    double result = 0.;
    LispNode* param = NULL;

    while ( node_ != NULL )
    {
        param = lisp_.Car(node_);
        if ( !param || param->m_type != NT_NUMBER )
        {
            cerr << "error: + expects a number" << endl;
            return NULL;
        }
        result += ((LispNumber*)param)->m_val;
        node_ = lisp_.Cdr(node_);
    }

    return new LispNumber(result);
}

// ------------------------------------------------------------
LispNode* LispSub( LispInterpreter &lisp_, LispNode *node_)
{
    double result = 0.;

    if ( node_ )
    {
        LispNode* param = lisp_.Car(node_);

        if ( !param || param->m_type != NT_NUMBER )
        {
            cerr << "error: - expects a number" << endl;
            return NULL;
        }
        result = ((LispNumber*)param)->m_val;

        node_ = lisp_.Cdr(node_);
        if ( !lisp_.Car(node_) )
        {
            result = -result;
        }
        else
        {
            while ( node_ != NULL )
            {
                param = lisp_.Car(node_);
                if ( !param || param->m_type != NT_NUMBER )
                {
                    cerr << "error: - expects a number" << endl;
                    return NULL;
                }
                result -= ((LispNumber*)param)->m_val;
                node_ = lisp_.Cdr(node_);
            }
        }
    }

    return new LispNumber(result);
}

// ------------------------------------------------------------
LispNode* LispMul( LispInterpreter &lisp_, LispNode *node_)
{
    double result = 1.;
    LispNode* param = NULL;

    while ( node_ != NULL )
    {
        param = lisp_.Car(node_);
        if ( !param || param->m_type != NT_NUMBER )
        {
            cerr << "error: * expects a number" << endl;
            return NULL;
        }
        result *= ((LispNumber*)param)->m_val;
        node_ = lisp_.Cdr(node_);
    }

    return new LispNumber(result);
}

// ------------------------------------------------------------
LispNode* LispDiv( LispInterpreter &lisp_, LispNode *node_)
{
    LispNode* param  = lisp_.Car(node_);

    if ( !param )
    {
        cerr << "error: / needs at least one parameter" << endl;
        return NULL;
    }
    if ( param->m_type != NT_NUMBER )
    {
        cerr << "error: / expects a number" << endl;
        return NULL;
    }

    double result = ((LispNumber*)param)->m_val;
    node_ = lisp_.Cdr(node_);
    if ( !lisp_.Car(node_) )
    {
        if ( result == 0.)
        {
            cerr << "error: division by zero" << endl;
            return NULL;
        }
        result = 1/result;
    }
    else
    {
        double div;
        while ( node_ != NULL )
        {
            param = lisp_.Car(node_);
            if ( !param || param->m_type != NT_NUMBER )
            {
                cerr << "error: / expects a number" << endl;
                return NULL;
            }
            div = ((LispNumber*)param)->m_val;
            if ( div == 0. )
            {
                cerr << "error: division by zero" << endl;
                return NULL;
            }
            result /= div;
            node_ = lisp_.Cdr(node_);
        }
    }

    return new LispNumber(result);
}

// ------------------------------------------------------------
LispNode* LispQuote( LispInterpreter &lisp_, LispNode *node_)
{
    return lisp_.Car(node_);
}

// ------------------------------------------------------------
LispNode* LispCar( LispInterpreter &lisp_, LispNode *node_)
{
    return lisp_.Car(lisp_.Car(node_));
}

// ------------------------------------------------------------
LispNode* LispCdr( LispInterpreter &lisp_, LispNode *node_)
{
    return lisp_.Cdr(lisp_.Car(node_));
}

// ------------------------------------------------------------
LispNode* LispEval( LispInterpreter &lisp_, LispNode *node_)
{
    return lisp_.Eval(lisp_.Car(node_));
}

// ------------------------------------------------------------
LispNode* LispCons( LispInterpreter &lisp_, LispNode *node_)
{
    return lisp_.Cons(lisp_.Car(node_), lisp_.Car(lisp_.Cdr(node_)) );
}

// ------------------------------------------------------------
LispNode* LispSetf( LispInterpreter &lisp_, LispNode *node_)
{
    LispNode* symbol  = lisp_.Car(node_);
    if ( !symbol || symbol->m_type != NT_SYMBOL)
    {
        cerr << "error: SETF needs a symbol as first param" << endl;
        return NULL;
    }

    LispNode* param = lisp_.Eval(lisp_.Car(lisp_.Cdr(node_)));
    if ( !param )
    {
        cerr << "error: SETF was called with too less arguments" << endl;
        cerr << "should be: (SETF symbol argument)" << endl;
        return NULL;
    }

    lisp_.MakeVar( (LispSymbol*)symbol, param);

    return param;
}

// ------------------------------------------------------------
// TODO
LispNode* LispDefun( LispInterpreter &lisp_, LispNode *node_)
{
    cout << "error: DEFUN is not yet implemented, sorry" << endl;

    return NULL;
}
