package org.jacorb.notification.filter.impl;

/*
 *        JacORB - a free Java ORB
 *
 *   Copyright (C) 1999-2014 Gerald Brose / The JacORB Team.
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Library General Public
 *   License as published by the Free Software Foundation; either
 *   version 2 of the License, or (at your option) any later version.
 *
 *   This library 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
 *   Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this library; if not, write to the Free
 *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

import org.jacorb.config.*;
import org.slf4j.Logger;
import org.jacorb.notification.filter.ETCLEvaluator;
import org.jacorb.notification.filter.EvaluationContext;
import org.jacorb.notification.filter.EvaluationException;
import org.jacorb.notification.filter.EvaluationResult;
import org.omg.CORBA.Any;
import org.omg.CORBA.ORB;
import org.omg.CORBA.TCKind;
import org.omg.CORBA.TypeCode;
import org.omg.CORBA.TypeCodePackage.BadKind;
import org.omg.CORBA.TypeCodePackage.Bounds;
import org.omg.CosNotification.Property;
import org.omg.DynamicAny.DynAny;
import org.omg.DynamicAny.DynAnyFactory;
import org.omg.DynamicAny.DynSequence;
import org.omg.DynamicAny.DynSequenceHelper;
import org.omg.DynamicAny.DynStruct;
import org.omg.DynamicAny.DynStructHelper;
import org.omg.DynamicAny.DynUnion;
import org.omg.DynamicAny.DynUnionHelper;
import org.omg.DynamicAny.DynAnyFactoryPackage.InconsistentTypeCode;
import org.omg.DynamicAny.DynAnyPackage.InvalidValue;
import org.omg.DynamicAny.DynAnyPackage.TypeMismatch;

/**
 * Provide the Basic operations needed to evaluate filter expressions on Anys.
 *
 * @author Alphonse Bendt
 */

public class DefaultETCLEvaluator implements ETCLEvaluator
{
    private static final String NAME = "name";

    private static final String VALUE = "value";

    ////////////////////////////////////////

    private final Logger logger_;

    private final DynAnyFactory dynAnyFactory_;

    private final ORB orb_;

    private final Any TRUE_ANY;

    private final Any FALSE_ANY;

    ////////////////////////////////////////

    public DefaultETCLEvaluator(ORB orb, Configuration config, DynAnyFactory dynAnyFactory)
    {
        orb_ = orb;

        logger_ = ((org.jacorb.config.Configuration) config).getLogger(getClass().getName());

        dynAnyFactory_ = dynAnyFactory;

        TRUE_ANY = orb_.create_any();

        FALSE_ANY = orb_.create_any();

        TRUE_ANY.insert_boolean(true);

        FALSE_ANY.insert_boolean(false);
    }

    ////////////////////////////////////////

    public boolean hasDefaultDiscriminator(Any any) throws EvaluationException
    {
        try
        {
            return (any.type().default_index() != -1);
        } catch (BadKind e)
        {
            throw newEvaluationException(e);
        }
    }

    public Any evaluateExistIdentifier(Any value, String identifier) throws EvaluationException
    {
        try
        {
            evaluateIdentifier(value, identifier);

            return TRUE_ANY;
        } catch (EvaluationException e)
        {
            return FALSE_ANY;
        }
    }

    public Any evaluateTypeName(Any value) throws EvaluationException
    {
        try
        {
            TypeCode _tc = value.type();
            Any _ret = orb_.create_any();
            _ret.insert_string(_tc.name());

            return _ret;
        } catch (BadKind e)
        {
            throw newEvaluationException(e);
        }
    }

    public Any evaluateRepositoryId(Any value) throws EvaluationException
    {
        try
        {
            TypeCode _tc = value.type();
            Any _ret = orb_.create_any();
            _ret.insert_string(_tc.id());

            return _ret;
        } catch (BadKind e)
        {
            throw newEvaluationException(e);
        }
    }

    public Any evaluateListLength(Any value) throws EvaluationException
    {
        final int _length;

        switch (value.type().kind().value()) {
        case TCKind._tk_array:
            DynAny _dynAny = toDynAny(value);
            _length = _dynAny.component_count();
            break;

        case TCKind._tk_sequence:
            DynSequence _dynSequence = toDynSequence(value);
            _length = _dynSequence.get_length();
            break;

        default:
            throw new EvaluationException("Neither array nor sequence");
        }

        Any _any = orb_.create_any();
        _any.insert_long(_length);

        return _any;
    }

    private String getDefaultUnionMemberName(TypeCode unionTypeCode) throws EvaluationException
    {
        try
        {
            int _defaultIndex = unionTypeCode.default_index();

            if (_defaultIndex != -1)
            {
                return unionTypeCode.member_name(_defaultIndex);
            }
        } catch (BadKind e)
        {
            throw newEvaluationException(e);
        } catch (Bounds e)
        {
            throw newEvaluationException(e);
        }

        throw new EvaluationException();
    }

    private String getUnionMemberNameFromDiscriminator(TypeCode unionTypeCode, int discriminator)
            throws EvaluationException
    {
        try
        {
            Any _any = orb_.create_any();

            switch (unionTypeCode.discriminator_type().kind().value()) {

            case TCKind._tk_long:
                _any.insert_long(discriminator);
                break;

            case TCKind._tk_ulong:
                _any.insert_ulong(discriminator);
                break;

            case TCKind._tk_short:
                _any.insert_short((short) discriminator);
                break;

            case TCKind._tk_double:
                _any.insert_double(discriminator);
                break;

            case TCKind._tk_ushort:
                _any.insert_ushort((short) discriminator);
                break;
            }

            int _memberCount = unionTypeCode.member_count();

            try
            {
                for (int _x = 0; _x < _memberCount; _x++)
                {
                    if (_any.equal(unionTypeCode.member_label(_x)))
                    {
                        return unionTypeCode.member_name(_x);
                    }
                }
            } catch (Bounds b)
            {
                // this should never happen as _x should be always < _memberCount.
                throw new RuntimeException();
            }

        } catch (BadKind e)
        {
            throw newEvaluationException(e);
        }

        throw new EvaluationException();
    }

    public Any evaluateUnion(Any value) throws EvaluationException
    {
        String _defaultMemberName = getDefaultUnionMemberName(value.type());

        return evaluateIdentifier(value, _defaultMemberName);
    }

    public Any evaluateUnion(Any value, int position) throws EvaluationException
    {
        final DynUnion _dynUnion = toDynUnion(value);

        _dynUnion.seek(0);

        if (logger_.isDebugEnabled())
        {
            logger_.debug("extract idx: " + position + " from Union " + _dynUnion.type());
        }

        String _discrimName = getUnionMemberNameFromDiscriminator(value.type(), position);

        return evaluateIdentifier(_dynUnion, _discrimName);
    }

    public Any evaluatePropertyList(Property[] list, String name)
    {
        if (logger_.isDebugEnabled())
        {
            logger_.debug("evaluatePropertyList " + list);
            logger_.debug("list length: " + list.length);
        }

        for (int x = 0; x < list.length; ++x)
        {
            if (logger_.isDebugEnabled())
            {
                logger_.debug(x + ": " + list[x].name + " => " + list[x].value);
            }

            if (name.equals(list[x].name))
            {
                return list[x].value;
            }
        }

        return null;
    }

    public Any evaluateNamedValueList(Any any, String name) throws EvaluationException
    {
        try
        {
            if (logger_.isDebugEnabled())
            {
                logger_.debug("evaluateNamedValueList(" + any + ", " + name + ")");
            }

            final DynAny _dynAny = toDynAny(any);
            final int _count = _dynAny.component_count();

            DynAny _cursor;
            Any _ret = null;

            _dynAny.rewind();

            if (logger_.isDebugEnabled())
            {
                logger_.debug("Entries: " + _count);
            }

            for (int _x = 0; _x < _count; _x++)
            {
                _dynAny.seek(_x);
                _cursor = _dynAny.current_component();
                _ret = evaluateNamedValue(_cursor, name);

                if (_ret != null)
                {
                    break;
                }
            }

            return _ret;
        } catch (TypeMismatch e)
        {
            throw newEvaluationException(e);
        }
    }

    private Any evaluateNamedValue(DynAny any, String name) throws EvaluationException
    {
        if (logger_.isDebugEnabled())
        {
            logger_.debug("evaluate assoc " + name + " on a Any of type: " + any.type());
        }

        final Any _result;

        final String _anyName = evaluateIdentifier(any, NAME).extract_string();

        if (logger_.isDebugEnabled())
        {
            logger_.debug("test if " + name + " == " + _anyName);
        }

        if (name.equals(_anyName))
        {
            logger_.debug("YES");
            _result = evaluateIdentifier(any, VALUE);
        }
        else
        {
            _result = null;
        }

        return _result;
    }

    public Any evaluateArrayIndex(Any any, int index) throws EvaluationException
    {
        try
        {
            if (logger_.isDebugEnabled())
            {
                logger_.debug("evaluate array idx " + index + " on a Any of type: " + any.type());
            }

            DynAny _dynAny = toDynAny(any);
            DynAny _cursor;

            _dynAny.rewind();
            _dynAny.seek(index);
            _cursor = _dynAny.current_component();

            if (logger_.isDebugEnabled())
            {
                logger_.debug("evaluation result is of type: " + _cursor.type());
            }

            return _cursor.to_any();
        } catch (TypeMismatch e)
        {
            throw newEvaluationException(e);
        }
    }

    private Any evaluateIdentifier(DynAny any, int position) throws EvaluationException
    {
        try
        {
            final DynAny _result;

            switch (any.type().kind().value()) {

            case TCKind._tk_struct:
                any.seek(position);
                _result = any.current_component();
                break;

            default:
                throw new EvaluationException("attempt to access member on non-struct");
            }

            return _result.to_any();
        } catch (TypeMismatch e)
        {
            throw newEvaluationException(e);
        }
    }

    public Any evaluateIdentifier(Any any, int position) throws EvaluationException
    {
        if (logger_.isDebugEnabled())
        {
            logger_.debug("evaluate idx " + position + " on Any");
        }

        DynAny _dynAny = toDynAny(any);

        return evaluateIdentifier(_dynAny, position);
    }

    public Any evaluateDiscriminator(Any any) throws EvaluationException
    {
        switch (any.type().kind().value()) {
        case TCKind._tk_union:
            DynUnion _dynUnion = toDynUnion(any);
            return _dynUnion.get_discriminator().to_any();

        default:
            throw new EvaluationException("any does not contain member _d");
        }
    }

    public EvaluationResult evaluateElementInSequence(EvaluationContext context,
            EvaluationResult element, Any sequence) throws EvaluationException
    {
        try
        {
            final DynSequence _dynSequence = DynSequenceHelper.narrow(toDynAny(sequence));

            _dynSequence.rewind();

            do
            {
                final DynAny _currentComponent = _dynSequence.current_component();

                final EvaluationResult _currentElement = EvaluationResult.fromAny(_currentComponent.to_any());

                if (element.compareTo(_currentElement) == 0)
                {
                    return EvaluationResult.BOOL_TRUE;
                }
            } while (_dynSequence.next());

            return EvaluationResult.BOOL_FALSE;
        } catch (TypeMismatch e)
        {
            throw newEvaluationException(e);
        }
    }

    /**
     * expensive
     */
    public Any evaluateIdentifier(Any any, String identifier) throws EvaluationException
    {
        // expensive call
        DynAny _dynAny = toDynAny(any);

        // expensive call
        return evaluateIdentifier(_dynAny, identifier);
    }

    private Any evaluateIdentifier(DynAny any, String identifier) throws EvaluationException
    {
        try
        {
            final String _strippedIdentifier = stripBackslash(identifier);

            if (logger_.isDebugEnabled())
            {
                logger_.debug("evaluate " + _strippedIdentifier + " on Any");
            }

            DynAny _cursor = any;

            SWITCH_LABEL:
            switch (any.type().kind().value()) {

            case TCKind._tk_struct:

                logger_.debug("Any is a struct");

                final DynStruct _dynStruct = DynStructHelper.narrow(any);
                String _currentName;

                _dynStruct.rewind();

                do
                {
                    _currentName = _dynStruct.current_member_name();

                    if (logger_.isDebugEnabled())
                    {
                        logger_.debug(" => " + _currentName);
                    }

                    if (_currentName.equals(_strippedIdentifier))
                    {
                        // expensive operation
                        _cursor = _dynStruct.current_component();
                        break SWITCH_LABEL;
                    }
                } while (_dynStruct.next());

                throw new EvaluationException("struct has no member " + _strippedIdentifier);

            case TCKind._tk_union:

                if (logger_.isDebugEnabled())
                {
                    logger_.debug("Any is a Union");
                }

                DynUnion _dynUnion = toDynUnion(any);

                if (_dynUnion.member_name().equals(_strippedIdentifier))
                {
                    _cursor = _dynUnion.member();
                }
                else
                {
                    if (logger_.isDebugEnabled())
                    {
                        logger_.debug(_dynUnion.member_name() + " != " + _strippedIdentifier);
                    }

                    throw new EvaluationException("member " + _strippedIdentifier
                            + " is not active on struct");
                }

                break;

            case TCKind._tk_any:
                logger_.debug("encapsulated any");

                return evaluateIdentifier(any.get_any(), _strippedIdentifier);

            default:
                logger_.debug("unknown " + any.type());

                return null;
            }

            if (logger_.isDebugEnabled())
            {
                logger_.debug("Result: " + _cursor);
            }

            if (_cursor != null && logger_.isDebugEnabled())
            {
                logger_.debug("evaluation result is of type: " + _cursor.type());
            }

            if (_cursor == null)
            {
                logger_.debug("Member not found");

                throw new EvaluationException("member not found");
            }

            return _cursor.to_any();
        } catch (InvalidValue e)
        {
            throw newEvaluationException(e);
        } catch (TypeMismatch e)
        {
            throw newEvaluationException(e);
        }
    }

    ////////////////////////////////////////

    private DynAny toDynAny(Any any) throws EvaluationException
    {
        try
        {
            return dynAnyFactory_.create_dyn_any(any);
        } catch (InconsistentTypeCode e)
        {
            throw newEvaluationException(e);
        }
    }

    private DynUnion toDynUnion(Any any) throws EvaluationException
    {
        return DynUnionHelper.narrow(toDynAny(any));
    }

    private DynUnion toDynUnion(DynAny dynAny)
    {
        return DynUnionHelper.narrow(dynAny);
    }

    private DynSequence toDynSequence(Any any) throws EvaluationException
    {
        return DynSequenceHelper.narrow(toDynAny(any));
    }

    private static String stripBackslash(String identifier)
    {
        StringBuffer _buffer = new StringBuffer();
        int _length = identifier.length();

        for (int _x = 0; _x < _length; _x++)
        {
            if (identifier.charAt(_x) != '\\')
            {
                _buffer.append(identifier.charAt(_x));
            }
        }

        return _buffer.toString();
    }

    private static EvaluationException newEvaluationException(Exception e)
    {
        return new EvaluationException(e.toString());
    }
}