/*************************************************************************************
 *  Copyright (C) 2010 by Aleix Pol <aleixpol@kde.org>                               *
 *                                                                                   *
 *  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, write to the Free Software                      *
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
 *************************************************************************************/

#ifndef EXPRESSIONTYPE_H
#define EXPRESSIONTYPE_H

#include "analitzaexport.h"
#include <QString>
#include <QList>
#include <QMap>

namespace Analitza
{

class ANALITZA_EXPORT ExpressionType
{
    public:
        ///Just use undefined type when returning from a recursion.
        ///Vector and MatrixRow are the same type.
        enum Type { Error=0, Value, Vector, List, Lambda, Any, Many, Object, Char, Bool, Matrix };
        QString toString() const;
        
        ExpressionType(Type t=Error, int any=-1);
        ExpressionType(Type t, const ExpressionType& contained, int s=0);
        ExpressionType(Type t, const QList<ExpressionType>& alternatives);
        
        /** Constructs a type that identifies a custom Object */
        ExpressionType(const QString& objectName);
        ExpressionType(const ExpressionType& t);
        
        ~ExpressionType() {/* delete contained; */}
        
        bool operator==(const ExpressionType& t) const;
        bool operator!=(const ExpressionType& t) const { return !operator==(t); }
        ExpressionType operator=(const ExpressionType& et);
        
        /** Depth search to check if it's defined */
        bool isError() const;
        
        Type type() const { return m_type; }
        bool hasContained() const;
        ExpressionType contained() const;
        QList<ExpressionType> alternatives() const { Q_ASSERT(m_type==Many); return m_contained; }
        
        /** In case it's a Many type, it adds @p t as an alternative. If @p t is a Many type too, they will be merged */
        void addAlternative(const ExpressionType& t);
        int size() const { return m_size; }
        int anyValue() const { return m_any; }
        
        ExpressionType& addParameter(const ExpressionType& t);
        QList<ExpressionType> parameters() const { Q_ASSERT(m_type==Lambda); return m_contained; }
        QList<ExpressionType>& parameters() { Q_ASSERT(m_type==Lambda); return m_contained; }
        ExpressionType returnValue() const;
        
        bool addAssumption(const QString& bvar, const ExpressionType& t);
        QMap<QString, ExpressionType> assumptions() const;
        QMap<QString, ExpressionType>& assumptions();
        ExpressionType assumptionFor(const QString& bvar) const { return m_assumptions.value(bvar); }
        void addAssumptions(const QMap< QString, ExpressionType >& a);
        void clearAssumptions();
        
        /** Returns a new type with the stars solved according t @p info */
        ExpressionType starsToType(const QMap<int, ExpressionType>& info) const;
        
        /** @returns true if the current type can be converted into @p type */
        bool canReduceTo(const ExpressionType& type) const;
        
        /**
         * @returns false in case that just by looking at the current type we see that it won't be able to be reduced to @p type.
         * Useful to improve error reporting
         */
        bool canCompareTo(const ExpressionType& type) const;
        
        int increaseStars(int stars);
        
        ExpressionType& simplifyStars();
        
        /** when it's a many type, reduce to the one(s) that can be reduced to */
        void reduce(const ExpressionType& type);
        
        /** @returns the name of a custom type */
        QString objectName() const;
        
        static ExpressionType minimumType(const ExpressionType& t1, const ExpressionType& t2);
        static bool assumptionsMerge(QMap<QString, ExpressionType>& data, const QMap<QString, ExpressionType>& newmap);
        static void assumptionsUnion(QMap<QString, ExpressionType>& data, const QMap<QString, ExpressionType>& newmap);
        static QMap<int, ExpressionType> computeStars(const QMap<int, ExpressionType>& initial, const ExpressionType& candidate, const ExpressionType& type);
        static bool matchAssumptions(QMap<int, ExpressionType>* stars, const QMap<QString, ExpressionType>& assum1, const QMap<QString, ExpressionType>& assum2);
        static QStringList wrongAssumptions(const QMap<QString, ExpressionType>& assum1, const QMap<QString, ExpressionType>& assum2);
        void removeAssumptions(const QStringList& bvarStrings);
        static QList<ExpressionType> lambdaFromArgs(const QList<ExpressionType>& args);
        static QList<ExpressionType> manyFromArgs(const QList<ExpressionType>& args);
    private:
        static void starsSimplification(ExpressionType& t, QMap<int, int>& reductions, int& next);
        static QMap<int, ExpressionType> processContained(const QMap<int, ExpressionType>& initial, const ExpressionType& candidate, const ExpressionType& type);
        
        Type m_type;
        ///In case of list and vector the inside type
        QList<ExpressionType> m_contained;
        QMap<QString, ExpressionType> m_assumptions;
        union { int m_size; int m_any; };
        QString m_objectName;
};

}

Q_DECLARE_TYPEINFO ( Analitza::ExpressionType, Q_MOVABLE_TYPE);

#endif // EXPRESSIONTYPE_H
