/*
 * Copyright (c) 2022 MiLaboratories Inc.
 *
 * 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 com.milaboratory.util.sorting;

import com.milaboratory.primitivio.PrimitivI;
import com.milaboratory.primitivio.PrimitivO;
import com.milaboratory.primitivio.Serializer;

import java.util.Objects;
import java.util.function.Function;

final class WithIndex<T> {
    public final int index;
    public final T obj;
    private static final Function<WithIndex<?>, ?> WI_EXTRACTOR = withIndex -> withIndex.obj;

    public WithIndex(int index, T obj) {
        this.index = index;
        this.obj = obj;
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    public static <T> Function<WithIndex<T>, T> withIndexUnWrapper() {
        return (Function) WI_EXTRACTOR;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        WithIndex<?> withIndex = (WithIndex<?>) o;
        return index == withIndex.index &&
                obj.equals(withIndex.obj);
    }

    @Override
    public int hashCode() {
        return Objects.hash(index, obj);
    }

    @Override
    public String toString() {
        return "WithIndex{" +
                "index=" + index +
                ", obj=" + obj +
                '}';
    }

    static final class WISerializer<T> implements Serializer<WithIndex<T>> {
        private final Class<T> objClass;

        public WISerializer(Class<T> objClass) {
            this.objClass = objClass;
        }

        @Override
        public void write(PrimitivO output, WithIndex<T> obj) {
            output.writeInt(obj.index);
            output.writeObject(obj.obj, objClass);
        }

        @Override
        public WithIndex<T> read(PrimitivI input) {
            int index = input.readInt();
            T obj = input.readObject(objClass);
            return new WithIndex<>(index, obj);
        }

        @Override
        public boolean isReference() {
            return false;
        }

        @Override
        public boolean handlesReference() {
            return false;
        }
    }
}
