/**
* 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.
 */
#ifndef IOTDB_COLUMN_H
#define IOTDB_COLUMN_H

#include <vector>
#include <memory>
#include <map>
#include <stdexcept>

#include "Common.h"
#include "ColumnDecoder.h"

enum class ColumnEncoding : uint8_t { ByteArray, Int32Array, Int64Array, BinaryArray, Rle };

class Binary {
public:
  explicit Binary(std::vector<uint8_t> data) : data_(std::move(data)) {}

  const std::vector<uint8_t>& getData() const {
    return data_;
  }

  std::string getStringValue() const {
    return {data_.begin(), data_.end()};
  }

private:
  std::vector<uint8_t> data_;
};

const std::map<ColumnEncoding, std::shared_ptr<ColumnDecoder>> kEncodingToDecoder = {
    {ColumnEncoding::Int32Array, std::make_shared<Int32ArrayColumnDecoder>()},
    {ColumnEncoding::Int64Array, std::make_shared<Int64ArrayColumnDecoder>()},
    {ColumnEncoding::ByteArray, std::make_shared<ByteArrayColumnDecoder>()},
    {ColumnEncoding::BinaryArray, std::make_shared<BinaryArrayColumnDecoder>()},
    {ColumnEncoding::Rle, std::make_shared<RunLengthColumnDecoder>()}};

const std::map<uint8_t, ColumnEncoding> kByteToEncoding = {{0, ColumnEncoding::ByteArray},
                                                           {1, ColumnEncoding::Int32Array},
                                                           {2, ColumnEncoding::Int64Array},
                                                           {3, ColumnEncoding::BinaryArray},
                                                           {4, ColumnEncoding::Rle}};

inline std::shared_ptr<ColumnDecoder> getColumnDecoder(ColumnEncoding encoding) {
  auto it = kEncodingToDecoder.find(encoding);
  if (it == kEncodingToDecoder.end()) {
    throw IoTDBException("Unsupported column encoding");
  }
  return it->second;
}

inline ColumnEncoding getColumnEncodingByByte(uint8_t b) {
  auto it = kByteToEncoding.find(b);
  if (it == kByteToEncoding.end()) {
    throw IoTDBException("Invalid encoding value: " + std::to_string(b));
  }
  return it->second;
}

class Column {
public:
  virtual ~Column() = default;

  virtual TSDataType::TSDataType getDataType() const = 0;
  virtual ColumnEncoding getEncoding() const = 0;

  virtual bool getBoolean(int32_t position) const {
    throw IoTDBException("Unsupported operation: getBoolean");
  }

  virtual int32_t getInt(int32_t position) const {
    throw IoTDBException("Unsupported operation: getInt");
  }

  virtual int64_t getLong(int32_t position) const {
    throw IoTDBException("Unsupported operation: getLong");
  }

  virtual float getFloat(int32_t position) const {
    throw IoTDBException("Unsupported operation: getFloat");
  }

  virtual double getDouble(int32_t position) const {
    throw IoTDBException("Unsupported operation: getDouble");
  }

  virtual std::shared_ptr<Binary> getBinary(int32_t position) const {
    throw IoTDBException("Unsupported operation: getBinary");
  }

  virtual std::vector<bool> getBooleans() const {
    throw IoTDBException("Unsupported operation: getBooleans");
  }

  virtual std::vector<int32_t> getInts() const {
    throw IoTDBException("Unsupported operation: getInts");
  }

  virtual std::vector<int64_t> getLongs() const {
    throw IoTDBException("Unsupported operation: getLongs");
  }

  virtual std::vector<float> getFloats() const {
    throw IoTDBException("Unsupported operation: getFloats");
  }

  virtual std::vector<double> getDoubles() const {
    throw IoTDBException("Unsupported operation: getDoubles");
  }

  virtual std::vector<std::shared_ptr<Binary>> getBinaries() const {
    throw IoTDBException("Unsupported operation: getBinaries");
  }

  virtual bool mayHaveNull() const = 0;
  virtual bool isNull(int32_t position) const = 0;
  virtual std::vector<bool> isNulls() const = 0;

  virtual int32_t getPositionCount() const = 0;
};

class TimeColumn : public Column {
public:
  TimeColumn(int32_t arrayOffset, int32_t positionCount, const std::vector<int64_t>& values);

  TSDataType::TSDataType getDataType() const override;
  ColumnEncoding getEncoding() const override;

  int64_t getLong(int32_t position) const override;

  bool mayHaveNull() const override;
  bool isNull(int32_t position) const override;
  std::vector<bool> isNulls() const override;

  int32_t getPositionCount() const override;

  int64_t getStartTime() const;
  int64_t getEndTime() const;

  const std::vector<int64_t>& getTimes() const;
  std::vector<int64_t> getLongs() const override;

private:
  int32_t arrayOffset_;
  int32_t positionCount_;
  std::vector<int64_t> values_;
};

class BinaryColumn : public Column {
public:
  BinaryColumn(int32_t arrayOffset, int32_t positionCount, const std::vector<bool>& valueIsNull,
               const std::vector<std::shared_ptr<Binary>>& values);

  TSDataType::TSDataType getDataType() const override;
  ColumnEncoding getEncoding() const override;

  std::shared_ptr<Binary> getBinary(int32_t position) const override;
  std::vector<std::shared_ptr<Binary>> getBinaries() const override;

  bool mayHaveNull() const override;
  bool isNull(int32_t position) const override;
  std::vector<bool> isNulls() const override;

  int32_t getPositionCount() const override;

private:
  int32_t arrayOffset_;
  int32_t positionCount_;
  std::vector<bool> valueIsNull_;
  std::vector<std::shared_ptr<Binary>> values_;
};

class IntColumn : public Column {
public:
  IntColumn(int32_t arrayOffset, int32_t positionCount, const std::vector<bool>& valueIsNull,
            const std::vector<int32_t>& values);

  TSDataType::TSDataType getDataType() const override;
  ColumnEncoding getEncoding() const override;

  int32_t getInt(int32_t position) const override;
  int64_t getLong(int32_t position) const override;
  float getFloat(int32_t position) const override;
  double getDouble(int32_t position) const override;
  std::vector<int32_t> getInts() const override;

  bool mayHaveNull() const override;
  bool isNull(int32_t position) const override;
  std::vector<bool> isNulls() const override;

  int32_t getPositionCount() const override;

private:
  int32_t arrayOffset_;
  int32_t positionCount_;
  std::vector<bool> valueNull_;
  std::vector<int32_t> values_;
};

class FloatColumn : public Column {
public:
  FloatColumn(int32_t arrayOffset, int32_t positionCount, const std::vector<bool>& valueIsNull,
              const std::vector<float>& values);

  TSDataType::TSDataType getDataType() const override;
  ColumnEncoding getEncoding() const override;

  float getFloat(int32_t position) const override;
  double getDouble(int32_t position) const override;
  std::vector<float> getFloats() const override;

  bool mayHaveNull() const override;
  bool isNull(int32_t position) const override;
  std::vector<bool> isNulls() const override;

  int32_t getPositionCount() const override;

private:
  int32_t arrayOffset_;
  int32_t positionCount_;
  std::vector<bool> valueIsNull_;
  std::vector<float> values_;
};

class LongColumn : public Column {
public:
  LongColumn(int32_t arrayOffset, int32_t positionCount, const std::vector<bool>& valueIsNull,
             const std::vector<int64_t>& values);

  TSDataType::TSDataType getDataType() const override;
  ColumnEncoding getEncoding() const override;

  int64_t getLong(int32_t position) const override;
  double getDouble(int32_t position) const override;
  std::vector<int64_t> getLongs() const override;

  bool mayHaveNull() const override;
  bool isNull(int32_t position) const override;
  std::vector<bool> isNulls() const override;

  int32_t getPositionCount() const override;

private:
  int32_t arrayOffset_;
  int32_t positionCount_;
  std::vector<bool> valueIsNull_;
  std::vector<int64_t> values_;
};

class DoubleColumn : public Column {
public:
  DoubleColumn(int32_t arrayOffset, int32_t positionCount, const std::vector<bool>& valueIsNull,
               const std::vector<double>& values);

  TSDataType::TSDataType getDataType() const override;
  ColumnEncoding getEncoding() const override;

  double getDouble(int32_t position) const override;
  std::vector<double> getDoubles() const override;

  bool mayHaveNull() const override;
  bool isNull(int32_t position) const override;
  std::vector<bool> isNulls() const override;

  int32_t getPositionCount() const override;

private:
  int32_t arrayOffset_;
  int32_t positionCount_;
  std::vector<bool> valueIsNull_;
  std::vector<double> values_;
};

class BooleanColumn : public Column {
public:
  BooleanColumn(int32_t arrayOffset, int32_t positionCount, const std::vector<bool>& valueIsNull,
                const std::vector<bool>& values);

  TSDataType::TSDataType getDataType() const override;
  ColumnEncoding getEncoding() const override;

  bool getBoolean(int32_t position) const override;
  std::vector<bool> getBooleans() const override;

  bool mayHaveNull() const override;
  bool isNull(int32_t position) const override;
  std::vector<bool> isNulls() const override;

  int32_t getPositionCount() const override;

private:
  int32_t arrayOffset_;
  int32_t positionCount_;
  std::vector<bool> valueIsNull_;
  std::vector<bool> values_;
};

class RunLengthEncodedColumn : public Column {
public:
  RunLengthEncodedColumn(std::shared_ptr<Column> value, int32_t positionCount);

  std::shared_ptr<Column> getValue() const;

  TSDataType::TSDataType getDataType() const override;
  ColumnEncoding getEncoding() const override;

  bool getBoolean(int32_t position) const override;
  int32_t getInt(int32_t position) const override;
  int64_t getLong(int32_t position) const override;
  float getFloat(int32_t position) const override;
  double getDouble(int32_t position) const override;
  std::shared_ptr<Binary> getBinary(int32_t position) const override;

  std::vector<bool> getBooleans() const override;
  std::vector<int32_t> getInts() const override;
  std::vector<int64_t> getLongs() const override;
  std::vector<float> getFloats() const override;
  std::vector<double> getDoubles() const override;
  std::vector<std::shared_ptr<Binary>> getBinaries() const override;

  bool mayHaveNull() const override;
  bool isNull(int32_t position) const override;
  std::vector<bool> isNulls() const override;

  int32_t getPositionCount() const override;

private:
  std::shared_ptr<Column> value_;
  int32_t positionCount_;
};

#endif
