/* 
 * Copyright (c) 2007, 2013, 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 _SQL_EDITOR_BE_H_
#define _SQL_EDITOR_BE_H_

// TODO: cleanup includes.
#include <memory>
#include "base/trackable.h"
#include "wbpublic_public_interface.h"
#include "grt/grt_threaded_task.h"

#include "grtsqlparser/sql_semantic_check.h"

#include "grts/structs.db.mgmt.h"
#include "grts/structs.db.query.h"
#include "grt/grt_manager.h"

#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/function.hpp>

#include "base/geometry.h"

namespace mforms {
  class CodeEditor;
  class FindPanel;
  class Menu;
  class View;
  class CodeEditorConfig;
};

class AutoCompleteCache;

// Identifiers for images used in auto completion lists.
#define AC_KEYWORD_IMAGE  1
#define AC_SCHEMA_IMAGE   2
#define AC_TABLE_IMAGE    3
#define AC_ROUTINE_IMAGE  4 // For SQL stored procedures + functions.
#define AC_FUNCTION_IMAGE 5 // For MySQL library functions.
#define AC_VIEW_IMAGE     6
#define AC_COLUMN_IMAGE   7
#define AC_OPERATOR_IMAGE 8
#define AC_ENGINE_IMAGE   9

/** Base class for SQL Editor Backend classes.
 Subclassed by specific RDBMS support modules.
 */

class WBPUBLICBACKEND_PUBLIC_FUNC Sql_editor : public boost::enable_shared_from_this<Sql_editor>, public base::trackable
{
public:
  struct TableReference
  {
    std::string schema;
    std::string table;
    std::string alias;
  };

  enum AutoCompletionWantedParts
  {
    CompletionWantNothing            = 0,
    CompletionWantMajorKeywords      = 1 << 0, // Keywords that can start a query.
    CompletionWantKeywords           = 1 << 1,
    CompletionWantExprStartKeywords  = 1 << 2, // Keywords allowed to begin an expression. Subset of all normal keywords.
    CompletionWantExprInnerKeywords  = 1 << 3, // Like the exp start keywords plus those between two expressions.
    CompletionWantRuntimeFunctions   = 1 << 4,
    CompletionWantFunctions          = 1 << 5,
    CompletionWantProcedures         = 1 << 6,
    CompletionWantSchemas            = 1 << 7,
    CompletionWantTables             = 1 << 8, // Tables + Views actually.
    CompletionWantColumns            = 1 << 9,
    CompletionWantEvents             = 1 << 10, // TODO
    CompletionWantTriggers           = 1 << 11, // TODO
    CompletionWantIndexes            = 1 << 12, // TODO
    CompletionWantEngines            = 1 << 13,
    CompletionWantUsers              = 1 << 14, // TODO

    // Keywords we know that must appear.
    CompletionWantSelect             = 1 << 16, // For sub queries.
    CompletionWantBy                 = 1 << 17, // After ORDER and GROUP (not after LOGFILE, though).
  };

#define CompletionWantAKeyword (AutoCompletionWantedParts) \
  (\
    CompletionWantMajorKeywords | CompletionWantKeywords | CompletionWantExprStartKeywords |\
    CompletionWantExprInnerKeywords | CompletionWantSelect | CompletionWantBy\
  )

  // Context structure for code completion results and token info. Located here as we need it also
  // for unit testing. Otherwise it is completely internal.
  struct AutoCompletionContext
  {
    AutoCompletionWantedParts wanted_parts;
    long version;               // The server version.

    bool check_identifier;     // Sometimes we should not try to parse identifiers.
    bool in_table_reference;   // Don't include aliases if we are in a table reference.
    bool qualified_identifier; // In a qualified identifier somewhere after a dot.

    // There can be at most 3 id parts in a qualified identifier (schema.table.column). But due to
    // the ambiguity of id.id, we can have different schemas for table and column lookup.
    std::string table_schema;
    std::string column_schema;
    std::string table;
    std::string column;

    // Position + other info of the token at the caret.
    unsigned int token_line;
    unsigned int token_start;
    unsigned int token_length;
    unsigned int token_type;
    std::string token;

    // Current caret position, statement to parse and input typed by the user.
    unsigned int line;
    unsigned int offset;
    std::string statement;
    std::string typed_part;

    std::vector<TableReference> references; // As in FROM, UPDATE etc.

    AutoCompletionContext()
    {
      wanted_parts = CompletionWantMajorKeywords;
      version = 50510;
      check_identifier = true;
      in_table_reference = false;
      qualified_identifier = false;

      token_line = 0;
      token_start = 0;
      token_length = 0;
      token_type = 0;

      line = 0;
      offset = 0;
    };

  };

  typedef boost::shared_ptr<Sql_editor> Ref;
  typedef boost::weak_ptr<Sql_editor> Ptr;

  static Ref create(db_mgmt_RdbmsRef rdbms, GrtVersionRef version, db_query_QueryBufferRef grtobj = db_query_QueryBufferRef());

  virtual ~Sql_editor();

  db_query_QueryBufferRef grtobj();

  void set_base_toolbar(mforms::ToolBar *toolbar);

  mforms::View* get_container();
  mforms::ToolBar* get_toolbar(bool include_file_actions = true);
  mforms::CodeEditor* get_editor_control();
  mforms::FindPanel* get_find_panel();
  mforms::CodeEditorConfig* get_editor_settings();

  void show_special_chars(bool flag);
  void enable_word_wrap(bool flag);

  db_mgmt_RdbmsRef rdbms();
  bec::GRTManager *grtm();
  std::string sql_mode() { return _sql_mode; };

  int int_option(std::string name);
  std::string string_option(std::string name);

  void set_current_schema(const std::string &schema);
  std::string sql();
  std::pair<const char*, size_t> text_ptr();
  void sql(const char *sql);
  
  bool empty();
  void append_text(const std::string &text);

  std::string current_statement();
  std::string get_selected_sql();

  int cursor_pos();
  std::pair<int, int> cursor_pos_row_column(bool local);
  void set_cursor_pos(int position);

  bool selected_range(int& start, int& end);
  void set_selected_range(int start, int end);

  bool is_refresh_enabled() const;
  void set_refresh_enabled(bool val);
  bool is_sql_check_enabled() const;
  void set_sql_check_enabled(bool val);
  
  void show_auto_completion(bool auto_choose_single);
  std::vector<std::pair<int, std::string> >  update_auto_completion(const std::string &typed_part);
  void cancel_auto_completion();
  void set_auto_completion_cache(AutoCompleteCache *cache);

  bool create_auto_completion_list(AutoCompletionContext &context);

  std::string selected_text();
  void set_selected_text(const std::string &new_text);
  void insert_text(const std::string &new_text);

  boost::signals2::signal<void ()>* text_change_signal();

  void sql_mode(const std::string &value);

  Sql_semantic_check::Ref sql_checker();
  bool has_sql_errors() const;

  void sql_check_progress_msg_throttle(double val);
  void stop_processing();

  void focus();

protected:
  Sql_editor(db_mgmt_RdbmsRef rdbms, GrtVersionRef version);

  mforms::CodeEditorConfig *_editor_config;  // Set by descendants.

private:
  class Private;
  Private *d; // d-pointer idiom.
  
  void set_grtobj(db_query_QueryBufferRef grtobj);

  void setup_auto_completion();
  std::string get_written_part(int position);
  virtual bool fill_auto_completion_keywords(std::vector<std::pair<int, std::string> > &entries,
    AutoCompletionWantedParts parts, bool upcase_keywords);

  void text_changed(int position, int length, int lines_changed, bool added);
  void char_added(int char_code);
  void dwell_event(bool started, int position, int x, int y);

  void setup_editor_menu();
  void editor_menu_opening();
  void activate_context_menu_item(const std::string &name);

  bool check_sql(bool sync);
  grt::StringRef do_check_sql(grt::GRT *grt, Ptr self_ptr);

  int on_report_sql_statement_border(int begin_lineno, int begin_line_pos, int end_lineno, int end_line_pos, int tag);
  int on_sql_error(int lineno, int tok_line_pos, int tok_len, const std::string &msg, int tag);
  int on_sql_check_progress(float progress, const std::string &msg, int tag);
  int on_sql_check_finished();

  void request_sql_check_results_refresh();

  bool code_completion_enabled();
  bool auto_start_code_completion();
  bool make_keywords_uppercase();
  bool get_current_statement_ranges(int &start, int &end);

  // These members are shared with sql_editor_autocomplete.cpp, so they cannot go into the private class.

  // Entries determined the last time we started auto completion. The actually shown list
  // is derived from these entries filtered by the current input.
  std::vector<std::pair<int, std::string> > _auto_completion_entries;
  AutoCompleteCache *_auto_completion_cache;

  mforms::CodeEditor* _code_editor;

  std::string _current_schema;
  std::string _last_ac_statement; // The last statement we used for auto completion.

  long _server_version;
  std::string _sql_mode;
  std::set<std::string> _charsets; // For lookup needed by parsers to determine repertoires.
};

#endif /* _SQL_EDITOR_BE_H_ */

