/*
 *                         OpenSplice DDS
 *
 *   This software and documentation are Copyright 2006 to TO_YEAR PrismTech
 *   Limited, its affiliated companies and licensors. All rights reserved.
 *
 *   Licensed 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.
 *
 */
package org.opensplice.dds.sub;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.omg.dds.core.DDSObject;
import org.omg.dds.core.Duration;
import org.omg.dds.core.InstanceHandle;
import org.omg.dds.core.ServiceEnvironment;
import org.omg.dds.core.Time;
import org.omg.dds.core.policy.ResourceLimits;
import org.omg.dds.core.status.LivelinessChangedStatus;
import org.omg.dds.core.status.RequestedDeadlineMissedStatus;
import org.omg.dds.core.status.RequestedIncompatibleQosStatus;
import org.omg.dds.core.status.SampleLostStatus;
import org.omg.dds.core.status.SampleRejectedStatus;
import org.omg.dds.core.status.SubscriptionMatchedStatus;
import org.omg.dds.sub.DataReaderQos;
import org.omg.dds.sub.ReadCondition;
import org.omg.dds.sub.Sample;
import org.omg.dds.sub.Sample.Iterator;
import org.omg.dds.topic.PublicationBuiltinTopicData;
import org.opensplice.dds.core.AbstractDDSObject;
import org.opensplice.dds.core.DDSExceptionImpl;
import org.opensplice.dds.core.IllegalArgumentExceptionImpl;
import org.opensplice.dds.core.InstanceHandleImpl;
import org.opensplice.dds.core.OsplServiceEnvironment;
import org.opensplice.dds.core.PreconditionNotMetExceptionImpl;
import org.opensplice.dds.core.Utilities;
import org.opensplice.dds.core.policy.PolicyConverter;
import org.opensplice.dds.core.status.StatusConverter;
import org.opensplice.dds.topic.PublicationBuiltinTopicDataImpl;

import DDS.SampleInfoHolder;
import DDS.SampleInfoSeqHolder;

public class ReflectionDataReader<TYPE, OUT_TYPE> extends AbstractDDSObject
        implements
        DDSObject {
    private final OsplServiceEnvironment environment;
    private final AbstractDataReader<OUT_TYPE> reader;
    private final DDS.DataReader old;
    private final Class<?> sampleSeqHolderClz;
    private final Field sampleSeqHolderValueField;

    private final Class<?> sampleHolderClz;
    private final Field sampleHolderValueField;

    private final Method read;
    private final Method take;
    private final Method readCondition;
    private final Method takeCondition;
    private final Method readNextSample;
    private final Method takeNextSample;
    private final Method readInstance;
    private final Method takeInstance;
    private final Method readNextInstance;
    private final Method takeNextInstance;
    private final Method readNextInstanceCondition;
    private final Method takeNextInstanceCondition;
    private final Method returnLoan;
    private final Method getKeyValue;
    private final Method lookupInstance;

    public ReflectionDataReader(OsplServiceEnvironment environment,
            AbstractDataReader<OUT_TYPE> reader, Class<TYPE> ddsTypeClz) {
        this.environment = environment;
        this.reader = reader;
        this.old = reader.getOld();

        Class<?> typedReaderClz;
        String typedReaderClzName = ddsTypeClz.getName() + "DataReaderImpl";

        try {
            typedReaderClz = Class.forName(typedReaderClzName);

            this.sampleHolderClz = Class.forName(ddsTypeClz.getName() + "Holder");
            this.sampleHolderValueField = this.sampleHolderClz
                    .getDeclaredField("value");

            this.sampleSeqHolderClz = Class.forName(ddsTypeClz.getName()
                    + "SeqHolder");
            this.sampleSeqHolderValueField = this.sampleSeqHolderClz
                    .getDeclaredField("value");

            this.read = typedReaderClz.getMethod("read",
                    this.sampleSeqHolderClz, SampleInfoSeqHolder.class,
                    int.class, int.class, int.class, int.class);
            this.take = typedReaderClz.getMethod("take",
                    this.sampleSeqHolderClz, SampleInfoSeqHolder.class,
                    int.class, int.class, int.class, int.class);

            this.readCondition = typedReaderClz.getMethod("read_w_condition",
                    this.sampleSeqHolderClz, SampleInfoSeqHolder.class,
                    int.class, DDS.ReadCondition.class);
            this.takeCondition = typedReaderClz.getMethod("take_w_condition",
                    this.sampleSeqHolderClz, SampleInfoSeqHolder.class,
                    int.class, DDS.ReadCondition.class);

            this.readNextSample = typedReaderClz.getMethod("read_next_sample",
                    this.sampleHolderClz, SampleInfoHolder.class);
            this.takeNextSample = typedReaderClz.getMethod("take_next_sample",
                    this.sampleHolderClz, SampleInfoHolder.class);

            this.readInstance = typedReaderClz.getMethod("read_instance",
                    this.sampleSeqHolderClz, SampleInfoSeqHolder.class,
                    int.class, long.class, int.class, int.class, int.class);
            this.takeInstance = typedReaderClz.getMethod("take_instance",
                    this.sampleSeqHolderClz, SampleInfoSeqHolder.class,
                    int.class, long.class, int.class, int.class, int.class);

            this.readNextInstance = typedReaderClz.getMethod(
                    "read_next_instance", this.sampleSeqHolderClz,
                    SampleInfoSeqHolder.class, int.class, long.class,
                    int.class, int.class, int.class);
            this.takeNextInstance = typedReaderClz.getMethod(
                    "take_next_instance", this.sampleSeqHolderClz,
                    SampleInfoSeqHolder.class, int.class, long.class,
                    int.class, int.class, int.class);

            this.readNextInstanceCondition = typedReaderClz.getMethod(
                    "read_next_instance_w_condition", this.sampleSeqHolderClz,
                    SampleInfoSeqHolder.class, int.class, long.class,
                    DDS.ReadCondition.class);
            this.takeNextInstanceCondition = typedReaderClz.getMethod(
                    "take_next_instance_w_condition", this.sampleSeqHolderClz,
                    SampleInfoSeqHolder.class, int.class, long.class,
                    DDS.ReadCondition.class);

            this.returnLoan = typedReaderClz.getMethod("return_loan",
                    this.sampleSeqHolderClz, SampleInfoSeqHolder.class);

            this.getKeyValue = typedReaderClz.getMethod("get_key_value",
                    this.sampleHolderClz, long.class);
            this.lookupInstance = typedReaderClz.getMethod("lookup_instance",
                    ddsTypeClz);
        } catch (ClassNotFoundException e) {
            throw new PreconditionNotMetExceptionImpl(
                    environment,
                    "Cannot find Typed DataReader '"
                            + typedReaderClzName
                            + "' that should have been generated manually with the OpenSplice IDL pre-processor.("
                            + e.getMessage() + ").");
        } catch (NoSuchMethodException e) {
            throw new DDSExceptionImpl(environment,
                    "Cannot find correct methods in OpenSplice IDL pre-processor generated class: "
                            + typedReaderClzName + " (" + e.getMessage() + ").");
        } catch (NoSuchFieldException e) {
            throw new DDSExceptionImpl(
                    environment,
                    "Cannot find 'value' field in "
                            + "the typed sampleHolderClass "
                            + "that should have been generated by the OpenSplice IDL pre-processor ("
                            + e.getMessage() + ").");
        } catch (SecurityException e) {
            throw new PreconditionNotMetExceptionImpl(
                    environment,
                    "Insufficient rights to find methods/fields in code that has been generated by the OpenSplice IDL pre-processor ("
                            + e.getMessage() + ").");
        }
    }

    @Override
    public ServiceEnvironment getEnvironment() {
        return this.environment;
    }

    public DataReaderQos getQos() {
        DDS.DataReaderQosHolder holder = new DDS.DataReaderQosHolder();
        int rc = this.old.get_qos(holder);
        Utilities.checkReturnCode(rc, this.environment,
                "DataReader.getQos() failed.");

        return DataReaderQosImpl.convert(this.environment, holder.value);
    }

    public void setQos(DataReaderQos qos) {
        DataReaderQosImpl q;

        if (qos == null) {
            throw new IllegalArgumentExceptionImpl(this.environment,
                    "Supplied DataReaderQos is null.");
        }
        try {
            q = (DataReaderQosImpl) qos;
        } catch (ClassCastException e) {
            throw new IllegalArgumentExceptionImpl(this.environment,
                    "Setting non-OpenSplice Qos not supported.");
        }
        int rc = this.old.set_qos(q.convert());
        Utilities.checkReturnCode(rc, this.environment,
                "DataReader.setQos() failed.");
    }

    public SampleRejectedStatus getSampleRejectedStatus() {
        DDS.SampleRejectedStatusHolder holder = new DDS.SampleRejectedStatusHolder();

        int rc = this.old.get_sample_rejected_status(holder);
        Utilities.checkReturnCode(rc, this.environment,
                "DataReader.getSampleRejectedStatus() failed.");

        return StatusConverter.convert(this.environment, holder.value);
    }

    public LivelinessChangedStatus getLivelinessChangedStatus() {
        DDS.LivelinessChangedStatusHolder holder = new DDS.LivelinessChangedStatusHolder();

        int rc = this.old.get_liveliness_changed_status(holder);
        Utilities.checkReturnCode(rc, this.environment,
                "DataReader.getLivelinessChangedStatus() failed.");

        return StatusConverter.convert(this.environment, holder.value);
    }

    public RequestedDeadlineMissedStatus getRequestedDeadlineMissedStatus() {
        DDS.RequestedDeadlineMissedStatusHolder holder = new DDS.RequestedDeadlineMissedStatusHolder();

        int rc = this.old.get_requested_deadline_missed_status(holder);
        Utilities.checkReturnCode(rc, this.environment,
                "DataReader.getRequestedDeadlineMissedStatus() failed.");

        return StatusConverter.convert(this.environment, holder.value);
    }

    public RequestedIncompatibleQosStatus getRequestedIncompatibleQosStatus() {
        DDS.RequestedIncompatibleQosStatusHolder holder = new DDS.RequestedIncompatibleQosStatusHolder();

        int rc = this.old.get_requested_incompatible_qos_status(holder);
        Utilities.checkReturnCode(rc, this.environment,
                "DataReader.getRequestedIncompatibleQosStatus() failed.");

        return StatusConverter.convert(this.environment, holder.value);
    }

    public SubscriptionMatchedStatus getSubscriptionMatchedStatus() {
        DDS.SubscriptionMatchedStatusHolder holder = new DDS.SubscriptionMatchedStatusHolder();

        int rc = this.old.get_subscription_matched_status(holder);
        Utilities.checkReturnCode(rc, this.environment,
                "DataReader.getSubscriptionMatchedStatus() failed.");

        return StatusConverter.convert(this.environment, holder.value);
    }

    public SampleLostStatus getSampleLostStatus() {
        DDS.SampleLostStatusHolder holder = new DDS.SampleLostStatusHolder();

        int rc = this.old.get_sample_lost_status(holder);
        Utilities.checkReturnCode(rc, this.environment,
                "DataReader.getSampleLostStatus() failed.");

        return StatusConverter.convert(this.environment, holder.value);
    }

    public void waitForHistoricalData(Duration maxWait) throws TimeoutException {
        int rc = this.old.wait_for_historical_data(Utilities.convert(this.environment,
                maxWait));
        Utilities.checkReturnCodeWithTimeout(rc, this.environment,
                "DataReader.waitForHistoricalData() failed.");

    }

    public void waitForHistoricalData(long maxWait, TimeUnit unit)
            throws TimeoutException {
        this.waitForHistoricalData(this.environment.getSPI().newDuration(
                maxWait, unit));
    }

    public void waitForHistoricalData(String filterExpression,
            List<String> filterParameters, Time minSourceTimestamp,
            Time maxSourceTimestamp, ResourceLimits resourceLimits,
            Duration maxWait) throws TimeoutException {
        if (resourceLimits == null) {
            throw new IllegalArgumentExceptionImpl(environment,
                    "Invalid resourceLimits (null) supplied.");
        }
        String[] params;

        if(filterParameters != null){
            params = filterParameters.toArray(new String[filterParameters
                    .size()]);
        } else {
            params = null;
        }
        int rc = this.old.wait_for_historical_data_w_condition(
                filterExpression, params,
                Utilities.convert(this.environment, minSourceTimestamp),
                Utilities.convert(this.environment, maxSourceTimestamp),
                PolicyConverter.convert(this.environment, resourceLimits),
                Utilities.convert(this.environment, maxWait));
        Utilities.checkReturnCodeWithTimeout(rc, this.environment,
                "DataReader.waitForHistoricalData() failed.");
    }

    public void waitForHistoricalData(String filterExpression,
            List<String> filterParameters, Time minSourceTimestamp,
            Time maxSourceTimestamp, ResourceLimits resourceLimits,
            long maxWait, TimeUnit unit) throws TimeoutException {
        this.waitForHistoricalData(filterExpression, filterParameters,
                minSourceTimestamp, maxSourceTimestamp, resourceLimits,
                this.environment.getSPI().newDuration(maxWait, unit));
    }

    public Set<InstanceHandle> getMatchedPublications() {
        DDS.InstanceHandleSeqHolder holder = new DDS.InstanceHandleSeqHolder();
        Set<InstanceHandle> handles;

        int rc = this.old.get_matched_publications(holder);
        Utilities.checkReturnCode(rc, this.environment,
                "DataReader.getMatchedPublications() failed.");

        handles = new HashSet<InstanceHandle>();

        for (long handle : holder.value) {
            handles.add(Utilities.convert(this.environment, handle));
        }
        return handles;
    }

    public PublicationBuiltinTopicData getMatchedPublicationData(
            InstanceHandle publicationHandle) {
        DDS.PublicationBuiltinTopicDataHolder holder = new DDS.PublicationBuiltinTopicDataHolder();
        int rc = this.old.get_matched_publication_data(holder,
                Utilities.convert(this.environment, publicationHandle));
        Utilities.checkReturnCode(rc, this.environment,
                "DataReader.getMatchedPublicationData() failed.");
        if (holder.value != null) {
            return new PublicationBuiltinTopicDataImpl(this.environment,
                    holder.value);
        }
        throw new PreconditionNotMetExceptionImpl(this.environment,
                    "No data for this instanceHandle.");
    }

    public TYPE getKeyValue(TYPE keyHolder, InstanceHandle handle) {
        Object sampleHolder;

        if (keyHolder == null) {
            throw new IllegalArgumentException(
                    "Invalid key holder (null) provided.");
        }

        try {
            sampleHolder = this.sampleHolderClz.newInstance();
            this.sampleHolderValueField.set(sampleHolder, keyHolder);
            int rc = (Integer) this.getKeyValue.invoke(this.old, sampleHolder,
                    Utilities.convert(this.environment, handle));
            Utilities.checkReturnCode(rc, this.environment,
                    "DataReader.getKeyValue() failed.");
        } catch (InstantiationException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalAccessException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalArgumentExceptionImpl e) {
            throw e;
        } catch (IllegalArgumentException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (InvocationTargetException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (ClassCastException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        }
        return keyHolder;
    }

    @SuppressWarnings("unchecked")
    public TYPE getKeyValue(InstanceHandle handle) {
        Object sampleHolder;


        try {
            sampleHolder = this.sampleHolderClz.newInstance();
            int rc = (Integer) this.getKeyValue.invoke(this.old, sampleHolder,
                    Utilities.convert(this.environment, handle));
            Utilities.checkReturnCode(rc, this.environment,
                    "DataReader.getKeyValue() failed.");

            return (TYPE) this.sampleHolderValueField.get(sampleHolder);
        } catch (InstantiationException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalAccessException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalArgumentExceptionImpl e) {
            throw e;
        } catch (IllegalArgumentException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (InvocationTargetException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (ClassCastException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        }
    }

    public InstanceHandle lookupInstance(TYPE keyHolder) {
        long oldHandle;

        if (keyHolder == null) {
            throw new IllegalArgumentException(
                    "Invalid key holder (null) provided.");
        }

        try {
            oldHandle = (Long) this.lookupInstance.invoke(this.old, keyHolder);
        } catch (IllegalAccessException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalArgumentExceptionImpl e) {
            throw e;
        } catch (IllegalArgumentException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (InvocationTargetException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (ClassCastException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        }
        return Utilities.convert(this.environment, oldHandle);
    }

    public void returnLoan(Object sampleSeqHolder,
            DDS.SampleInfoSeqHolder infoSeqHolder) {
        try {
            int rc = (Integer) this.returnLoan.invoke(this.old,
                    sampleSeqHolder, infoSeqHolder);
            Utilities.checkReturnCode(rc, this.environment,
                    "Return loan failed.");
        } catch (IllegalAccessException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (InvocationTargetException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        }
    }

    public Iterator<OUT_TYPE> read() {
        return this.read(DDS.LENGTH_UNLIMITED.value);
    }

    @SuppressWarnings("unchecked")
    public Iterator<OUT_TYPE> read(
            org.omg.dds.sub.DataReader.Selector<OUT_TYPE> query) {
        DDS.SampleInfoSeqHolder info = new DDS.SampleInfoSeqHolder();
        Object sampleSeqHolder;

        if (query == null) {
            throw new IllegalArgumentExceptionImpl(this.environment,
                    "Invalid Selector (null) provided.");
        }
        ReadCondition<OUT_TYPE> condition = null;

        if (query.getQueryExpression() != null) {
            condition = query.getCondition();
        }
        InstanceHandle instance = query.getInstance();

        try {
            DataStateImpl state = (DataStateImpl) query.getDataState();
            InstanceHandleImpl handle = (InstanceHandleImpl) instance;
            sampleSeqHolder = this.sampleSeqHolderClz.newInstance();
            int rc;

            if (condition != null) {
                DDS.ReadCondition oldCondition = ((ReadConditionImpl<OUT_TYPE>) condition)
                        .getOld();

                if (query.retrieveNextInstance()) {
                    // read_next_instance_w_condition
                    rc = (Integer) this.readNextInstanceCondition.invoke(
                            this.old, sampleSeqHolder, info,
                            query.getMaxSamples(), handle.getValue(),
                            oldCondition);
                } else {
                    // read_w_condition
                    rc = (Integer) this.readCondition.invoke(this.old,
                            sampleSeqHolder, info, query.getMaxSamples(),
                            oldCondition);
                }
            } else {
                // read_next_instance
                if (query.retrieveNextInstance()) {
                    rc = (Integer) this.readNextInstance.invoke(this.old,
                            sampleSeqHolder, info, query.getMaxSamples(),
                            handle.getValue(), state.getOldSampleState(),
                            state.getOldViewState(),
                            state.getOldInstanceState());
                } else {
                    // read
                    if (instance.isNil()) {
                        rc = (Integer) this.read.invoke(this.old,
                                sampleSeqHolder, info, query.getMaxSamples(),
                                state.getOldSampleState(),
                                state.getOldViewState(),
                                state.getOldInstanceState());
                    }
                    // read_instance
                    else {
                        rc = (Integer) this.readInstance.invoke(this.old,
                                sampleSeqHolder, info, query.getMaxSamples(),
                                handle.getValue(), state.getOldSampleState(),
                                state.getOldViewState(),
                                state.getOldInstanceState());
                    }
                }
            }
            Utilities.checkReturnCode(rc, this.environment,
                    "DataReader.read() failed.");
        } catch (InstantiationException e) {
            throw new DDSExceptionImpl(this.environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalAccessException e) {
            throw new DDSExceptionImpl(this.environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalArgumentExceptionImpl e) {
            throw e;
        } catch (IllegalArgumentException e) {
            throw new DDSExceptionImpl(this.environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (InvocationTargetException e) {
            throw new DDSExceptionImpl(this.environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (ClassCastException e) {
            throw new IllegalArgumentExceptionImpl(
                    this.environment,
                    "Reading with non-OpenSplice DataState, InstanceHandle or ReadCondition not supported");
        }
        return (Iterator<OUT_TYPE>) this.reader.createIterator(
                sampleSeqHolder, this.sampleSeqHolderValueField, info);
    }

    @SuppressWarnings("unchecked")
    public Iterator<OUT_TYPE> read(int maxSamples) {
        DDS.SampleInfoSeqHolder info = new DDS.SampleInfoSeqHolder();
        Object sampleSeqHolder;

        try {
            sampleSeqHolder = this.sampleSeqHolderClz.newInstance();
            int rc = (Integer) this.read.invoke(this.old, sampleSeqHolder,
                    info, maxSamples, DDS.ANY_SAMPLE_STATE.value,
                    DDS.ANY_VIEW_STATE.value, DDS.ANY_INSTANCE_STATE.value);
            Utilities.checkReturnCode(rc, this.environment,
                    "DataReader.read() failed.");
        } catch (InstantiationException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalAccessException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalArgumentExceptionImpl e) {
            throw e;
        } catch (IllegalArgumentException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (InvocationTargetException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        }
        return (Iterator<OUT_TYPE>) this.reader.createIterator(
                sampleSeqHolder, this.sampleSeqHolderValueField, info);
    }

    public Iterator<OUT_TYPE> take() {
        return this.take(DDS.LENGTH_UNLIMITED.value);
    }

    @SuppressWarnings("unchecked")
    public Iterator<OUT_TYPE> take(int maxSamples) {
        DDS.SampleInfoSeqHolder info = new DDS.SampleInfoSeqHolder();
        Object sampleSeqHolder;

        try {
            sampleSeqHolder = this.sampleSeqHolderClz.newInstance();
            int rc = (Integer) this.take.invoke(this.old, sampleSeqHolder,
                    info, maxSamples, DDS.ANY_SAMPLE_STATE.value,
                    DDS.ANY_VIEW_STATE.value, DDS.ANY_INSTANCE_STATE.value);
            Utilities.checkReturnCode(rc, this.environment,
                    "DataReader.take() failed.");
        } catch (InstantiationException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalAccessException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalArgumentExceptionImpl e) {
            throw e;
        } catch (IllegalArgumentException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (InvocationTargetException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        }
        return (Iterator<OUT_TYPE>) this.reader.createIterator(
                sampleSeqHolder, this.sampleSeqHolderValueField, info);
    }

    public Field getSampleSeqHolderValueField() {
        return this.sampleSeqHolderValueField;
    }

    @SuppressWarnings("unchecked")
    public Iterator<OUT_TYPE> take(
            org.omg.dds.sub.DataReader.Selector<OUT_TYPE> query) {
        DDS.SampleInfoSeqHolder info = new DDS.SampleInfoSeqHolder();
        Object sampleSeqHolder;

        if (query == null) {
            throw new IllegalArgumentExceptionImpl(this.environment,
                    "Invalid Selector (null) provided.");
        }
        ReadCondition<OUT_TYPE> condition = null;

        if (query.getQueryExpression() != null) {
            condition = query.getCondition();
        }
        InstanceHandle instance = query.getInstance();

        try {
            DataStateImpl state = (DataStateImpl) query.getDataState();
            InstanceHandleImpl handle = (InstanceHandleImpl) instance;
            sampleSeqHolder = this.sampleSeqHolderClz.newInstance();
            int rc;

            if (condition != null) {
                DDS.ReadCondition oldCondition = ((ReadConditionImpl<OUT_TYPE>) condition)
                        .getOld();

                if (query.retrieveNextInstance()) {
                    // take_next_instance_w_condition
                    rc = (Integer) this.takeNextInstanceCondition.invoke(
                            this.old, sampleSeqHolder, info,
                            query.getMaxSamples(), handle.getValue(),
                            oldCondition);
                } else {
                    // take_w_condition
                    rc = (Integer) this.takeCondition.invoke(this.old,
                            sampleSeqHolder, info, query.getMaxSamples(),
                            oldCondition);
                }
            } else {
                // take_next_instance
                if (query.retrieveNextInstance()) {
                    rc = (Integer) this.takeNextInstance.invoke(this.old,
                            sampleSeqHolder, info, query.getMaxSamples(),
                            handle.getValue(), state.getOldSampleState(),
                            state.getOldViewState(),
                            state.getOldInstanceState());
                } else {
                    // take
                    if (instance.isNil()) {
                        rc = (Integer) this.take.invoke(this.old,
                                sampleSeqHolder, info, query.getMaxSamples(),
                                state.getOldSampleState(),
                                state.getOldViewState(),
                                state.getOldInstanceState());
                    }
                    // take_instance
                    else {
                        rc = (Integer) this.takeInstance.invoke(this.old,
                                sampleSeqHolder, info, query.getMaxSamples(),
                                handle.getValue(), state.getOldSampleState(),
                                state.getOldViewState(),
                                state.getOldInstanceState());
                    }
                }
            }
            Utilities.checkReturnCode(rc, this.environment,
                    "DataReader.take() failed.");
        } catch (InstantiationException e) {
            throw new DDSExceptionImpl(this.environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalAccessException e) {
            throw new DDSExceptionImpl(this.environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalArgumentExceptionImpl e) {
            throw e;
        } catch (IllegalArgumentException e) {
            throw new DDSExceptionImpl(this.environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (InvocationTargetException e) {
            throw new DDSExceptionImpl(this.environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (ClassCastException e) {
            throw new IllegalArgumentExceptionImpl(
                    this.environment,
                    "Taking with non-OpenSplice DataState, InstanceHandle or ReadCondition not supported");
        }
        return (Iterator<OUT_TYPE>) this.reader.createIterator(
                sampleSeqHolder, this.sampleSeqHolderValueField, info);
    }

    @SuppressWarnings("unchecked")
    public boolean readNextSample(SampleImpl<TYPE> sample) {
        DDS.SampleInfoHolder info;
        Object sampleHolder;
        boolean result;

        if (sample == null) {
            throw new IllegalArgumentExceptionImpl(this.environment,
                    "Provided an invalid null sample.");
        }
        try {
            info = new DDS.SampleInfoHolder();
            sampleHolder = this.sampleHolderClz.newInstance();

            this.sampleHolderValueField.set(sampleHolder, sample.getData());
            int rc = (Integer) this.readNextSample.invoke(this.old,
                    sampleHolder, info);
            Utilities.checkReturnCode(rc, this.environment,
                    "DataReader.readNextSample() failed.");

            if (rc == DDS.RETCODE_OK.value) {
                sample.setContent(
                        (TYPE) this.sampleHolderValueField.get(sampleHolder),
                        info.value);
                result = true;
            } else {
                result = false;
            }

        } catch (InstantiationException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalAccessException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalArgumentExceptionImpl e) {
            throw e;
        } catch (IllegalArgumentException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (InvocationTargetException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (ClassCastException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        }
        return result;
    }

    @SuppressWarnings("unchecked")
    public boolean takeNextSample(SampleImpl<TYPE> sample) {
        DDS.SampleInfoHolder info;
        Object sampleHolder;
        boolean result;

        if (sample == null) {
            throw new IllegalArgumentExceptionImpl(this.environment,
                    "Provided an invalid null sample.");
        }
        try {
            info = new DDS.SampleInfoHolder();
            sampleHolder = this.sampleHolderClz.newInstance();
            this.sampleHolderValueField.set(sampleHolder, sample.getData());
            int rc = (Integer) this.takeNextSample.invoke(this.old,
                    sampleHolder, info);
            Utilities.checkReturnCode(rc, this.environment,
                    "DataReader.readNextSample() failed.");

            if (rc == DDS.RETCODE_OK.value) {
                sample.setContent(
                        (TYPE) this.sampleHolderValueField.get(sampleHolder),
                        info.value);
                result = true;
            } else {
                result = false;
            }

        } catch (InstantiationException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalAccessException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalArgumentExceptionImpl e) {
            throw e;
        } catch (IllegalArgumentException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (InvocationTargetException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (ClassCastException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        }
        return result;
    }

    public List<Sample<OUT_TYPE>> take(List<Sample<OUT_TYPE>> samples) {
        PreAllocator<OUT_TYPE> pa = this.reader.getPreAllocator(samples,
                this.sampleSeqHolderClz, this.sampleSeqHolderValueField);

        try {
            int rc = (Integer) this.take.invoke(this.old,
                    pa.getDataSeqHolder(), pa.getInfoSeqHolder(),
                    DDS.LENGTH_UNLIMITED.value, DDS.ANY_SAMPLE_STATE.value,
                    DDS.ANY_VIEW_STATE.value, DDS.ANY_INSTANCE_STATE.value);
            Utilities.checkReturnCode(rc, this.environment,
                    "DataReader.read() failed.");
        } catch (IllegalAccessException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalArgumentExceptionImpl e) {
            throw e;
        } catch (IllegalArgumentException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (InvocationTargetException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        }
        pa.updateReferences();

        return pa.getSampleList();
    }

    public List<Sample<OUT_TYPE>> take(List<Sample<OUT_TYPE>> samples,
            org.omg.dds.sub.DataReader.Selector<OUT_TYPE> selector) {
        if (selector == null) {
            return this.take(samples);
        }

        PreAllocator<OUT_TYPE> pa = this.reader.getPreAllocator(samples,
                this.sampleSeqHolderClz, this.sampleSeqHolderValueField);
        ReadCondition<OUT_TYPE> condition = null;

        if (selector.getQueryExpression() != null) {
            condition = selector.getCondition();
        }
        InstanceHandle instance = selector.getInstance();

        try {
            DataStateImpl state = (DataStateImpl) selector.getDataState();
            InstanceHandleImpl handle = (InstanceHandleImpl) instance;
            int rc;

            if (condition != null) {
                DDS.ReadCondition oldCondition = ((ReadConditionImpl<OUT_TYPE>) condition)
                        .getOld();

                if (selector.retrieveNextInstance()) {
                    // take_next_instance_w_condition
                    rc = (Integer) this.takeNextInstanceCondition.invoke(
                            this.old, pa.getDataSeqHolder(),
                            pa.getInfoSeqHolder(), selector.getMaxSamples(),
                            handle.getValue(), oldCondition);
                } else {
                    // take_w_condition
                    rc = (Integer) this.takeCondition.invoke(this.old,
                            pa.getDataSeqHolder(), pa.getInfoSeqHolder(),
                            selector.getMaxSamples(), oldCondition);
                }
            } else {
                // take_next_instance
                if (selector.retrieveNextInstance()) {
                    rc = (Integer) this.takeNextInstance.invoke(this.old,
                            pa.getDataSeqHolder(), pa.getInfoSeqHolder(),
                            selector.getMaxSamples(), handle.getValue(),
                            state.getOldSampleState(), state.getOldViewState(),
                            state.getOldInstanceState());
                } else {
                    // take
                    if (instance.isNil()) {
                        rc = (Integer) this.take.invoke(this.old,
                                pa.getDataSeqHolder(), pa.getInfoSeqHolder(),
                                selector.getMaxSamples(),
                                state.getOldSampleState(),
                                state.getOldViewState(),
                                state.getOldInstanceState());
                    }
                    // take_instance
                    else {
                        rc = (Integer) this.takeInstance.invoke(this.old,
                                pa.getDataSeqHolder(), pa.getInfoSeqHolder(),
                                selector.getMaxSamples(), handle.getValue(),
                                state.getOldSampleState(),
                                state.getOldViewState(),
                                state.getOldInstanceState());
                    }
                }
            }
            Utilities.checkReturnCode(rc, this.environment,
                    "DataReader.take() failed.");
        } catch (IllegalAccessException e) {
            throw new DDSExceptionImpl(this.environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalArgumentExceptionImpl e) {
            throw e;
        } catch (IllegalArgumentException e) {
            throw new DDSExceptionImpl(this.environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (InvocationTargetException e) {
            throw new DDSExceptionImpl(this.environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (ClassCastException e) {
            throw new IllegalArgumentExceptionImpl(
                    this.environment,
                    "Reading with non-OpenSplice DataState, InstanceHandle or ReadCondition not supported");
        }
        pa.updateReferences();

        return pa.getSampleList();
    }

    public List<Sample<OUT_TYPE>> read(List<Sample<OUT_TYPE>> samples,
            org.omg.dds.sub.DataReader.Selector<OUT_TYPE> selector) {

        if (selector == null) {
            return this.read(samples);
        }

        PreAllocator<OUT_TYPE> pa = this.reader.getPreAllocator(samples,
                this.sampleSeqHolderClz, this.sampleSeqHolderValueField);

        ReadCondition<OUT_TYPE> condition = null;

        if (selector.getQueryExpression() != null) {
            condition = selector.getCondition();
        }
        InstanceHandle instance = selector.getInstance();

        try {
            DataStateImpl state = (DataStateImpl) selector.getDataState();
            InstanceHandleImpl handle = (InstanceHandleImpl) instance;
            int rc;

            if (condition != null) {
                DDS.ReadCondition oldCondition = ((ReadConditionImpl<OUT_TYPE>) condition)
                        .getOld();

                if (selector.retrieveNextInstance()) {
                    // read_next_instance_w_condition
                    rc = (Integer) this.readNextInstanceCondition.invoke(
                            this.old, pa.getDataSeqHolder(),
                            pa.getInfoSeqHolder(), selector.getMaxSamples(),
                            handle.getValue(), oldCondition);
                } else {
                    // read_w_condition
                    rc = (Integer) this.readCondition.invoke(this.old,
                            pa.getDataSeqHolder(), pa.getInfoSeqHolder(),
                            selector.getMaxSamples(), oldCondition);
                }
            } else {
                // read_next_instance
                if (selector.retrieveNextInstance()) {
                    rc = (Integer) this.readNextInstance.invoke(this.old,
                            pa.getDataSeqHolder(), pa.getInfoSeqHolder(),
                            selector.getMaxSamples(), handle.getValue(),
                            state.getOldSampleState(), state.getOldViewState(),
                            state.getOldInstanceState());
                } else {
                    // read
                    if (instance.isNil()) {
                        rc = (Integer) this.read.invoke(this.old,
                                pa.getDataSeqHolder(), pa.getInfoSeqHolder(),
                                selector.getMaxSamples(),
                                state.getOldSampleState(),
                                state.getOldViewState(),
                                state.getOldInstanceState());
                    }
                    // read_instance
                    else {
                        rc = (Integer) this.readInstance.invoke(this.old,
                                pa.getDataSeqHolder(), pa.getInfoSeqHolder(),
                                selector.getMaxSamples(), handle.getValue(),
                                state.getOldSampleState(),
                                state.getOldViewState(),
                                state.getOldInstanceState());
                    }
                }
            }
            Utilities.checkReturnCode(rc, this.environment,
                    "DataReader.read() failed.");
        } catch (IllegalAccessException e) {
            throw new DDSExceptionImpl(this.environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalArgumentExceptionImpl e) {
            throw e;
        } catch (IllegalArgumentException e) {
            throw new DDSExceptionImpl(this.environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (InvocationTargetException e) {
            throw new DDSExceptionImpl(this.environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (ClassCastException e) {
            throw new IllegalArgumentExceptionImpl(
                    this.environment,
                    "Reading with non-OpenSplice DataState, InstanceHandle or ReadCondition not supported");
        }
        pa.updateReferences();

        return pa.getSampleList();
    }

    public List<Sample<OUT_TYPE>> read(List<Sample<OUT_TYPE>> samples) {
        PreAllocator<OUT_TYPE> pa = this.reader.getPreAllocator(samples,
                this.sampleSeqHolderClz, this.sampleSeqHolderValueField);

        try {
            int rc = (Integer) this.read.invoke(this.old,
                    pa.getDataSeqHolder(), pa.getInfoSeqHolder(),
                    DDS.LENGTH_UNLIMITED.value, DDS.ANY_SAMPLE_STATE.value,
                    DDS.ANY_VIEW_STATE.value, DDS.ANY_INSTANCE_STATE.value);
            Utilities.checkReturnCode(rc, this.environment,
                    "DataReader.read() failed.");
        } catch (IllegalAccessException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (IllegalArgumentExceptionImpl e) {
            throw e;
        } catch (IllegalArgumentException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        } catch (InvocationTargetException e) {
            throw new DDSExceptionImpl(environment, "Internal error ("
                    + e.getMessage() + ").");
        }
        pa.updateReferences();

        return pa.getSampleList();
    }
}
