/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * 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.
 */

#include "src/trace_processor/sqlite/stats_table.h"

#include "perfetto/base/status.h"
#include "src/trace_processor/sqlite/sqlite_utils.h"

namespace perfetto {
namespace trace_processor {

StatsTable::StatsTable(sqlite3*, const TraceStorage* storage)
    : storage_(storage) {}

StatsTable::~StatsTable() = default;

util::Status StatsTable::Init(int, const char* const*, Schema* schema) {
  *schema = Schema(
      {
          SqliteTable::Column(Column::kName, "name", SqlValue::Type::kString),
          // Calling a column "index" causes sqlite to silently fail, hence idx.
          SqliteTable::Column(Column::kIndex, "idx", SqlValue::Type::kLong),
          SqliteTable::Column(Column::kSeverity, "severity",
                              SqlValue::Type::kString),
          SqliteTable::Column(Column::kSource, "source",
                              SqlValue::Type::kString),
          SqliteTable::Column(Column::kValue, "value", SqlValue::Type::kLong),
          SqliteTable::Column(Column::kDescription, "description",
                              SqlValue::Type::kString),
      },
      {Column::kName});
  return util::OkStatus();
}

std::unique_ptr<SqliteTable::BaseCursor> StatsTable::CreateCursor() {
  return std::unique_ptr<SqliteTable::BaseCursor>(new Cursor(this));
}

int StatsTable::BestIndex(const QueryConstraints&, BestIndexInfo*) {
  return SQLITE_OK;
}

StatsTable::Cursor::Cursor(StatsTable* table)
    : SqliteTable::BaseCursor(table),
      table_(table),
      storage_(table->storage_) {}

StatsTable::Cursor::~Cursor() = default;

base::Status StatsTable::Cursor::Filter(const QueryConstraints&,
                                        sqlite3_value**,
                                        FilterHistory) {
  *this = Cursor(table_);
  return base::OkStatus();
}

base::Status StatsTable::Cursor::Column(sqlite3_context* ctx, int N) {
  const auto kSqliteStatic = sqlite_utils::kSqliteStatic;
  switch (N) {
    case Column::kName:
      sqlite3_result_text(ctx, stats::kNames[key_], -1, kSqliteStatic);
      break;
    case Column::kIndex:
      if (stats::kTypes[key_] == stats::kIndexed) {
        sqlite3_result_int(ctx, index_->first);
      } else {
        sqlite3_result_null(ctx);
      }
      break;
    case Column::kSeverity:
      switch (stats::kSeverities[key_]) {
        case stats::kInfo:
          sqlite3_result_text(ctx, "info", -1, kSqliteStatic);
          break;
        case stats::kDataLoss:
          sqlite3_result_text(ctx, "data_loss", -1, kSqliteStatic);
          break;
        case stats::kError:
          sqlite3_result_text(ctx, "error", -1, kSqliteStatic);
          break;
      }
      break;
    case Column::kSource:
      switch (stats::kSources[key_]) {
        case stats::kTrace:
          sqlite3_result_text(ctx, "trace", -1, kSqliteStatic);
          break;
        case stats::kAnalysis:
          sqlite3_result_text(ctx, "analysis", -1, kSqliteStatic);
          break;
      }
      break;
    case Column::kValue:
      if (stats::kTypes[key_] == stats::kIndexed) {
        sqlite3_result_int64(ctx, index_->second);
      } else {
        sqlite3_result_int64(ctx, storage_->stats()[key_].value);
      }
      break;
    case Column::kDescription:
      sqlite3_result_text(ctx, stats::kDescriptions[key_], -1, kSqliteStatic);
      break;
    default:
      PERFETTO_FATAL("Unknown column %d", N);
      break;
  }
  return base::OkStatus();
}

base::Status StatsTable::Cursor::Next() {
  static_assert(stats::kTypes[0] == stats::kSingle,
                "the first stats entry cannot be indexed");
  const auto* cur_entry = &storage_->stats()[key_];
  if (stats::kTypes[key_] == stats::kIndexed) {
    if (++index_ != cur_entry->indexed_values.end()) {
      return base::OkStatus();
    }
  }
  while (++key_ < stats::kNumKeys) {
    cur_entry = &storage_->stats()[key_];
    index_ = cur_entry->indexed_values.begin();
    if (stats::kTypes[key_] == stats::kSingle ||
        !cur_entry->indexed_values.empty()) {
      break;
    }
  }
  return base::OkStatus();
}

bool StatsTable::Cursor::Eof() {
  return key_ >= stats::kNumKeys;
}

}  // namespace trace_processor
}  // namespace perfetto
