/*
// Licensed to DynamoBI Corporation (DynamoBI) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  DynamoBI licenses this file
// to you 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.
*/

#ifndef Fennel_CalcAssemblerException_Included
#define Fennel_CalcAssemblerException_Included

#include "fennel/calculator/CalcTypedefs.h"
#include "fennel/common/FennelExcn.h"

#include <strstream>

FENNEL_BEGIN_NAMESPACE

using namespace std;

/**
 * Exception indicating an invalid type
 */
class FENNEL_CALCULATOR_EXPORT InvalidTypeException
    : public FennelExcn
{
public:
    //! Constructs a InvalidTypeException with the given description,
    //! invalid type, and expected type.
    explicit
    InvalidTypeException(
        string str,
        StandardTypeDescriptorOrdinal type,
        StandardTypeDescriptorOrdinal expected)
        : FennelExcn(str),
        mInvalidType(type),
        mExpectedType(expected)
    {
        if (msg.length() > 0) {
            msg += ": ";
        }
        msg += "Invalid type ";
        msg += StandardTypeDescriptor::toString(type);
        msg += ", expecting ";
        msg += StandardTypeDescriptor::toString(expected);
    }
    ~InvalidTypeException() throw() {}

protected:
    //! Invalid type that was specified
    StandardTypeDescriptorOrdinal mInvalidType;
    //! Expected type
    StandardTypeDescriptorOrdinal mExpectedType;
};

/**
 * Exception indicating a type/value mismatch.
 */
template <typename T>
class InvalidValueException : public FennelExcn
{
public:
    //! Constructs a InvalidValueException with the given description,
    //! type, and value.
    explicit
    InvalidValueException(
        string str, StandardTypeDescriptorOrdinal type, T value)
        : FennelExcn(str), mType(type), mValue(value)
    {
        ostringstream ostr("");
        ostr << "Invalid value " << value << " for type "
             << StandardTypeDescriptor::toString(type);
        if (msg.length() > 0) {
            msg += ": ";
        }
        msg += ostr.str();
    }
    ~InvalidValueException() throw() {}

protected:
    StandardTypeDescriptorOrdinal mType;
    T mValue;
};

/**
 * A CalcAssemblerException represents a exception generated by the
 * CalcAssembler while assembling a calculator program.
 *
 * The CalcAssemblerException contains a description of the error, and
 * the location in the program where the error occured if available.
 * The text of the program code itself may also be included in the
 * exception.
 */
class FENNEL_CALCULATOR_EXPORT CalcAssemblerException
    : public FennelExcn
{
public:
    //! Constructs CalcAssemblerException with location information
    explicit
    CalcAssemblerException(string str, CalcYYLocType loc)
        : FennelExcn(str), mDescription(str), mLoc(loc), mLocValid(true)
    {
        msg += getLocationString();
    }

    //! Constructs CalcAssemblerException without location information
    explicit
    CalcAssemblerException(string str)
        : FennelExcn(str), mDescription(str), mLocValid(false)
    {
    }

    ~CalcAssemblerException() throw() {}

    //! Sets the location of the error
    void setLocation(CalcYYLocType loc)
    {
        mLocValid = true;
        mLoc = loc;
        msg = mDescription + getLocationString();
    }

    //! Sets the program text associated with this exception
    void setCode(string s)
    {
        mCode = s;
        if (mLocValid) {
            // The following assert is often not met with weird
            // programs: Better to get some sort of error message with
            // the invariant violated, than to just get an assert
            // error masking the problem.

            // SWZ: REVIEW: 9/15/2005: I find this to occur for
            // certain *sequences* of very simple programs, any one of
            // which when run singly, causes no problems!

            //assert(mLoc.first_pos <= mLoc.last_pos);

            // SWZ: REVIEW: 9/15/2005: Additionally, when invalid,
            // mLoc.first_pos can be very large (e.g. past the end of
            // s) which causes an out_of_range exception.  Added code
            // to prevent s.substr from throwing.  A better solution
            // would be to fix the bug that causes a supposedly valid
            // mLoc to have wildly out-of-range values.

            if (mLoc.first_pos <= mLoc.last_pos
                && mLoc.first_pos < s.length())
            {
                mCodeSnippet =
                    s.substr(
                        mLoc.first_pos,
                        mLoc.last_pos - mLoc.first_pos + 1);
            } else {
                mCodeSnippet = s;
            }
        }
    }

    //! Returns the program text
    const string& getCode() const
    {
        return mCode;
    }

    //! Returns the segment of program text that caused this exception
    const string& getCodeSnippet() const
    {
        return mCodeSnippet;
    }

    //! Returns a nicely formated string representing the location of the error
    const string getLocationString() const
    {
        string locStr;
        if (mLocValid) {
            ostringstream ostr(locStr);
            ostr << " (at line:col " << mLoc.first_line
                 << ":" << mLoc.first_column
                 << " to " << mLoc.last_line << ":" << mLoc.last_column
                 << ", characters " << mLoc.first_pos
                 << " to " << mLoc.last_pos << ")";
            locStr = ostr.str();
        } else {
            locStr = "Unknown location";
        }
        return locStr;
    }

    //! String describing the error
    string mDescription;
    //! Location of the error
    CalcYYLocType mLoc;
    //! Indicates whether the location (mLoc) is valid or not
    bool mLocValid;
    //! The complete text of the program code that was being assembled
    string mCode;
    //! The segment of program code that caused this exception
    string mCodeSnippet;
};

FENNEL_END_NAMESPACE

#endif

// End CalcAssemblerException.h
