/* Copyright (c) 2014, 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 Street, Fifth Floor, Boston, MA 02110-1301, USA */

// First include (the generated) my_config.h, to get correct platform defines.
#include "my_config.h"
#include <gtest/gtest.h>

#include "field_temporal_utils.h"
#include "test_utils.h"
#include "fake_table.h"

#include "field.h"

namespace field_date_unittests {

using my_testing::Server_initializer;
using my_testing::Mock_error_handler;

class FieldDateTest : public ::testing::Test
{
protected:
  virtual void SetUp() { initializer.SetUp(); }
  virtual void TearDown() { initializer.TearDown(); }

  THD *thd() { return initializer.thd(); }

  Server_initializer initializer;

  Field_set *create_field_set(TYPELIB *tl);

  // Store zero date using different combinations of SQL modes
  static const int no_modes= 3;
  static const sql_mode_t strict_modes[no_modes];

  static const type_conversion_status nozero_expected_status[];
};

const sql_mode_t FieldDateTest::strict_modes[no_modes]=
{
  MODE_STRICT_TRANS_TABLES,
  MODE_STRICT_ALL_TABLES,
  MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES
};

class Mock_field_date : public Field_newdate
{
private:
  uchar buffer[PACK_LENGTH];
  uchar null_byte;

  void initialize()
  {
    ptr= buffer;
    memset(buffer, 0, PACK_LENGTH);
    null_byte= '\0';
    set_null_ptr(&null_byte, 1);
  }
public:

  Mock_field_date()
    : Field_newdate(0,                          // ptr_arg
                    NULL,                       // null_ptr_arg
                    1,                          // null_bit_arg
                    Field::NONE,                // unireg_check_arg
                    "field_name")               // field_name_arg
  {
    initialize();
  }

  void make_writable() { bitmap_set_bit(table->write_set, field_index); }
  void make_readable() { bitmap_set_bit(table->read_set, field_index); }
};


TEST_F(FieldDateTest, StoreLegalStringValues)
{
  Mock_field_date field_date;
  Fake_TABLE table(&field_date);
  table.in_use= thd();
  field_date.make_writable();
  field_date.make_readable();

  {
    SCOPED_TRACE("");
    test_store_string(&field_date, STRING_WITH_LEN("2001-01-01"),
                      "2001-01-01", 0, TYPE_OK);
  }
  {
    SCOPED_TRACE("");
    test_store_string(&field_date, STRING_WITH_LEN("0000-00-00"),
                      "0000-00-00", 0, TYPE_OK);
  }
  {
    SCOPED_TRACE("");
    test_store_string(&field_date, STRING_WITH_LEN("0001-00-00"),
                      "0001-00-00", 0, TYPE_OK);
  }
}

TEST_F(FieldDateTest, StoreIllegalStringValues)
{
  Mock_field_date field_date;
  Fake_TABLE table(&field_date);
  table.in_use= thd();
  field_date.make_writable();
  field_date.make_readable();
  thd()->count_cuted_fields= CHECK_FIELD_WARN;

  // Truncates time
  {
    SCOPED_TRACE("");
    test_store_string(&field_date, STRING_WITH_LEN("2001-01-01 00:00:01"),
                      "2001-01-01",
                      WARN_DATA_TRUNCATED, TYPE_NOTE_TIME_TRUNCATED);
  }

  // Bad year
  {
    SCOPED_TRACE("");
    test_store_string(&field_date, STRING_WITH_LEN("99999-01-01"),
                      "0000-00-00",
                      WARN_DATA_TRUNCATED, TYPE_ERR_BAD_VALUE);
  }

  // Bad month
  {
    SCOPED_TRACE("");
    test_store_string(&field_date, STRING_WITH_LEN("2001-13-01"), "0000-00-00",
                      WARN_DATA_TRUNCATED, TYPE_ERR_BAD_VALUE);
  }

  // Bad day
  {
    SCOPED_TRACE("");
    test_store_string(&field_date, STRING_WITH_LEN("2001-01-32"), "0000-00-00",
                      WARN_DATA_TRUNCATED, TYPE_ERR_BAD_VALUE);
  }

  // Not a date
  {
    SCOPED_TRACE("");
    test_store_string(&field_date, STRING_WITH_LEN("foo"), "0000-00-00",
                      WARN_DATA_TRUNCATED, TYPE_ERR_BAD_VALUE);
  }
}

/**
  Strictness mode test 1:

  Try storing dates with zeroes when NO SQL MODE is set. There
  should be no errors.
*/
TEST_F(FieldDateTest, StoreZeroDateSqlModeNothing)
{
  Mock_field_date field_date;
  Fake_TABLE table(&field_date);
  table.in_use= thd();
  field_date.make_writable();
  field_date.make_readable();
  thd()->count_cuted_fields= CHECK_FIELD_WARN;

  // Without STRICT MODE, Everything is valid
  {
    SCOPED_TRACE("");
    store_zero_in_sql_mode(&field_date,
                           STRING_WITH_LEN("0000-00-00"),
                           "0000-00-00",
                           TYPE_OK,
                           0,
                           0);
  }

  {
    SCOPED_TRACE("");
    store_zero_in_sql_mode(&field_date,
                           STRING_WITH_LEN("0000-01-01"),
                           "0000-01-01",
                           TYPE_OK,
                           0,
                           0);
  }

  {
    SCOPED_TRACE("");
    store_zero_in_sql_mode(&field_date,
                           STRING_WITH_LEN("2001-00-01"),
                           "2001-00-01",
                           TYPE_OK,
                           0,
                           0);
  }

  {
    SCOPED_TRACE("");
    store_zero_in_sql_mode(&field_date,
                           STRING_WITH_LEN("2001-01-00"),
                           "2001-01-00",
                           TYPE_OK,
                           0,
                           0);
  }
}

/**
  Strictness mode test 2:

  Try storing dates with zeroes when STRICT MODE is set. There
  should be no errors unless either month or day is zero.
*/
TEST_F(FieldDateTest, StoreZeroDateSqlModeStrict)
{
  Mock_field_date field_date;
  Fake_TABLE table(&field_date);
  table.in_use= thd();
  field_date.make_writable();
  field_date.make_readable();
  thd()->count_cuted_fields= CHECK_FIELD_WARN;

  // With strict mode set, Entire date zero is not ok
  for (int i= 0; i < no_modes; i++)
  {
    SCOPED_TRACE("");
    store_zero_in_sql_mode(&field_date,
                           STRING_WITH_LEN("0000-00-00"),
                           "0000-00-00",
                           TYPE_ERR_BAD_VALUE,
                           strict_modes[i],
                           ER_TRUNCATED_WRONG_VALUE);
  }

  // Year 0 is valid in strict mode too
  for (int i= 0; i < no_modes; i++)
  {
    SCOPED_TRACE("");
    store_zero_in_sql_mode(&field_date,
                           STRING_WITH_LEN("0000-01-01"),
                           "0000-01-01",
                           TYPE_OK,
                           strict_modes[i],
                           0);
  }

  // Month 0 is NOT valid in strict mode, stores all-zero date
  for (int i= 0; i < no_modes; i++)
  {
    SCOPED_TRACE("");
    store_zero_in_sql_mode(&field_date,
                           STRING_WITH_LEN("2001-00-01"),
                           "0000-00-00",
                           TYPE_ERR_BAD_VALUE,
                           strict_modes[i],
                           ER_TRUNCATED_WRONG_VALUE);
  }

  // Day 0 is NOT valid in strict mode, stores all-zero date
  for (int i= 0; i < no_modes; i++)
  {
    SCOPED_TRACE("");
    store_zero_in_sql_mode(&field_date,
                           STRING_WITH_LEN("2001-01-00"),
                           "0000-00-00",
                           TYPE_ERR_BAD_VALUE,
                           strict_modes[i],
                           ER_TRUNCATED_WRONG_VALUE);
  }
}


}
