/* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA  */

#ifndef FAKE_TABLE_H
#define FAKE_TABLE_H

#include "sql_class.h"
#include "handler-t.h"
#include <gmock/gmock.h>
#include "mock_field_long.h" // todo: put this #include first

using ::testing::NiceMock;
using std::vector;
using std::string;

static const uint MAX_TABLE_COLUMNS= sizeof(int) * 8;

/*
  A fake class for setting up TABLE_LIST object, required for table id mgmt.
*/
class Fake_TABLE_LIST : public TABLE_LIST
{
public:
  Fake_TABLE_LIST()
  {
  }
  ~Fake_TABLE_LIST()
  {
  }
};

/*
  A fake class to make setting up a TABLE object a little easier.
*/
class Fake_TABLE_SHARE : public TABLE_SHARE
{
  uint32 all_set_buf;

public:
  /**
    Creates a TABLE_SHARE with the requested number of columns

    @param  number_of_columns  The number of columns in the table
  */
  Fake_TABLE_SHARE(uint number_of_columns)
  {
    static const char *fakepath= "fakepath";
    fields= number_of_columns;
    db_create_options= 0;
    primary_key= 0;
    column_bitmap_size= sizeof(int);
    tmp_table= NO_TMP_TABLE;
    db_low_byte_first= true;
    path.str= const_cast<char*>(fakepath);
    path.length= strlen(path.str);

    EXPECT_EQ(0, bitmap_init(&all_set, &all_set_buf, fields, false));
    bitmap_set_above(&all_set, 0, 1);
  }
  ~Fake_TABLE_SHARE() {}
};

/*
  A fake class to make setting up a TABLE object a little easier. The
  table has a local fake table share.
*/
class Fake_TABLE: public TABLE
{
  // make room for 8 indexes (mysql permits 64)
  static const int max_keys= 8;
  KEY m_keys[max_keys];
  // make room for up to 8 keyparts per index
  KEY_PART_INFO m_key_part_infos[max_keys][8];

  Fake_TABLE_LIST  table_list;
  Fake_TABLE_SHARE table_share;
  // Storage space for the handler's handlerton
  Fake_handlerton fake_handlerton;
  MY_BITMAP write_set_struct;
  uint32 write_set_buf;
  MY_BITMAP read_set_struct;
  uint32 read_set_buf;
  Field *m_field_array[MAX_TABLE_COLUMNS];
  Mock_field_long *m_mock_field_array[MAX_TABLE_COLUMNS];

  // Counter for creating unique index id's. See create_index().
  int highest_index_id;

  // Counter for creating unique table id's. See initialize().
  static int highest_table_id;

  void initialize()
  {
    TABLE *as_table= static_cast<TABLE*>(this);
    memset(as_table, 0, sizeof(*as_table));
    s= &table_share;
    in_use= current_thd;
    null_row= '\0';
    read_set= &read_set_struct;
    write_set= &write_set_struct;
    next_number_field= NULL; // No autoinc column
    pos_in_table_list= &table_list;
    table_list.table= this;
    EXPECT_EQ(0, bitmap_init(write_set, &write_set_buf, s->fields, false));
    EXPECT_EQ(0, bitmap_init(read_set, &read_set_buf, s->fields, false));

    const_table= false;
    table_list.set_tableno(highest_table_id);
    highest_table_id= (highest_table_id + 1) % MAX_TABLES;
    key_info= &m_keys[0];
    for (int i= 0; i < max_keys; i++)
      key_info[i].key_part= m_key_part_infos[i];
    // We choose non-zero to avoid it working by coincidence.
    highest_index_id= 3;

    set_handler(&mock_handler);
    mock_handler.change_table_ptr(this, &table_share);
    field= m_field_array;
  }

public:

  /**
    Unless you hand it anything else, this class will create
    Mock_field_long columns, and this is their pack_length.
  */
  static const int DEFAULT_PACK_LENGTH= Mock_field_long::PACK_LENGTH;
  NiceMock<Mock_HANDLER> mock_handler;

  Fake_TABLE(List<Field> fields) :
    table_share(fields.elements),
    mock_handler(static_cast<handlerton*>(NULL), &table_share)
  {
    field= m_field_array;

    List_iterator<Field> it(fields);
    int nbr_fields= 0;
    for (Field *cur_field= it++; cur_field; cur_field= it++)
      add(cur_field, nbr_fields++);

    initialize();
  }

  Fake_TABLE(Field *column) :
    table_share(1),
    mock_handler(static_cast<handlerton*>(NULL), &table_share)
  {
    initialize();
    add(column, 0);
  }

  Fake_TABLE(Field *column1, Field *column2) :
    table_share(2),
    mock_handler(static_cast<handlerton*>(NULL), &table_share)
  {
    initialize();
    add(column1, 0);
    add(column2, 1);
  }

  Fake_TABLE(Field *column1, Field *column2, Field *column3) :
    table_share(3),
    mock_handler(static_cast<handlerton*>(NULL), &table_share)
  {
    initialize();
    add(column1, 0);
    add(column2, 1);
    add(column3, 2);
  }


  /**
    Creates a table with the requested number of columns without
    creating indexes.

    @param  column_count     The number of columns in the table
    @param  cols_nullable    Whether or not columns are allowed to be NULL
  */
  Fake_TABLE(int column_count, bool cols_nullable) :
    table_share(column_count),
    mock_handler(&fake_handlerton, &table_share)
  {
    DBUG_ASSERT(static_cast<size_t>(column_count) <= sizeof(int) * 8);
    initialize();
    for (int i= 0; i < column_count; ++i)
    {
      std::stringstream str;
      str << "field_" << (i + 1);
      add(new Mock_field_long(str.str().c_str(), cols_nullable), i);
    }
  }


  /**
     Creates a one-column fake table and stores the value in the one field.

     @param column_value Item holding the integer value to be stored.
  */
  Fake_TABLE(Item_int *column_value) :
    table_share(1),
      mock_handler(&fake_handlerton, &table_share)
  {
    initialize();
    add(new Mock_field_long("field_1"), 0);
    column_value->save_in_field_no_warnings(field[0], true);
  }


  /**
     Creates a two-column fake table and stores the values in their
     corresponding fields.

     @param column1_value Item holding integer value to be stored.
     @param column2_value Item holding integer value to be stored.
  */
  Fake_TABLE(Item_int *column1_value, Item_int *column2_value) :
    table_share(2),
    mock_handler(static_cast<handlerton*>(NULL), &table_share)
  {
    field= m_field_array;
    field[0]= new Mock_field_long("field_1");
    field[0]->table= this;
    field[1]= new Mock_field_long("field_2");
    field[1]->table= this;
    initialize();
    column1_value->save_in_field_no_warnings(field[0], true);
    column2_value->save_in_field_no_warnings(field[1], true);
  }

  ~Fake_TABLE()
  {
    /*
      This DTOR should be empty, since we inherit from TABLE,
      which cannot have virtual member functions.
    */
  }

  // Defines an index over (column1, column2) and generates a unique id.
  int create_index(Field *column1, Field *column2) {
    column1->flags|= PART_KEY_FLAG;
    column2->flags|= PART_KEY_FLAG;
    int index_id= highest_index_id++;
    column1->key_start.set_bit(index_id);
    keys_in_use_for_query.set_bit(index_id);
    return index_id;
  }

  void set_handler(handler *h) { file= h; }
  TABLE_SHARE *get_share() { return &table_share; }

private:
  void add(Field *new_field, int pos)
  {
    field[pos]= new_field;
    new_field->table= this;
    static const char *table_name= "Fake";
    new_field->table_name= &table_name;
    new_field->field_index= pos;
    bitmap_set_bit(read_set, pos);
  }
};

// We choose non-zero to avoid it working by coincidence.
int Fake_TABLE::highest_table_id= 5;

#endif // FAKE_TABLE_H
