/*
 * 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.arrow.vector.types;

import static org.apache.arrow.vector.types.FloatingPointPrecision.DOUBLE;
import static org.apache.arrow.vector.types.FloatingPointPrecision.HALF;
import static org.apache.arrow.vector.types.FloatingPointPrecision.SINGLE;
import static org.apache.arrow.vector.types.UnionMode.Dense;
import static org.apache.arrow.vector.types.UnionMode.Sparse;

import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.vector.BigIntVector;
import org.apache.arrow.vector.BitVector;
import org.apache.arrow.vector.DateDayVector;
import org.apache.arrow.vector.DateMilliVector;
import org.apache.arrow.vector.Decimal256Vector;
import org.apache.arrow.vector.DecimalVector;
import org.apache.arrow.vector.DurationVector;
import org.apache.arrow.vector.ExtensionTypeVector;
import org.apache.arrow.vector.FieldVector;
import org.apache.arrow.vector.FixedSizeBinaryVector;
import org.apache.arrow.vector.Float2Vector;
import org.apache.arrow.vector.Float4Vector;
import org.apache.arrow.vector.Float8Vector;
import org.apache.arrow.vector.IntVector;
import org.apache.arrow.vector.IntervalDayVector;
import org.apache.arrow.vector.IntervalMonthDayNanoVector;
import org.apache.arrow.vector.IntervalYearVector;
import org.apache.arrow.vector.LargeVarBinaryVector;
import org.apache.arrow.vector.LargeVarCharVector;
import org.apache.arrow.vector.NullVector;
import org.apache.arrow.vector.SmallIntVector;
import org.apache.arrow.vector.TimeMicroVector;
import org.apache.arrow.vector.TimeMilliVector;
import org.apache.arrow.vector.TimeNanoVector;
import org.apache.arrow.vector.TimeSecVector;
import org.apache.arrow.vector.TimeStampMicroTZVector;
import org.apache.arrow.vector.TimeStampMicroVector;
import org.apache.arrow.vector.TimeStampMilliTZVector;
import org.apache.arrow.vector.TimeStampMilliVector;
import org.apache.arrow.vector.TimeStampNanoTZVector;
import org.apache.arrow.vector.TimeStampNanoVector;
import org.apache.arrow.vector.TimeStampSecTZVector;
import org.apache.arrow.vector.TimeStampSecVector;
import org.apache.arrow.vector.TinyIntVector;
import org.apache.arrow.vector.UInt1Vector;
import org.apache.arrow.vector.UInt2Vector;
import org.apache.arrow.vector.UInt4Vector;
import org.apache.arrow.vector.UInt8Vector;
import org.apache.arrow.vector.ValueVector;
import org.apache.arrow.vector.VarBinaryVector;
import org.apache.arrow.vector.VarCharVector;
import org.apache.arrow.vector.ViewVarBinaryVector;
import org.apache.arrow.vector.ViewVarCharVector;
import org.apache.arrow.vector.complex.DenseUnionVector;
import org.apache.arrow.vector.complex.FixedSizeListVector;
import org.apache.arrow.vector.complex.LargeListVector;
import org.apache.arrow.vector.complex.LargeListViewVector;
import org.apache.arrow.vector.complex.ListVector;
import org.apache.arrow.vector.complex.ListViewVector;
import org.apache.arrow.vector.complex.MapVector;
import org.apache.arrow.vector.complex.RunEndEncodedVector;
import org.apache.arrow.vector.complex.StructVector;
import org.apache.arrow.vector.complex.UnionVector;
import org.apache.arrow.vector.complex.impl.BigIntWriterImpl;
import org.apache.arrow.vector.complex.impl.BitWriterImpl;
import org.apache.arrow.vector.complex.impl.DateDayWriterImpl;
import org.apache.arrow.vector.complex.impl.DateMilliWriterImpl;
import org.apache.arrow.vector.complex.impl.Decimal256WriterImpl;
import org.apache.arrow.vector.complex.impl.DecimalWriterImpl;
import org.apache.arrow.vector.complex.impl.DenseUnionWriter;
import org.apache.arrow.vector.complex.impl.DurationWriterImpl;
import org.apache.arrow.vector.complex.impl.FixedSizeBinaryWriterImpl;
import org.apache.arrow.vector.complex.impl.Float2WriterImpl;
import org.apache.arrow.vector.complex.impl.Float4WriterImpl;
import org.apache.arrow.vector.complex.impl.Float8WriterImpl;
import org.apache.arrow.vector.complex.impl.IntWriterImpl;
import org.apache.arrow.vector.complex.impl.IntervalDayWriterImpl;
import org.apache.arrow.vector.complex.impl.IntervalMonthDayNanoWriterImpl;
import org.apache.arrow.vector.complex.impl.IntervalYearWriterImpl;
import org.apache.arrow.vector.complex.impl.LargeVarBinaryWriterImpl;
import org.apache.arrow.vector.complex.impl.LargeVarCharWriterImpl;
import org.apache.arrow.vector.complex.impl.NullableStructWriter;
import org.apache.arrow.vector.complex.impl.SmallIntWriterImpl;
import org.apache.arrow.vector.complex.impl.TimeMicroWriterImpl;
import org.apache.arrow.vector.complex.impl.TimeMilliWriterImpl;
import org.apache.arrow.vector.complex.impl.TimeNanoWriterImpl;
import org.apache.arrow.vector.complex.impl.TimeSecWriterImpl;
import org.apache.arrow.vector.complex.impl.TimeStampMicroTZWriterImpl;
import org.apache.arrow.vector.complex.impl.TimeStampMicroWriterImpl;
import org.apache.arrow.vector.complex.impl.TimeStampMilliTZWriterImpl;
import org.apache.arrow.vector.complex.impl.TimeStampMilliWriterImpl;
import org.apache.arrow.vector.complex.impl.TimeStampNanoTZWriterImpl;
import org.apache.arrow.vector.complex.impl.TimeStampNanoWriterImpl;
import org.apache.arrow.vector.complex.impl.TimeStampSecTZWriterImpl;
import org.apache.arrow.vector.complex.impl.TimeStampSecWriterImpl;
import org.apache.arrow.vector.complex.impl.TinyIntWriterImpl;
import org.apache.arrow.vector.complex.impl.UInt1WriterImpl;
import org.apache.arrow.vector.complex.impl.UInt2WriterImpl;
import org.apache.arrow.vector.complex.impl.UInt4WriterImpl;
import org.apache.arrow.vector.complex.impl.UInt8WriterImpl;
import org.apache.arrow.vector.complex.impl.UnionLargeListViewWriter;
import org.apache.arrow.vector.complex.impl.UnionLargeListWriter;
import org.apache.arrow.vector.complex.impl.UnionListWriter;
import org.apache.arrow.vector.complex.impl.UnionWriter;
import org.apache.arrow.vector.complex.impl.VarBinaryWriterImpl;
import org.apache.arrow.vector.complex.impl.VarCharWriterImpl;
import org.apache.arrow.vector.complex.impl.ViewVarBinaryWriterImpl;
import org.apache.arrow.vector.complex.impl.ViewVarCharWriterImpl;
import org.apache.arrow.vector.complex.writer.FieldWriter;
import org.apache.arrow.vector.types.pojo.ArrowType;
import org.apache.arrow.vector.types.pojo.ArrowType.ArrowTypeVisitor;
import org.apache.arrow.vector.types.pojo.ArrowType.Binary;
import org.apache.arrow.vector.types.pojo.ArrowType.BinaryView;
import org.apache.arrow.vector.types.pojo.ArrowType.Bool;
import org.apache.arrow.vector.types.pojo.ArrowType.Date;
import org.apache.arrow.vector.types.pojo.ArrowType.Decimal;
import org.apache.arrow.vector.types.pojo.ArrowType.Duration;
import org.apache.arrow.vector.types.pojo.ArrowType.ExtensionType;
import org.apache.arrow.vector.types.pojo.ArrowType.FixedSizeBinary;
import org.apache.arrow.vector.types.pojo.ArrowType.FixedSizeList;
import org.apache.arrow.vector.types.pojo.ArrowType.FloatingPoint;
import org.apache.arrow.vector.types.pojo.ArrowType.Int;
import org.apache.arrow.vector.types.pojo.ArrowType.Interval;
import org.apache.arrow.vector.types.pojo.ArrowType.LargeBinary;
import org.apache.arrow.vector.types.pojo.ArrowType.LargeListView;
import org.apache.arrow.vector.types.pojo.ArrowType.LargeUtf8;
import org.apache.arrow.vector.types.pojo.ArrowType.List;
import org.apache.arrow.vector.types.pojo.ArrowType.ListView;
import org.apache.arrow.vector.types.pojo.ArrowType.Map;
import org.apache.arrow.vector.types.pojo.ArrowType.Null;
import org.apache.arrow.vector.types.pojo.ArrowType.RunEndEncoded;
import org.apache.arrow.vector.types.pojo.ArrowType.Struct;
import org.apache.arrow.vector.types.pojo.ArrowType.Time;
import org.apache.arrow.vector.types.pojo.ArrowType.Timestamp;
import org.apache.arrow.vector.types.pojo.ArrowType.Union;
import org.apache.arrow.vector.types.pojo.ArrowType.Utf8;
import org.apache.arrow.vector.types.pojo.ArrowType.Utf8View;
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.arrow.vector.types.pojo.FieldType;
import org.apache.arrow.vector.util.CallBack;

/** An enumeration of all logical types supported by this library. */
public class Types {

  /** The actual enumeration of types. */
  public enum MinorType {
    NULL(Null.INSTANCE) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new NullVector(field.getName());
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return null;
      }
    },
    STRUCT(Struct.INSTANCE) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new StructVector(
            field.getName(), allocator, field.getFieldType(), schemaChangeCallback);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new NullableStructWriter((StructVector) vector);
      }
    },
    TINYINT(new Int(8, true)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new TinyIntVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new TinyIntWriterImpl((TinyIntVector) vector);
      }
    },
    SMALLINT(new Int(16, true)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new SmallIntVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new SmallIntWriterImpl((SmallIntVector) vector);
      }
    },
    INT(new Int(32, true)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new IntVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new IntWriterImpl((IntVector) vector);
      }
    },
    BIGINT(new Int(64, true)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new BigIntVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new BigIntWriterImpl((BigIntVector) vector);
      }
    },
    DATEDAY(new Date(DateUnit.DAY)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new DateDayVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new DateDayWriterImpl((DateDayVector) vector);
      }
    },
    DATEMILLI(new Date(DateUnit.MILLISECOND)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new DateMilliVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new DateMilliWriterImpl((DateMilliVector) vector);
      }
    },
    TIMESEC(new Time(TimeUnit.SECOND, 32)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new TimeSecVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new TimeSecWriterImpl((TimeSecVector) vector);
      }
    },
    TIMEMILLI(new Time(TimeUnit.MILLISECOND, 32)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new TimeMilliVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new TimeMilliWriterImpl((TimeMilliVector) vector);
      }
    },
    TIMEMICRO(new Time(TimeUnit.MICROSECOND, 64)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new TimeMicroVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new TimeMicroWriterImpl((TimeMicroVector) vector);
      }
    },
    TIMENANO(new Time(TimeUnit.NANOSECOND, 64)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new TimeNanoVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new TimeNanoWriterImpl((TimeNanoVector) vector);
      }
    },
    // time in second from the Unix epoch, 00:00:00 on 1 January 1970, UTC.
    TIMESTAMPSEC(new Timestamp(org.apache.arrow.vector.types.TimeUnit.SECOND, null)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new TimeStampSecVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new TimeStampSecWriterImpl((TimeStampSecVector) vector);
      }
    },
    // time in millis from the Unix epoch, 00:00:00.000 on 1 January 1970, UTC.
    TIMESTAMPMILLI(new Timestamp(org.apache.arrow.vector.types.TimeUnit.MILLISECOND, null)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new TimeStampMilliVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new TimeStampMilliWriterImpl((TimeStampMilliVector) vector);
      }
    },
    // time in microsecond from the Unix epoch, 00:00:00.000000 on 1 January 1970, UTC.
    TIMESTAMPMICRO(new Timestamp(org.apache.arrow.vector.types.TimeUnit.MICROSECOND, null)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new TimeStampMicroVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new TimeStampMicroWriterImpl((TimeStampMicroVector) vector);
      }
    },
    // time in nanosecond from the Unix epoch, 00:00:00.000000000 on 1 January 1970, UTC.
    TIMESTAMPNANO(new Timestamp(org.apache.arrow.vector.types.TimeUnit.NANOSECOND, null)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new TimeStampNanoVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new TimeStampNanoWriterImpl((TimeStampNanoVector) vector);
      }
    },
    INTERVALDAY(new Interval(IntervalUnit.DAY_TIME)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new IntervalDayVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new IntervalDayWriterImpl((IntervalDayVector) vector);
      }
    },
    INTERVALMONTHDAYNANO(new Interval(IntervalUnit.MONTH_DAY_NANO)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new IntervalMonthDayNanoVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new IntervalMonthDayNanoWriterImpl((IntervalMonthDayNanoVector) vector);
      }
    },
    DURATION(null) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new DurationVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new DurationWriterImpl((DurationVector) vector);
      }
    },

    INTERVALYEAR(new Interval(IntervalUnit.YEAR_MONTH)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new IntervalYearVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new IntervalYearWriterImpl((IntervalYearVector) vector);
      }
    },
    FLOAT2(new FloatingPoint(HALF)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new Float2Vector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new Float2WriterImpl((Float2Vector) vector);
      }
    },
    //  4 byte ieee 754
    FLOAT4(new FloatingPoint(SINGLE)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new Float4Vector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new Float4WriterImpl((Float4Vector) vector);
      }
    },
    //  8 byte ieee 754
    FLOAT8(new FloatingPoint(DOUBLE)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new Float8Vector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new Float8WriterImpl((Float8Vector) vector);
      }
    },
    BIT(Bool.INSTANCE) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new BitVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new BitWriterImpl((BitVector) vector);
      }
    },
    VARCHAR(Utf8.INSTANCE) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new VarCharVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new VarCharWriterImpl((VarCharVector) vector);
      }
    },
    VIEWVARCHAR(Utf8View.INSTANCE) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new ViewVarCharVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new ViewVarCharWriterImpl((ViewVarCharVector) vector);
      }
    },
    LARGEVARCHAR(LargeUtf8.INSTANCE) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new LargeVarCharVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new LargeVarCharWriterImpl((LargeVarCharVector) vector);
      }
    },
    LARGEVARBINARY(LargeBinary.INSTANCE) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new LargeVarBinaryVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new LargeVarBinaryWriterImpl((LargeVarBinaryVector) vector);
      }
    },
    VARBINARY(Binary.INSTANCE) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new VarBinaryVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new VarBinaryWriterImpl((VarBinaryVector) vector);
      }
    },
    VIEWVARBINARY(BinaryView.INSTANCE) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new ViewVarBinaryVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new ViewVarBinaryWriterImpl((ViewVarBinaryVector) vector);
      }
    },
    DECIMAL(null) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new DecimalVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new DecimalWriterImpl((DecimalVector) vector);
      }
    },
    DECIMAL256(null) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new Decimal256Vector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new Decimal256WriterImpl((Decimal256Vector) vector);
      }
    },
    FIXEDSIZEBINARY(null) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new FixedSizeBinaryVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new FixedSizeBinaryWriterImpl((FixedSizeBinaryVector) vector);
      }
    },
    UINT1(new Int(8, false)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new UInt1Vector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new UInt1WriterImpl((UInt1Vector) vector);
      }
    },
    UINT2(new Int(16, false)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new UInt2Vector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new UInt2WriterImpl((UInt2Vector) vector);
      }
    },
    UINT4(new Int(32, false)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new UInt4Vector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new UInt4WriterImpl((UInt4Vector) vector);
      }
    },
    UINT8(new Int(64, false)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new UInt8Vector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new UInt8WriterImpl((UInt8Vector) vector);
      }
    },
    LIST(List.INSTANCE) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new ListVector(
            field.getName(), allocator, field.getFieldType(), schemaChangeCallback);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new UnionListWriter((ListVector) vector);
      }
    },
    LISTVIEW(ListView.INSTANCE) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new ListViewVector(
            field.getName(), allocator, field.getFieldType(), schemaChangeCallback);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new UnionListWriter((ListVector) vector);
      }
    },
    LARGELIST(ArrowType.LargeList.INSTANCE) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new LargeListVector(
            field.getName(), allocator, field.getFieldType(), schemaChangeCallback);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new UnionLargeListWriter((LargeListVector) vector);
      }
    },
    LARGELISTVIEW(ArrowType.LargeListView.INSTANCE) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new LargeListViewVector(
            field.getName(), allocator, field.getFieldType(), schemaChangeCallback);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new UnionLargeListViewWriter((LargeListViewVector) vector);
      }
    },
    FIXED_SIZE_LIST(null) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new FixedSizeListVector(
            field.getName(), allocator, field.getFieldType(), schemaChangeCallback);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        throw new UnsupportedOperationException(
            "FieldWriter not implemented for FixedSizeList " + "type");
      }
    },
    UNION(new Union(Sparse, null)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        if (field.getFieldType().getDictionary() != null) {
          throw new UnsupportedOperationException(
              "Dictionary encoding not supported for complex " + "types");
        }
        return new UnionVector(
            field.getName(), allocator, field.getFieldType(), schemaChangeCallback);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new UnionWriter((UnionVector) vector);
      }
    },
    DENSEUNION(new Union(Dense, null)) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        if (field.getFieldType().getDictionary() != null) {
          throw new UnsupportedOperationException(
              "Dictionary encoding not supported for complex " + "types");
        }
        return new DenseUnionVector(
            field.getName(), allocator, field.getFieldType(), schemaChangeCallback);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new DenseUnionWriter((DenseUnionVector) vector);
      }
    },
    MAP(null) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new MapVector(
            field.getName(), allocator, field.getFieldType(), schemaChangeCallback);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new UnionListWriter((MapVector) vector);
      }
    },
    TIMESTAMPSECTZ(null) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new TimeStampSecTZVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new TimeStampSecTZWriterImpl((TimeStampSecTZVector) vector);
      }
    },
    TIMESTAMPMILLITZ(null) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new TimeStampMilliTZVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new TimeStampMilliTZWriterImpl((TimeStampMilliTZVector) vector);
      }
    },
    TIMESTAMPMICROTZ(null) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new TimeStampMicroTZVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new TimeStampMicroTZWriterImpl((TimeStampMicroTZVector) vector);
      }
    },
    TIMESTAMPNANOTZ(null) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new TimeStampNanoTZVector(field, allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return new TimeStampNanoTZWriterImpl((TimeStampNanoTZVector) vector);
      }
    },
    EXTENSIONTYPE(null) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return ((ExtensionType) field.getFieldType().getType())
            .getNewVector(field.getName(), field.getFieldType(), allocator);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        return ((ExtensionTypeVector) vector)
            .getUnderlyingVector()
            .getMinorType()
            .getNewFieldWriter(vector);
      }
    },
    RUNENDENCODED(RunEndEncoded.INSTANCE) {
      @Override
      public FieldVector getNewVector(
          Field field, BufferAllocator allocator, CallBack schemaChangeCallback) {
        return new RunEndEncodedVector(field, allocator, schemaChangeCallback);
      }

      @Override
      public FieldWriter getNewFieldWriter(ValueVector vector) {
        throw new UnsupportedOperationException(
            "FieldWriter for run-end encoded vector is not implemented yet.");
      }
    },
    ;

    private final ArrowType type;

    MinorType(ArrowType type) {
      this.type = type;
    }

    /** Returns the {@link ArrowType} equivalent of this type. */
    public final ArrowType getType() {
      if (type == null) {
        throw new UnsupportedOperationException("Cannot get simple type for type " + name());
      }
      return type;
    }

    /** Constructs a new vector for the given type. */
    public final FieldVector getNewVector(
        String name,
        FieldType fieldType,
        BufferAllocator allocator,
        CallBack schemaChangeCallback) {
      return getNewVector(new Field(name, fieldType, null), allocator, schemaChangeCallback);
    }

    /** Constructs a new vector for the given type. */
    public abstract FieldVector getNewVector(
        Field field, BufferAllocator allocator, CallBack schemaChangeCallback);

    public abstract FieldWriter getNewFieldWriter(ValueVector vector);
  }

  /** Maps the ArrowType to the java implementations MinorType. */
  public static MinorType getMinorTypeForArrowType(ArrowType arrowType) {
    return arrowType.accept(
        new ArrowTypeVisitor<MinorType>() {
          @Override
          public MinorType visit(Null type) {
            return MinorType.NULL;
          }

          @Override
          public MinorType visit(Struct type) {
            return MinorType.STRUCT;
          }

          @Override
          public MinorType visit(List type) {
            return MinorType.LIST;
          }

          @Override
          public MinorType visit(FixedSizeList type) {
            return MinorType.FIXED_SIZE_LIST;
          }

          @Override
          public MinorType visit(Union type) {
            switch (type.getMode()) {
              case Sparse:
                return MinorType.UNION;
              case Dense:
                return MinorType.DENSEUNION;
              default:
                throw new IllegalArgumentException(
                    "only Dense or Sparse unions supported: " + type);
            }
          }

          @Override
          public MinorType visit(Map type) {
            return MinorType.MAP;
          }

          @Override
          public MinorType visit(ArrowType.LargeList type) {
            return MinorType.LARGELIST;
          }

          @Override
          public MinorType visit(Int type) {
            switch (type.getBitWidth()) {
              case 8:
                return type.getIsSigned() ? MinorType.TINYINT : MinorType.UINT1;
              case 16:
                return type.getIsSigned() ? MinorType.SMALLINT : MinorType.UINT2;
              case 32:
                return type.getIsSigned() ? MinorType.INT : MinorType.UINT4;
              case 64:
                return type.getIsSigned() ? MinorType.BIGINT : MinorType.UINT8;
              default:
                throw new IllegalArgumentException("only 8, 16, 32, 64 supported: " + type);
            }
          }

          @Override
          public MinorType visit(FloatingPoint type) {
            switch (type.getPrecision()) {
              case HALF:
                return MinorType.FLOAT2;
              case SINGLE:
                return MinorType.FLOAT4;
              case DOUBLE:
                return MinorType.FLOAT8;
              default:
                throw new IllegalArgumentException("unknown precision: " + type);
            }
          }

          @Override
          public MinorType visit(Utf8 type) {
            return MinorType.VARCHAR;
          }

          @Override
          public MinorType visit(Utf8View type) {
            return MinorType.VIEWVARCHAR;
          }

          @Override
          public Types.MinorType visit(LargeUtf8 type) {
            return MinorType.LARGEVARCHAR;
          }

          @Override
          public MinorType visit(Binary type) {
            return MinorType.VARBINARY;
          }

          @Override
          public MinorType visit(BinaryView type) {
            return MinorType.VIEWVARBINARY;
          }

          @Override
          public MinorType visit(LargeBinary type) {
            return MinorType.LARGEVARBINARY;
          }

          @Override
          public MinorType visit(Bool type) {
            return MinorType.BIT;
          }

          @Override
          public MinorType visit(Decimal type) {
            if (type.getBitWidth() == 256) {
              return MinorType.DECIMAL256;
            }
            return MinorType.DECIMAL;
          }

          @Override
          public MinorType visit(FixedSizeBinary type) {
            return MinorType.FIXEDSIZEBINARY;
          }

          @Override
          public MinorType visit(Date type) {
            switch (type.getUnit()) {
              case DAY:
                return MinorType.DATEDAY;
              case MILLISECOND:
                return MinorType.DATEMILLI;
              default:
                throw new IllegalArgumentException("unknown unit: " + type);
            }
          }

          @Override
          public MinorType visit(Time type) {
            switch (type.getUnit()) {
              case SECOND:
                return MinorType.TIMESEC;
              case MILLISECOND:
                return MinorType.TIMEMILLI;
              case MICROSECOND:
                return MinorType.TIMEMICRO;
              case NANOSECOND:
                return MinorType.TIMENANO;
              default:
                throw new IllegalArgumentException("unknown unit: " + type);
            }
          }

          @Override
          public MinorType visit(Timestamp type) {
            String tz = type.getTimezone();
            switch (type.getUnit()) {
              case SECOND:
                return tz == null ? MinorType.TIMESTAMPSEC : MinorType.TIMESTAMPSECTZ;
              case MILLISECOND:
                return tz == null ? MinorType.TIMESTAMPMILLI : MinorType.TIMESTAMPMILLITZ;
              case MICROSECOND:
                return tz == null ? MinorType.TIMESTAMPMICRO : MinorType.TIMESTAMPMICROTZ;
              case NANOSECOND:
                return tz == null ? MinorType.TIMESTAMPNANO : MinorType.TIMESTAMPNANOTZ;
              default:
                throw new IllegalArgumentException("unknown unit: " + type);
            }
          }

          @Override
          public MinorType visit(Interval type) {
            switch (type.getUnit()) {
              case DAY_TIME:
                return MinorType.INTERVALDAY;
              case YEAR_MONTH:
                return MinorType.INTERVALYEAR;
              case MONTH_DAY_NANO:
                return MinorType.INTERVALMONTHDAYNANO;
              default:
                throw new IllegalArgumentException("unknown unit: " + type);
            }
          }

          @Override
          public MinorType visit(Duration type) {
            return MinorType.DURATION;
          }

          @Override
          public MinorType visit(ListView type) {
            return MinorType.LISTVIEW;
          }

          @Override
          public MinorType visit(LargeListView type) {
            return MinorType.LARGELISTVIEW;
          }

          @Override
          public MinorType visit(ExtensionType type) {
            return MinorType.EXTENSIONTYPE;
          }

          @Override
          public MinorType visit(RunEndEncoded type) {
            return MinorType.RUNENDENCODED;
          }
        });
  }
}
