#include "myx_grt_mysql_reveng_script_helper.h"
#include "grtpp_util.h"
#include "myx_sql_tree_item.h"
#include "grts/structs.db.mysql.h"

using namespace grt;

db_mysql_SchemaRef add_schema(ListRef<db_mysql_Schema> &schemata, HELPER_ARGS *args, const char *schema_name)
{
  db_mysql_SchemaRef schema(args->grt);
  schema.name(schema_name);
  schemata.insert(schema);
  return schema;
}

void process_create_schema_statement(MyxSQLTreeItem *tree, HELPER_ARGS *args)
{
  ListRef<db_mysql_Schema> schemata= args->catalog.schemata();
  MyxCreateSchemaTreeItem helper(tree);
  add_schema(schemata, args, helper.get_schema_name());
}

db_mysql_TableRef add_table(ListRef<db_mysql_Table> &schema_tables, HELPER_ARGS *args, MyxCreateTableTreeItem *helper)
{
  db_mysql_TableRef table(args->grt);

  // add table attributes
  if(helper->get_table_name() != NULL)
  {
    table.name(helper->get_table_name());
  }
  if(helper->get_table_engine() != NULL)
  {
    table.tableEngine(helper->get_table_engine());
  }
  table.isTemporary(helper->is_temporary() ? 1 : 0);
  if(args->sql != NULL)
  {
    table.sql(args->sql);
  }
  if(helper->get_row_format() != NULL)
  {
    table.rowFormat(helper->get_row_format());
  }
  if(helper->get_next_auto_inc() != NULL)
  {
    table.nextAutoInc(helper->get_next_auto_inc());
  }
  if(helper->get_def_collation() != NULL)
  {
    table.defaultCollationName(helper->get_def_collation());
  }
  if(helper->get_comment() != NULL)
  {
    table.comment(helper->get_comment());
  }
  if(helper->get_delay_key_write() != NULL)
  {
    table.delayKeyWrite(*helper->get_delay_key_write()); //!explicit cast added (const char*) -> (int)
  }
  if(helper->get_def_charset() != NULL)
  {
    table.defaultCharacterSetName(helper->get_def_charset());
  }
  if(helper->get_insert_method() != NULL)
  {
    table.mergeInsert(helper->get_insert_method());
  }
  if(helper->get_data_directory() != NULL)
  {
    table.tableDataDir(helper->get_data_directory());
  }
  if(helper->get_index_directory() != NULL)
  {
    table.tableIndexDir(helper->get_index_directory());
  }
  if(helper->get_raid_type() != NULL)
  {
    table.raidType(helper->get_raid_type());
  }
  if(helper->get_raid_chunks() != NULL)
  {
    table.raidChunks(helper->get_raid_chunks());
  }
  if(helper->get_raid_chunksize() != NULL)
  {
    table.raidChunkSize(helper->get_raid_chunksize());
  }
  if(helper->get_pack_keys() != NULL)
  {
    table.packKeys(helper->get_pack_keys());
  }
  if(helper->get_avg_row_length() != NULL)
  {
    table.avgRowLength(helper->get_avg_row_length());
  }
  if(helper->get_min_rows() != NULL)
  {
    table.minRows(helper->get_min_rows());
  }
  if(helper->get_max_rows() != NULL)
  {
    table.maxRows(helper->get_max_rows());
  }
  if(helper->get_checksum() != NULL)
  {
    table.checksum(*helper->get_checksum()); //! explicit cast added (const char*) -> (int)
  }

  // build datatype cache
  GHashTable *datatype_cache= g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); //! to be replaced by alternative cpp class

  ListRef<db_SimpleDatatype> datatypes= args->catalog.simpleDatatypes();
  for (unsigned i= 0; i < datatypes.count(); i++)
  {
    db_SimpleDatatypeRef datatype= datatypes.get(i);
    g_hash_table_insert(datatype_cache, (char *)((std::string)datatype->name()).c_str(), datatype.valueptr());
  }

  // add columns  
  ListRef<db_mysql_Column> columns(args->grt);
  table.columns(columns);

  for(MyxCreateTableTreeItem::ColumnList::const_iterator it= helper->get_columns()->begin(); it != helper->get_columns()->end(); it++)
  {
    const MyxCreateTableTreeItem::Column& c= *it;
    db_mysql_ColumnRef column(args->grt);

    if(c.get_column_name() != NULL)
    {
      column.name(c.get_column_name());
      //column.oldName(c.get_column_name());
    }
    if(c.get_datatype_name() != NULL)
    {
      //!datatypeName is not a member
      //!column.datatypeName(c.get_datatype_name());
      MYX_GRT_VALUE* datatype_= static_cast<MYX_GRT_VALUE *>(g_hash_table_lookup(datatype_cache, c.get_datatype_name()));
      if(datatype_ != NULL)
      {
        db_SimpleDatatypeRef datatype(datatype_);
        column.simpleType(datatype);
      }
    }
    column.precision(c.get_precision());
    column.scale(c.get_scale());
    column.length(c.get_length());
    column.datatypeExplicitParams(c.get_explicit_params());
    //!unsigned is not a member
    //!column.unsigned(c.is_unsigned());
    if(c.get_collation_name() != NULL)
    {
      column.collationName(c.get_collation_name());
    }
    if(c.get_collation_name() != NULL)
    {
      column.isNotNull(!c.is_nullable());
    }
    if(c.get_default_value() != NULL)
    {
      column.defaultValue(c.get_default_value());
    }
    column.defaultValueIsNull(c.is_default_value_null());
    column.autoIncrement(c.get_auto_increment());
    if(c.get_comment() != NULL)
    {
      column.comment(c.get_comment());
    }

    columns.insert(column);
  }

  // add indices
  ListRef<db_mysql_Index> indices(args->grt);
  table.indices(indices);
  
  for(MyxCreateTableTreeItem::IndexList::const_iterator it = helper->get_indices()->begin(); it != helper->get_indices()->end(); it++)
  {
    const MyxCreateTableTreeItem::Index& idx= *it;
    db_mysql_IndexRef index(args->grt);

    index.name(idx.get_index_name());
    //index.oldName(idx.get_index_name());
    index.owner(table);
    
    //index.comment(idx.);
    index.unique(idx.is_unique() ? 1 : 0);
    if(idx.is_primary())
    {
      index.indexKind("PRIMARY");
      table.primaryKey(index);
    }
    if(idx.get_index_type() != NULL)
    {
      index.indexType(idx.get_index_type());
    }

    // add index columns
    ListRef<db_IndexColumn> index_columns(args->grt);
    index.columns(index_columns);

    for(MyxCreateTableTreeItem::IndexColumnList::const_iterator colit = idx.get_columns()->begin(); colit != idx.get_columns()->end(); colit++)
    {
      const MyxCreateTableTreeItem::IndexColumn& idxcol= *colit;
      db_mysql_IndexColumnRef index_column(args->grt);

      index_column.name(idxcol.get_ref_column()->get_column_name());
      index_column.owner(index);
      index_column.columnLength(idxcol.get_length());
      index_column.descend(idxcol.is_descend() ? 0 : 1);

      const char *index_column_name= idxcol.get_ref_column()->get_column_name();

      for (unsigned i= 0; i < columns.count(); i++)
      {
        db_mysql_ColumnRef column= columns.get(i);

        if ((std::string)column->name() == std::string(index_column_name))
        {
          index_column.referedColumn(column);
          break;
        }
      }

      index_columns.insert(index_column);
    }

    indices.insert(index);
  }

  // add foreign keys
  ListRef<db_ForeignKey> fks(args->grt);
  table.foreignKeys(fks);
  
  for(MyxCreateTableTreeItem::ForeignKeyList::const_iterator it = helper->get_fks()->begin(); it != helper->get_fks()->end(); it++)
  {
    const MyxCreateTableTreeItem::ForeignKey& fkey= *it;
    db_mysql_ForeignKeyRef fk(args->grt);

    ListRef<db_Column> fk_columns(args->grt);
    fk.columns(fk_columns);

    StringListRef fk_refered_column_names(args->grt);
    fk.referedColumnNames(fk_refered_column_names);

    ListRef<db_Column> fk_refered_columns(args->grt);
    fk.referedColumns(fk_refered_columns);

    if(fkey.get_fk_name() != NULL)
    {
      fk.name(fkey.get_fk_name());
      //fk.oldName(fkey.get_fk_name());    
    }

    if(fkey.get_ref_schema_name() != NULL)
    {
      fk.referedTableSchemaName(fkey.get_ref_schema_name());
    }

    if(fkey.get_ref_table_name())
    {
      fk.referedTableName(fkey.get_ref_table_name());
    }

    if(fkey.get_delete_rule() != NULL)
    {
      fk.deleteRule(fkey.get_delete_rule());
    }
    if(fkey.get_update_rule() != NULL)
    {
      fk.updateRule(fkey.get_update_rule());
    }

    for(MyxCreateTableTreeItem::CStringList::const_iterator it= fkey.get_ref_columns()->begin(); it != fkey.get_ref_columns()->end(); it++)
    {
      fk_refered_column_names.insert(*it);
    }

    for(MyxCreateTableTreeItem::ColumnList::const_iterator it= fkey.get_columns()->begin(); it != fkey.get_columns()->end(); it++)
    {
      const MyxCreateTableTreeItem::Column& c= *it;
      
      for (unsigned j= 0; j < columns.count(); j++)
      {
        db_mysql_ColumnRef table_column= columns.get(j);
        if (table_column->name() == std::string(c.get_column_name()))
        {
          fk_columns.insert(table_column);
          break;
        }
      }
    }

    fks.insert(fk);
  }

  schema_tables.insert(table);

  return table;
}

/*
MYX_GRT_VALUE * add_routine(MYX_GRT_VALUE * schema_routines, HELPER_ARGS * args, MyxCreateRoutineTreeItem * helper)
{
  MYX_GRT_VALUE *routine= myx_grt_dict_new(args->grt, "db.mysql.Routine");
  myx_grt_dict_generate_id(routine);

  if(helper->get_routine_name() != NULL)
  {
    myx_grt_dict_item_set_value_from_string(routine, "name", helper->get_routine_name());
    //myx_grt_dict_item_set_value_from_string(routine, "oldName", helper->get_routine_name());
  }

  myx_grt_dict_item_set_value_from_string(routine, "owner", myx_grt_dict_item_get_as_string(args->used_schema, "_id"));
  myx_grt_dict_item_set_value_from_string(routine, "routineType", helper->get_routine_type());
  
  if(strcmp2(helper->get_routine_type(), "function") == 0)
  {
    myx_grt_dict_item_set_value_from_string(routine, "returnDatatype", helper->get_return_type());
  }
  
  myx_grt_dict_item_set_value_from_string(routine, "routineCode", args->sql);

  MYX_GRT_VALUE *sp_params= myx_grt_list_new(DictType, "db.mysql.RoutineParam");

  myx_grt_dict_item_set_value(routine, "params", sp_params);
  myx_grt_value_release(sp_params);

  for(MyxCreateRoutineTreeItem::RoutineParamList::const_iterator it= helper->get_params()->begin(); it != helper->get_params()->end(); it++)
  {
    const MyxCreateRoutineTreeItem::RoutineParam& p= *it;
    
    MYX_GRT_VALUE *sp_param= myx_grt_dict_new(args->grt, "db.mysql.RoutineParam");
    myx_grt_dict_generate_id(sp_param);    

    myx_grt_dict_item_set_value_from_string(sp_param, "name", p.get_param_name());
    myx_grt_dict_item_set_value_from_string(sp_param, "owner", myx_grt_dict_item_get_as_string(routine, "_id"));
    myx_grt_dict_item_set_value_from_string(sp_param, "datatype", p.get_param_type());
    myx_grt_dict_item_set_value_from_string(sp_param, "paramType", p.get_param_dir());  // in/out/input

    myx_grt_list_item_add(sp_params, sp_param);
    myx_grt_value_release(sp_param);
  }

  myx_grt_list_item_add(schema_routines, routine);
  myx_grt_value_release(routine);

  return routine;
}

MYX_GRT_VALUE * add_view(MYX_GRT_VALUE * schema_views, HELPER_ARGS * args, MyxCreateViewTreeItem * helper)
{
  MYX_GRT_VALUE *view= myx_grt_dict_new(args->grt, "db.mysql.View");
  myx_grt_dict_generate_id(view);
  myx_grt_dict_item_set_value_from_string(view, "name", helper->get_view_name());
  //myx_grt_dict_item_set_value_from_string(view, "oldName", helper->get_view_name());
  myx_grt_dict_item_set_value_from_string(view, "owner", myx_grt_dict_item_get_as_string(args->used_schema, "_id"));

  myx_grt_dict_item_set_value_from_string(view, "queryExpression", args->sql);
  myx_grt_dict_item_set_value_from_int(view, "withCheckCondition", helper->is_with_check() ? 1 : 0);  

  myx_grt_list_item_add(schema_views, view);
  myx_grt_value_release(view);

  return view;
}
*/
db_mysql_SchemaRef set_used_schema(HELPER_ARGS * args, const char *schema_name)
{
  ListRef<db_mysql_Schema> schemata= args->catalog.schemata();
  db_mysql_SchemaRef schema= find_named_object_in_list(schemata, schema_name);
  
  if(schema.is_valid())
  {
    schema= add_schema(schemata, args, schema_name);
  }

  args->used_schema= schema;
  return schema;
}

void process_create_table_statement(MyxSQLTreeItem *tree, HELPER_ARGS * args)
{
  db_mysql_SchemaRef prev_schema= args->used_schema;
  MyxCreateTableTreeItem helper(tree);

  if(helper.get_schema_name() != NULL)
  {
    set_used_schema(args, helper.get_schema_name());  // this will add schema to schemata if nesessary
  }
  
  add_table(args->used_schema.tables(), args, &helper);

  args->used_schema= prev_schema;
/*
  MYX_GRT_VALUE *prev_schema= args->used_schema;
  MyxCreateTableTreeItem helper(tree);

  if(helper.get_schema_name() != NULL)
  {
    set_used_schema(args, helper.get_schema_name());  // this will add schema to schemata if nesessary
  }
  
  MYX_GRT_VALUE *schema_tables= myx_grt_dict_item_get_value(args->used_schema, "tables");
  add_table(schema_tables, args, &helper);

  args->used_schema= prev_schema;
*/
}
/*
void process_create_routine_statement(MyxSQLTreeItem *tree, HELPER_ARGS * args)
{
  MYX_GRT_VALUE *prev_schema= args->used_schema;
  MyxCreateRoutineTreeItem helper(tree);

  if(helper.get_schema_name() != NULL)
  {
    set_used_schema(args, helper.get_schema_name());  // this will add schema to schemata if nesessary
  }
  
  MYX_GRT_VALUE *schema_routines= myx_grt_dict_item_get_value(args->used_schema, "routines");
  add_routine(schema_routines, args, &helper);

  args->used_schema= prev_schema;
}

void process_create_view_statement(MyxSQLTreeItem *tree, HELPER_ARGS * args)
{
  MYX_GRT_VALUE *prev_schema= args->used_schema;
  MyxCreateViewTreeItem helper(tree);

  if(helper.get_schema_name() != NULL)
  {
    set_used_schema(args, helper.get_schema_name());  // this will add schema to schemata if nesessary
  }
  
  MYX_GRT_VALUE *schema_views= myx_grt_dict_item_get_value(args->used_schema, "views");
  add_view(schema_views, args, &helper);

  args->used_schema= prev_schema;
}

void process_use_statement(MyxSQLTreeItem *tree, HELPER_ARGS * args)
{
  MyxUseSchemaTreeItem helper(tree);
  set_used_schema(args, helper.get_schema_name()); 
}
*/
extern "C" void convert_parser_dom_to_grt_dom(void *parser_dom_tree, HELPER_ARGS * args)
{
  //MYX_GRT_VALUE * catalog= args->catalog;
  //MYX_GRT * grt= args->grt;
  
  MyxSQLTreeItem *tree = static_cast<MyxSQLTreeItem *>(parser_dom_tree);

  if(MyxCreateSchemaTreeItem::check(tree)) 
  {
    process_create_schema_statement(tree, args);
  }
  else if(MyxCreateTableTreeItem::check(tree)) 
  {
    process_create_table_statement(tree, args);
  }
  else if(MyxCreateRoutineTreeItem::check(tree)) 
  {
//    process_create_routine_statement(tree, args);
  }
  else if(MyxCreateViewTreeItem::check(tree)) 
  {
//    process_create_view_statement(tree, args);
  }
  else if(MyxUseSchemaTreeItem::check(tree)) 
  {
//    process_use_statement(tree, args);
  }
}
