/*
 *                         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.core.status;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.omg.dds.core.policy.Deadline;
import org.omg.dds.core.policy.DestinationOrder;
import org.omg.dds.core.policy.Durability;
import org.omg.dds.core.policy.EntityFactory;
import org.omg.dds.core.policy.GroupData;
import org.omg.dds.core.policy.History;
import org.omg.dds.core.policy.LatencyBudget;
import org.omg.dds.core.policy.Lifespan;
import org.omg.dds.core.policy.Liveliness;
import org.omg.dds.core.policy.Ownership;
import org.omg.dds.core.policy.Partition;
import org.omg.dds.core.policy.Presentation;
import org.omg.dds.core.policy.QosPolicy;
import org.omg.dds.core.policy.QosPolicyCount;
import org.omg.dds.core.policy.ReaderDataLifecycle;
import org.omg.dds.core.policy.Reliability;
import org.omg.dds.core.policy.ResourceLimits;
import org.omg.dds.core.policy.TimeBasedFilter;
import org.omg.dds.core.policy.TopicData;
import org.omg.dds.core.policy.TransportPriority;
import org.omg.dds.core.policy.UserData;
import org.omg.dds.core.policy.WriterDataLifecycle;
import org.omg.dds.core.status.DataAvailableStatus;
import org.omg.dds.core.status.DataOnReadersStatus;
import org.omg.dds.core.status.InconsistentTopicStatus;
import org.omg.dds.core.status.LivelinessChangedStatus;
import org.omg.dds.core.status.LivelinessLostStatus;
import org.omg.dds.core.status.OfferedDeadlineMissedStatus;
import org.omg.dds.core.status.OfferedIncompatibleQosStatus;
import org.omg.dds.core.status.PublicationMatchedStatus;
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.SampleRejectedStatus.Kind;
import org.omg.dds.core.status.Status;
import org.omg.dds.core.status.SubscriptionMatchedStatus;
import org.opensplice.dds.core.IllegalArgumentExceptionImpl;
import org.opensplice.dds.core.InstanceHandleImpl;
import org.opensplice.dds.core.OsplServiceEnvironment;
import org.opensplice.dds.core.Utilities;
import org.opensplice.dds.core.policy.PolicyConverter;
import org.opensplice.dds.core.policy.QosPolicyCountImpl;
import org.opensplice.dds.core.policy.Scheduling;

import DDS.SampleRejectedStatusKind;

public class StatusConverter {
    private static boolean stateTest(int state, int mask) {
        return (((state) & (mask)) == mask) ? true : false;
    }

    public static Set<Class<? extends Status>> convertMask(
            OsplServiceEnvironment environment, int state) {
        HashSet<Class<? extends Status>> statuses = new HashSet<Class<? extends Status>>();

        if (state == DDS.STATUS_MASK_ANY_V1_2.value) {
            statuses.add(AllDataDisposedStatus.class);
            statuses.add(DataAvailableStatus.class);
            statuses.add(DataOnReadersStatus.class);
            statuses.add(InconsistentTopicStatus.class);
            statuses.add(LivelinessChangedStatus.class);
            statuses.add(LivelinessLostStatus.class);
            statuses.add(OfferedDeadlineMissedStatus.class);
            statuses.add(OfferedIncompatibleQosStatus.class);
            statuses.add(PublicationMatchedStatus.class);
            statuses.add(RequestedDeadlineMissedStatus.class);
            statuses.add(RequestedIncompatibleQosStatus.class);
            statuses.add(SampleLostStatus.class);
            statuses.add(SampleRejectedStatus.class);
            statuses.add(SubscriptionMatchedStatus.class);
        } else {
            if (stateTest(state, DDS.ALL_DATA_DISPOSED_TOPIC_STATUS.value)) {
                statuses.add(AllDataDisposedStatus.class);
            }
            if (stateTest(state, DDS.DATA_AVAILABLE_STATUS.value)) {
                statuses.add(DataAvailableStatus.class);
            }
            if (stateTest(state, DDS.DATA_ON_READERS_STATUS.value)) {
                statuses.add(DataOnReadersStatus.class);
            }
            if (stateTest(state, DDS.INCONSISTENT_TOPIC_STATUS.value)) {
                statuses.add(InconsistentTopicStatus.class);
            }
            if (stateTest(state, DDS.LIVELINESS_CHANGED_STATUS.value)) {
                statuses.add(LivelinessChangedStatus.class);
            }
            if (stateTest(state, DDS.LIVELINESS_LOST_STATUS.value)) {
                statuses.add(LivelinessLostStatus.class);
            }
            if (stateTest(state, DDS.OFFERED_DEADLINE_MISSED_STATUS.value)) {
                statuses.add(OfferedDeadlineMissedStatus.class);
            }
            if (stateTest(state, DDS.OFFERED_INCOMPATIBLE_QOS_STATUS.value)) {
                statuses.add(OfferedIncompatibleQosStatus.class);
            }
            if (stateTest(state, DDS.PUBLICATION_MATCHED_STATUS.value)) {
                statuses.add(PublicationMatchedStatus.class);
            }
            if (stateTest(state, DDS.REQUESTED_DEADLINE_MISSED_STATUS.value)) {
                statuses.add(RequestedDeadlineMissedStatus.class);
            }
            if (stateTest(state, DDS.REQUESTED_INCOMPATIBLE_QOS_STATUS.value)) {
                statuses.add(RequestedIncompatibleQosStatus.class);
            }
            if (stateTest(state, DDS.SAMPLE_LOST_STATUS.value)) {
                statuses.add(SampleLostStatus.class);
            }
            if (stateTest(state, DDS.SAMPLE_REJECTED_STATUS.value)) {
                statuses.add(SampleRejectedStatus.class);
            }
            if (stateTest(state, DDS.SUBSCRIPTION_MATCHED_STATUS.value)) {
                statuses.add(SubscriptionMatchedStatus.class);
            }
        }
        return statuses;
    }

    public static int convertMask(OsplServiceEnvironment env,
            Class<? extends Status>... statuses) {
        return StatusConverter.convertMask(env, Arrays.asList(statuses));
    }

    public static int getAnyMask() {
        return DDS.STATUS_MASK_ANY_V1_2.value;
    }

    public static int convertMask(OsplServiceEnvironment env,
            Collection<Class<? extends Status>> statuses) {
        int mask;

        if (statuses == null) {
            return DDS.STATUS_MASK_ANY_V1_2.value;
        } else if (statuses.size() == 0) {
            return DDS.STATUS_MASK_NONE.value;
        } else if (statuses.size() == 1 && statuses.iterator().next() == null) {
            return DDS.STATUS_MASK_ANY_V1_2.value;
        }
        mask = DDS.STATUS_MASK_NONE.value;

        for (Class<? extends Status> clz : statuses) {
            if (clz == null) {
                throw new IllegalArgumentExceptionImpl(env,
                        "Passed illegal <null> status.");
            } else if (clz.equals(DataAvailableStatus.class)) {
                mask |= DDS.DATA_AVAILABLE_STATUS.value;
            } else if (clz.equals(InconsistentTopicStatus.class)) {
                mask |= DDS.INCONSISTENT_TOPIC_STATUS.value;
            } else if (clz.equals(OfferedDeadlineMissedStatus.class)) {
                mask |= DDS.OFFERED_DEADLINE_MISSED_STATUS.value;
            } else if (clz.equals(RequestedDeadlineMissedStatus.class)) {
                mask |= DDS.REQUESTED_DEADLINE_MISSED_STATUS.value;
            } else if (clz.equals(OfferedIncompatibleQosStatus.class)) {
                mask |= DDS.OFFERED_INCOMPATIBLE_QOS_STATUS.value;
            } else if (clz.equals(RequestedIncompatibleQosStatus.class)) {
                mask |= DDS.REQUESTED_INCOMPATIBLE_QOS_STATUS.value;
            } else if (clz.equals(SampleLostStatus.class)) {
                mask |= DDS.SAMPLE_LOST_STATUS.value;
            } else if (clz.equals(SampleRejectedStatus.class)) {
                mask |= DDS.SAMPLE_REJECTED_STATUS.value;
            } else if (clz.equals(DataOnReadersStatus.class)) {
                mask |= DDS.DATA_ON_READERS_STATUS.value;
            } else if (clz.equals(LivelinessLostStatus.class)) {
                mask |= DDS.LIVELINESS_LOST_STATUS.value;
            } else if (clz.equals(LivelinessChangedStatus.class)) {
                mask |= DDS.LIVELINESS_CHANGED_STATUS.value;
            } else if (clz.equals(PublicationMatchedStatus.class)) {
                mask |= DDS.PUBLICATION_MATCHED_STATUS.value;
            } else if (clz.equals(SubscriptionMatchedStatus.class)) {
                mask |= DDS.SUBSCRIPTION_MATCHED_STATUS.value;
            } else if (clz.equals(AllDataDisposedStatus.class)) {
                mask |= DDS.ALL_DATA_DISPOSED_TOPIC_STATUS.value;
            } else if (clz.equals(Status.class)) {
                throw new IllegalArgumentExceptionImpl(env,
                        "Provided class does not extend from the org.omg.dds.core.status.Status class.");
            } else {
                throw new IllegalArgumentExceptionImpl(env,
                        "Found illegal Class<? extends Status>: "
                                + clz.getName());
            }
        }
        return mask;
    }

    public static QosPolicyCount[] convert(OsplServiceEnvironment env,
            DDS.QosPolicyCount[] old) {
        Class<? extends QosPolicy> policyClass;
        ArrayList<QosPolicyCountImpl> policies = new ArrayList<QosPolicyCountImpl>();

        for (int i = 0; i < old.length; i++) {
            policyClass = PolicyConverter.convert(env, old[i].policy_id);

            if (policyClass != null) {
                if (old[i].count != 0) {
                    policies.add(new QosPolicyCountImpl(env, policyClass,
                            old[i].count));
                }
            }
        }
        return policies.toArray(new QosPolicyCountImpl[policies.size()]);
    }

    public static int convert(OsplServiceEnvironment env,
            Class<? extends QosPolicy> policy) {
        int id;

        if (policy.equals(Deadline.class)) {
            id = DDS.DEADLINE_QOS_POLICY_ID.value;
        } else if (policy.equals(DestinationOrder.class)) {
            id = DDS.DESTINATIONORDER_QOS_POLICY_ID.value;
        } else if (policy.equals(Durability.class)) {
            id = DDS.DURABILITYSERVICE_QOS_POLICY_ID.value;
        } else if (policy.equals(EntityFactory.class)) {
            id = DDS.ENTITYFACTORY_QOS_POLICY_ID.value;
        } else if (policy.equals(GroupData.class)) {
            id = DDS.GROUPDATA_QOS_POLICY_ID.value;
        } else if (policy.equals(History.class)) {
            id = DDS.HISTORY_QOS_POLICY_ID.value;
        } else if (policy.equals(LatencyBudget.class)) {
            id = DDS.LATENCYBUDGET_QOS_POLICY_ID.value;
        } else if (policy.equals(Lifespan.class)) {
            id = DDS.LIFESPAN_QOS_POLICY_ID.value;
        } else if (policy.equals(Liveliness.class)) {
            id = DDS.LIVELINESS_QOS_POLICY_ID.value;
        } else if (policy.equals(Ownership.class)) {
            id = DDS.OWNERSHIP_QOS_POLICY_ID.value;
        } else if (policy.equals(Partition.class)) {
            id = DDS.PARTITION_QOS_POLICY_ID.value;
        } else if (policy.equals(Presentation.class)) {
            id = DDS.PRESENTATION_QOS_POLICY_ID.value;
        } else if (policy.equals(ReaderDataLifecycle.class)) {
            id = DDS.READERDATALIFECYCLE_QOS_POLICY_ID.value;
        } else if (policy.equals(Reliability.class)) {
            id = DDS.RELIABILITY_QOS_POLICY_ID.value;
        } else if (policy.equals(ResourceLimits.class)) {
            id = DDS.RESOURCELIMITS_QOS_POLICY_ID.value;
        } else if (policy.equals(Scheduling.class)) {
            id = DDS.SCHEDULING_QOS_POLICY_ID.value;
        } else if (policy.equals(TimeBasedFilter.class)) {
            id = DDS.TIMEBASEDFILTER_QOS_POLICY_ID.value;
        } else if (policy.equals(TopicData.class)) {
            id = DDS.TOPICDATA_QOS_POLICY_ID.value;
        } else if (policy.equals(TransportPriority.class)) {
            id = DDS.TRANSPORTPRIORITY_QOS_POLICY_ID.value;
        } else if (policy.equals(UserData.class)) {
            id = DDS.USERDATA_QOS_POLICY_ID.value;
        } else if (policy.equals(WriterDataLifecycle.class)) {
            id = DDS.WRITERDATALIFECYCLE_QOS_POLICY_ID.value;
        } else {
            throw new IllegalArgumentExceptionImpl(env,
                    "Found illegal QoSPolicy: " + policy.getName());
        }
        return id;
    }

    public static DDS.QosPolicyCount[] convert(OsplServiceEnvironment env,
            Set<QosPolicyCount> count) {
        DDS.QosPolicyCount[] old = new DDS.QosPolicyCount[count.size()];
        Iterator<QosPolicyCount> iter = count.iterator();
        QosPolicyCount current;

        for (int i = 0; i < count.size(); i++) {
            current = iter.next();
            old[i] = new DDS.QosPolicyCount(convert(env,
                    current.getPolicyClass()), current.getCount());
        }
        return old;
    }

    public static InconsistentTopicStatus convert(OsplServiceEnvironment env,
            DDS.InconsistentTopicStatus old) {
        return new InconsistentTopicStatusImpl(env, old.total_count,
                old.total_count_change);
    }

    public static DDS.InconsistentTopicStatus convert(
            OsplServiceEnvironment env, InconsistentTopicStatus status) {
        return new DDS.InconsistentTopicStatus(status.getTotalCount(),
                status.getTotalCountChange());
    }

    public static AllDataDisposedStatus convert(OsplServiceEnvironment env,
            DDS.AllDataDisposedTopicStatus old) {
        return new AllDataDisposedStatusImpl(env, old.total_count,
                old.total_count_change);
    }

    public static DDS.AllDataDisposedTopicStatus convert(
            OsplServiceEnvironment env, AllDataDisposedStatus status) {
        return new DDS.AllDataDisposedTopicStatus(status.getTotalCount(),
                status.getTotalCountChange());
    }

    public static LivelinessChangedStatus convert(OsplServiceEnvironment env,
            DDS.LivelinessChangedStatus old) {
        return new LivelinessChangedStatusImpl(env, old.alive_count,
                old.alive_count_change, old.not_alive_count,
                old.not_alive_count_change, new InstanceHandleImpl(env,
                        old.last_publication_handle));
    }

    public static DDS.LivelinessChangedStatus convert(
            OsplServiceEnvironment env, LivelinessChangedStatus status) {
        return new DDS.LivelinessChangedStatus(status.getAliveCount(),
                status.getAliveCountChange(), status.getNotAliveCount(),
                status.getNotAliveCountChange(), Utilities.convert(env,
                        status.getLastPublicationHandle()));
    }

    public static LivelinessLostStatus convert(OsplServiceEnvironment env,
            DDS.LivelinessLostStatus old) {
        return new LivelinessLostStatusImpl(env, old.total_count,
                old.total_count_change);
    }

    public static DDS.LivelinessLostStatus convert(OsplServiceEnvironment env,
            LivelinessLostStatus status) {
        return new DDS.LivelinessLostStatus(status.getTotalCount(),
                status.getTotalCountChange());
    }

    public static OfferedDeadlineMissedStatus convert(
            OsplServiceEnvironment env, DDS.OfferedDeadlineMissedStatus old) {
        return new OfferedDeadlineMissedStatusImpl(env, old.total_count,
                old.total_count_change, Utilities.convert(env,
                        old.last_instance_handle));
    }

    public static DDS.OfferedDeadlineMissedStatus convert(
            OsplServiceEnvironment env, OfferedDeadlineMissedStatus status) {
        return new DDS.OfferedDeadlineMissedStatus(status.getTotalCount(),
                status.getTotalCountChange(), Utilities.convert(env,
                        status.getLastInstanceHandle()));
    }

    public static OfferedIncompatibleQosStatus convert(
            OsplServiceEnvironment env, DDS.OfferedIncompatibleQosStatus old) {

        return new OfferedIncompatibleQosStatusImpl(env, old.total_count,
                old.total_count_change, PolicyConverter.convert(env,
                        old.last_policy_id), convert(env, old.policies));
    }

    public static DDS.OfferedIncompatibleQosStatus convert(
            OsplServiceEnvironment env, OfferedIncompatibleQosStatus status) {
        return new DDS.OfferedIncompatibleQosStatus(status.getTotalCount(),
                status.getTotalCountChange(), convert(env,
                        status.getLastPolicyClass()), convert(env,
                        status.getPolicies()));
    }

    public static PublicationMatchedStatus convert(OsplServiceEnvironment env,
            DDS.PublicationMatchedStatus old) {

        return new PublicationMatchedStatusImpl(env, old.total_count,
                old.total_count_change, old.current_count,
                old.current_count_change, Utilities.convert(env,
                        old.last_subscription_handle));
    }

    public static DDS.PublicationMatchedStatus convert(
            OsplServiceEnvironment env, PublicationMatchedStatus status) {
        return new DDS.PublicationMatchedStatus(status.getTotalCount(),
                status.getTotalCountChange(), status.getCurrentCount(),
                status.getCurrentCountChange(), Utilities.convert(env,
                        status.getLastSubscriptionHandle()));
    }

    public static RequestedDeadlineMissedStatus convert(
            OsplServiceEnvironment env, DDS.RequestedDeadlineMissedStatus old) {
        return new RequestedDeadlineMissedStatusImpl(env, old.total_count,
                old.total_count_change, Utilities.convert(env,
                        old.last_instance_handle));
    }

    public static DDS.RequestedDeadlineMissedStatus convert(
            OsplServiceEnvironment env, RequestedDeadlineMissedStatus status) {
        return new DDS.RequestedDeadlineMissedStatus(status.getTotalCount(),
                status.getTotalCountChange(), Utilities.convert(env,
                        status.getLastInstanceHandle()));
    }

    public static SampleRejectedStatus convert(OsplServiceEnvironment env,
            DDS.SampleRejectedStatus old) {
        SampleRejectedStatus.Kind kind;

        switch (old.last_reason.value()) {
        case SampleRejectedStatusKind._NOT_REJECTED:
            kind = Kind.NOT_REJECTED;
            break;
        case SampleRejectedStatusKind._REJECTED_BY_INSTANCES_LIMIT:
            kind = Kind.REJECTED_BY_INSTANCES_LIMIT;
            break;
        case SampleRejectedStatusKind._REJECTED_BY_SAMPLES_LIMIT:
            kind = Kind.REJECTED_BY_SAMPLES_LIMIT;
            break;
        case SampleRejectedStatusKind._REJECTED_BY_SAMPLES_PER_INSTANCE_LIMIT:
            kind = Kind.REJECTED_BY_SAMPLES_PER_INSTANCE_LIMIT;
            break;
        default:
            throw new IllegalArgumentExceptionImpl(env,
                    "Found illegal SampleRejectedStatus.Kind "
                            + old.last_reason.value());
        }
        return new SampleRejectedStatusImpl(env, old.total_count,
                old.total_count_change, kind, Utilities.convert(env,
                        old.last_instance_handle));
    }

    public static DDS.SampleRejectedStatus convert(OsplServiceEnvironment env,
            SampleRejectedStatus status) {
        DDS.SampleRejectedStatusKind kind;

        switch (status.getLastReason()) {
        case NOT_REJECTED:
            kind = DDS.SampleRejectedStatusKind.NOT_REJECTED;
            break;
        case REJECTED_BY_INSTANCES_LIMIT:
            kind = DDS.SampleRejectedStatusKind.REJECTED_BY_INSTANCES_LIMIT;
            break;
        case REJECTED_BY_SAMPLES_LIMIT:
            kind = DDS.SampleRejectedStatusKind.REJECTED_BY_SAMPLES_LIMIT;
            break;
        case REJECTED_BY_SAMPLES_PER_INSTANCE_LIMIT:
            kind = DDS.SampleRejectedStatusKind.REJECTED_BY_SAMPLES_PER_INSTANCE_LIMIT;
            break;
        default:
            throw new IllegalArgumentExceptionImpl(env,
                    "Found illegal SampleRejectedStatus.Kind");
        }
        return new DDS.SampleRejectedStatus(status.getTotalCount(),
                status.getTotalCountChange(), kind, Utilities.convert(env,
                        status.getLastInstanceHandle()));
    }

    public static RequestedIncompatibleQosStatus convert(
            OsplServiceEnvironment env, DDS.RequestedIncompatibleQosStatus old) {

        return new RequestedIncompatibleQosStatusImpl(env, old.total_count,
                old.total_count_change, PolicyConverter.convert(env,
                        old.last_policy_id), convert(env, old.policies));
    }

    public static DDS.RequestedIncompatibleQosStatus convert(
            OsplServiceEnvironment env, RequestedIncompatibleQosStatus status) {
        return new DDS.RequestedIncompatibleQosStatus(status.getTotalCount(),
                status.getTotalCountChange(), convert(env,
                        status.getLastPolicyClass()), convert(env,
                        status.getPolicies()));
    }

    public static SubscriptionMatchedStatus convert(OsplServiceEnvironment env,
            DDS.SubscriptionMatchedStatus old) {

        return new SubscriptionMatchedStatusImpl(env, old.total_count,
                old.total_count_change, old.current_count,
                old.current_count_change, Utilities.convert(env,
                        old.last_publication_handle));
    }

    public static DDS.SubscriptionMatchedStatus convert(
            OsplServiceEnvironment env, SubscriptionMatchedStatus status) {
        return new DDS.SubscriptionMatchedStatus(status.getTotalCount(),
                status.getTotalCountChange(), status.getCurrentCount(),
                status.getCurrentCountChange(), Utilities.convert(env,
                        status.getLastPublicationHandle()));
    }

    public static SampleLostStatus convert(OsplServiceEnvironment env,
            DDS.SampleLostStatus old) {
        return new SampleLostStatusImpl(env, old.total_count,
                old.total_count_change);
    }

    public static DDS.SampleLostStatus convert(SampleLostStatus status) {
        return new DDS.SampleLostStatus(status.getTotalCount(),
                status.getTotalCountChange());
    }
}
