/* 
 * Copyright (c) 2010, 2011, 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 _CODE_EDITOR_H_
#define _CODE_EDITOR_H_

#include <mforms/view.h>
#include <mforms/utilities.h>

/**
 * Provides a code editor with syntax highlighting for mforms.
 */

namespace mforms {

  class CodeEditor;

  enum SyntaxHighlighterLanguage {
    LanguageNone,
    LanguageMySQL,
    LanguageCpp,
    LanguageLua,
    LanguagePython
  };

  /**
   * A number of flags used to specify visual appearance of an editor line.
   */
  enum LineMarkup {
    LineMarkupNone          = 0,      // No markup for the given line.
    LineMarkupBreakpoint    = 1 << 0, // Line has a marker set for a break point.
    LineMarkupBreakpointHit = 1 << 1, // Line has a marker set for a break point which is currently hit.
    LineMarkupError         = 1 << 2, // Line's background is drawn in red to mark an execution error.
    LineMarkupStatement     = 1 << 3, // Marks a line as having a statement starting on it.
    LineMarkupCurrent       = 1 << 4, // Current execution line.

    LineMarkupAll           = 0xFF,   // All markup, useful for remove_markup.
  };

  inline LineMarkup operator| (LineMarkup a, LineMarkup b)
  {
    return (LineMarkup) ((int) a | (int) b);
  }

  enum CodeEditorFeature {
    FeatureNone          = 0,
    FeatureWrapText      = 1 << 0,  // Enables word wrapping.

    FeatureAll           = 0xFF,
  };

#ifndef DOXYGEN_SHOULD_SKIP_THIS
#ifndef SWIG
  struct CodeEditorImplPtrs
  {
    bool (__stdcall *create)(CodeEditor* self);
    void (__stdcall *set_text)(CodeEditor* self, const std::string& text);
    const std::string (__stdcall *get_text)(CodeEditor* self, bool selection_only);
    void (__stdcall *get_selection)(CodeEditor* self, int &start, int &length);
    void (__stdcall *set_selection)(CodeEditor* self, int start, int length);
    bool (__stdcall *get_range_of_line)(CodeEditor* self, int line, int &start, int &length);
    void (__stdcall *set_language)(CodeEditor* self, SyntaxHighlighterLanguage language);
    void (__stdcall *set_read_only)(CodeEditor* self, bool flag);
    void (__stdcall *show_markup)(CodeEditor* self, LineMarkup markup, int line);
    void (__stdcall *remove_markup)(CodeEditor* self, LineMarkup markup, int line);
    int (__stdcall *line_count)(CodeEditor* self);
    void (__stdcall *set_font)(CodeEditor* self, const std::string &fontDescription);
    void (__stdcall *show_gutter)(CodeEditor* self, bool flag); // TODO: Linux
    void (__stdcall *set_features)(CodeEditor* self, CodeEditorFeature feature, bool flag); // TODO: Linux, Mac
  };
#endif
#endif

  class MFORMS_EXPORT CodeEditor : public View
  {
  public:
    CodeEditor();

    /** Replaces the text in the editor. */
    void set_text(const std::string& text);
    void set_value(const std::string& text) { set_text(text); };

    /** Returns the text which is currently in the editor. If selection_only is true only the current
     *  selection is returned. If there is no selection then the result is an empty string in that case.
     */
    const std::string get_text(bool selection_only);
    virtual std::string get_string_value() { return get_text(false); };

    /** Selects the text at the given range. If length is 0 it will just move the cursor. */
    void set_selection(int start, int length);
    
    /** Gets the current selection range. */
    void get_selection(int &start, int &length);

    /** Gets the character range for the given line. Returns false if the line number is invalid */
    bool get_range_of_line(int line, int &start, int &length);
    
    /** Sets the language for the syntax highlighter. */
    void set_language(SyntaxHighlighterLanguage language);

    void set_read_only(bool flag);

    /** Adds the given markup to a line if not yet there. Does not touch other markup. */
    void show_markup(LineMarkup markup, int line);

    /** Removes the given markup from that line, without affecting other markup. */
    void remove_markup(LineMarkup markup, int line);

    /** Returns the number of lines currently in the editor. */
    int line_count();

    void set_font(const std::string &fontDescription); // e.g. "Trebuchet MS bold 9"
    
    // Note: the interface is partially temporary to make the Windows part working. It will be reworked
    //       to allow proper setup of the code editor using more of the native Scintilla features
    //       instead that of ScintillaNET.

    /** Show or hide the gutter (the part where line numbers and markers are shown). */
    void show_gutter(bool flag);

    /** Enables or disables different features in the editor which have a yes/no behavior. */
    void set_features(CodeEditorFeature features, bool flag);

#ifndef SWIG
    /** Signal emitted when content is edited
     *  Parameters are:
     *    The line in which the change happened (the start line if it spans more than one line).
     *    The number of lines which have been added (if positive) or removed (if negative).
     */
    boost::signals2::signal<void (int, int)>* signal_changed() { return &_signal_changed; }

    /** Signal emitted when the user clicks on the gutter.
     *  Parameters are:
     *    The margin (part of the gutter) the click occurred (0 = line numbers, 1 = markers, 2 = folding).
     *    The line in which this happened.
     *    The modifier keys that were pressed.
     */
    boost::signals2::signal<void (int, int, mforms::ModifierKey)>* signal_gutter_clicked() { return &_signal_gutter_clicked; }

#ifndef DOXYGEN_SHOULD_SKIP_THIS
    void text_changed(int line, int linesAdded);
    void gutter_clicked(int margin, int line, mforms::ModifierKey modifiers);
#endif
#endif
  protected:
    CodeEditorImplPtrs* _code_editor_impl;
    bool _updating;

    boost::signals2::signal<void (int, int)> _signal_changed;
    boost::signals2::signal<void (int, int, mforms::ModifierKey)> _signal_gutter_clicked;
  };
};

#endif // _CODE_EDITOR_H_
