/*
 * 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.facebook.presto.operator;

import com.facebook.presto.spi.Page;
import com.facebook.presto.spi.block.Block;
import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.longs.LongArrayList;

import java.util.List;
import java.util.Optional;

import static com.facebook.presto.operator.SyntheticAddress.decodePosition;
import static com.facebook.presto.operator.SyntheticAddress.decodeSliceIndex;
import static java.util.Objects.requireNonNull;

public class StandardJoinFilterFunction
        implements JoinFilterFunction
{
    private static final Block[] EMPTY_BLOCK_ARRAY = new Block[0];

    private final InternalJoinFilterFunction filterFunction;
    private final LongArrayList addresses;
    private final List<Block[]> pages;
    private final Optional<Integer> sortChannel;

    public StandardJoinFilterFunction(InternalJoinFilterFunction filterFunction, LongArrayList addresses, List<List<Block>> channels, Optional<Integer> sortChannel)
    {
        this.filterFunction = requireNonNull(filterFunction, "filterFunction can not be null");
        this.addresses = requireNonNull(addresses, "addresses is null");
        this.sortChannel = requireNonNull(sortChannel, "sortChannel is null");

        requireNonNull(channels, "channels can not be null");
        ImmutableList.Builder<Block[]> pagesBuilder = ImmutableList.builder();
        if (!channels.isEmpty()) {
            int pagesCount = channels.get(0).size();
            for (int pageIndex = 0; pageIndex < pagesCount; ++pageIndex) {
                Block[] blocks = new Block[channels.size()];
                for (int channelIndex = 0; channelIndex < channels.size(); ++channelIndex) {
                    blocks[channelIndex] = channels.get(channelIndex).get(pageIndex);
                }
                pagesBuilder.add(blocks);
            }
        }
        this.pages = pagesBuilder.build();
    }

    @Override
    public boolean filter(int leftAddress, int rightPosition, Page rightPage)
    {
        long pageAddress = addresses.getLong(leftAddress);
        int blockIndex = decodeSliceIndex(pageAddress);
        int blockPosition = decodePosition(pageAddress);

        return filterFunction.filter(blockPosition, getLeftBlocks(blockIndex), rightPosition, rightPage.getBlocks());
    }

    @Override
    public Optional<Integer> getSortChannel()
    {
        return sortChannel;
    }

    private Block[] getLeftBlocks(int leftBlockIndex)
    {
        if (pages.isEmpty()) {
            return EMPTY_BLOCK_ARRAY;
        }
        else {
            return pages.get(leftBlockIndex);
        }
    }
}
