/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.hadoop.hive.ql.exec.vector;

import java.util.Arrays;

/**
 * The representation of a vectorized column of multi-valued objects, such
 * as lists and maps.
 *
 * Each object is composed of a range of elements in the underlying child
 * ColumnVector. The range for list i is
 * offsets[i]..offsets[i]+lengths[i]-1 inclusive.
 */
public abstract class MultiValuedColumnVector extends ColumnVector {

  public long[] offsets;
  public long[] lengths;
  // the number of children slots used
  public int childCount;

  /**
   * Constructor for MultiValuedColumnVector.
   *
   * @param len Vector length
   */
  public MultiValuedColumnVector(Type type, int len) {
    super(type, len);
    childCount = 0;
    offsets = new long[len];
    lengths = new long[len];
  }

  protected abstract void childFlatten(boolean useSelected, int[] selected,
                                       int size);

  @Override
  public void flatten(boolean selectedInUse, int[] sel, int size) {
    flattenPush();

    if (isRepeating) {
      if (noNulls || !isNull[0]) {
        if (selectedInUse) {
          for (int i = 0; i < size; ++i) {
            int row = sel[i];
            offsets[row] = offsets[0];
            lengths[row] = lengths[0];
            isNull[row] = false;
          }
        } else {
          Arrays.fill(offsets, 0, size, offsets[0]);
          Arrays.fill(lengths, 0, size, lengths[0]);
          Arrays.fill(isNull, 0, size, false);
        }
        // We optimize by assuming that a repeating list/map will run from
        // from 0 .. lengths[0] in the child vector.
        // Sanity check the assumption that we can start at 0.
        if (offsets[0] != 0) {
          throw new IllegalArgumentException("Repeating offset isn't 0, but " +
                                             offsets[0]);
        }
        childFlatten(false, null, (int) lengths[0]);
      } else {
        if (selectedInUse) {
          for(int i=0; i < size; ++i) {
            isNull[sel[i]] = true;
          }
        } else {
          Arrays.fill(isNull, 0, size, true);
        }
      }
      isRepeating = false;
      noNulls = false;
    } else {
      if (selectedInUse) {
        int childSize = 0;
        for(int i=0; i < size; ++i) {
          childSize += lengths[sel[i]];
        }
        int[] childSelection = new int[childSize];
        int idx = 0;
        for(int i=0; i < size; ++i) {
          int row = sel[i];
          for(int elem=0; elem < lengths[row]; ++elem) {
            childSelection[idx++] = (int) (offsets[row] + elem);
          }
        }
        childFlatten(true, childSelection, childSize);
      } else {
        childFlatten(false, null, childCount);
      }
      flattenNoNulls(selectedInUse, sel, size);
    }
  }

  @Override
  public void ensureSize(int size, boolean preserveData) {
    super.ensureSize(size, preserveData);
    if (size > offsets.length) {
      long[] oldOffsets = offsets;
      offsets = new long[size];
      long oldLengths[] = lengths;
      lengths = new long[size];
      if (preserveData) {
        if (isRepeating) {
          offsets[0] = oldOffsets[0];
          lengths[0] = oldLengths[0];
        } else {
          System.arraycopy(oldOffsets, 0, offsets, 0 , oldOffsets.length);
          System.arraycopy(oldLengths, 0, lengths, 0, oldLengths.length);
        }
      }
    }
  }

  /**
   * Initializee the vector
   */
  @Override
  public void init() {
    super.init();
    childCount = 0;
  }

  /**
   * Reset the vector for the next batch.
   */
  @Override
  public void reset() {
    super.reset();
    childCount = 0;
  }

  @Override
  public void shallowCopyTo(ColumnVector otherCv) {
    throw new UnsupportedOperationException(); // Implement in future, if needed.
  }
}
