import os, subprocess
from tempfile import mkstemp
from gettext import gettext as _
from gi.repository import GObject, Gtk, Pluma, Peas, PeasGtk
from settings import Settings

SAVE_PATH_KEY = 'save-path'
SAVE_PATH_DEFAULT = os.path.expanduser("~")

ui_str = """<ui>
  <menubar name="MenuBar">
    <menu name="FileMenu" action="File">
      <placeholder name="FileOps_4">
        <separator/>
        <menu name="MarkdownExporterMenu" action="MarkdownExporterMenu">
            <menuitem name="MarkdownExporterPDF" action="MarkdownExporterPDF"/>
            <menuitem name="MarkdownExporterHTML" action="MarkdownExporterHTML"/>
        </menu>
      </placeholder>
    </menu>
  </menubar>
</ui>
"""

class Exporter(GObject.Object, Peas.Activatable, PeasGtk.Configurable):
    """Plugin used to export Markdown files to HTML or PDF.
    
    Uses the `libpeas` module as well as the `GObject` modules in order to
    build up the UI and signal actions. Menu option currently placed between the
    `Revert` and `Print` options.

    ...

    Default methods
    ---------------
    __init__()
        Initializes using `GObject`
    do_activate()
        Called when plugin is activated or window is created.
        Overrides `WindowActivatable.do_activate`.
    do_deactivate()
        Called when plugin is deactivated or window is destroyed.
        Overrides `WindowActivatable.do_deactivate`.
    do_update_state()
        Called whenever window has been updated; for example, if the active tab
        has been changed.
        Overrides `WindowActivatable.do_update_state`.
    do_create_configure_widget()
        Called when the plugin's preferences are opened.

    Helper methods
    --------------
    _insert_menu()
        Inserts custom menu and subcommands into the `File` menu.
        Called on `do_activate`.
    _remove_menu()
        Removes custom menu and subcommands.
        Called on `do_deactivate`.
    _save_configuration(button)
        Saves the user entered configuration to the `exporter-settings` file.
        Called via callback from `do_create_configure_widget`.
    _export_to_pdf(action)
        Menu activation handler for PDF export.
    _export_to_html(action)
        Menu activation handler for HTML export.
    _write_temp_file(doc)
        Writes the current active document's text to a temporary file
        for further processing.
    _write_exported_html_file(converterArgs, outputPath)
        Writes a converted html file to the given `outputPath`.
    """
    __gtype_name__ = "Exporter"

    object = GObject.Property(type=GObject.Object)

    def __init__(self):
        GObject.Object.__init__(self)

    def do_activate(self):
        self.window = self.object
        self._insert_menu()

    def do_deactivate(self):
        self._remove_menu()
        self._action_group = None

    def do_update_state(self):
        self._action_group.set_sensitive(self.window.get_active_document() != None)

    def do_create_configure_widget(self):
        widget = Gtk.Table()
        widget.set_row_spacings(5)
        widget.set_col_spacings(5)
        
        label = Gtk.Label("Save to: ")

        self._configure_directory_textfield = Gtk.TextView()
        self._configure_directory_textfield.get_buffer().set_text(Settings.get_value(SAVE_PATH_KEY, SAVE_PATH_DEFAULT))

        save_button = Gtk.Button("Save")
        save_button.connect("clicked", self._save_configuration)

        widget.attach(label, 0, 1, 0, 1)
        widget.attach(self._configure_directory_textfield, 1, 2, 0, 1)
        widget.attach(save_button, 0, 2, 2, 3)

        return widget
    
    def _save_configuration(self, button):
        """Saves entered directory to the `exporter-settings` file."""
        buffer = self._configure_directory_textfield.get_buffer()
        rawInput = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), False)

        #: strips end slash from input if it exists
        path = rawInput[:-1] if rawInput[-1] == '/' else rawInput

        #: saves path key, path value pair to settings
        Settings.save(SAVE_PATH_KEY, path)

    def _insert_menu(self):
        """Inserts custom menu items into `File` menu."""
        manager = self.window.get_ui_manager()

        self._action_group = Gtk.ActionGroup("MarkdownExporterPluginActions")
        self._action_group.add_actions([("MarkdownExporterMenu", None, _("Exporter")),
                                        ("MarkdownExporterPDF", None, _("Export as PDF"),
                                         None, _("Export Markdown file to PDF"),
                                         self._export_to_pdf),
                                        ("MarkdownExporterHTML", None, _("Export as HTML"),
                                         None, _("Export Markdown file to HTML"),
                                         self._export_to_html)])

        manager.insert_action_group(self._action_group, -1)
        self._ui_id = manager.add_ui_from_string(ui_str)

    def _remove_menu(self):
        """Removes custom menu items from `File` menu."""
        manager = self.window.get_ui_manager()
        manager.remove_ui(self._ui_id)
        manager.remove_action_group(self._action_group)
        manager.ensure_update()

    def _export_to_pdf(self, action):
        """
        Menu activation handler for PDF export.

        Uses `markdown` process from the `discount` package to convert a
        markdown file to HTML.

        Also uses `lowriter` (from libreoffice) in headless mode to convert HTML
        to PDF.
        """
        doc = self.window.get_active_document()
        if not doc:
            print("[Export to PDF] No document.")
            return

        #: writes current document to temporary file and grabs the path of file
        path = self._write_temp_file(doc)
        fileName = os.path.splitext(path)[0]

        #: writes markdown file to HTML
        htmlPath = fileName + ".html"
        self._write_exported_html_file(["markdown", path], htmlPath)

        #: writes HTML file to PDF
        subprocess.call(["lowriter", "--headless", "--convert-to", "pdf", "--outdir", Settings.get_value(SAVE_PATH_KEY, SAVE_PATH_DEFAULT), htmlPath])

        #: cleans up the temporary files
        os.remove(path)
        os.remove(htmlPath)

    def _export_to_html(self, action):
        """
        Menu activation handler for HTMl export.

        Uses `markdown` process from the `discount` package to convert a
        markdown file to HTML.
        """
        doc = self.window.get_active_document()
        if not doc:
            print("[Export to HTML] No document.")
            return

        #: writes current document to temporary file and grabs the path of file
        path = self._write_temp_file(doc)

        #: writes markdown file to HTML
        exportFile = Settings.get_value(SAVE_PATH_KEY, SAVE_PATH_DEFAULT) + "/" + os.path.splitext(os.path.basename(path))[0] + ".html"
        self._write_exported_html_file(["markdown", path], exportFile)

        #: cleans up temporary file
        os.remove(path)

    def _write_temp_file(self, doc):
        """
        Writes temporary file text to markdown. 
        Uses `mkstemp` to create the temporary file.

        Args:
            doc (Pluma.Document): Current working document.
        
        Returns:
            string: Path to temporary document.
        """
        #: get text from current working document
        text = doc.get_text(doc.get_start_iter(), doc.get_end_iter(), True)

        #: write text to temporary file
        fd, path = mkstemp(suffix=".md")
        with open(path, "w") as tempFD:
            tempFD.write(text)
        
        #: closes file descriptor
        os.close(fd)
        return path

    def _write_exported_html_file(self, converterArgs, outputPath):
        """
        Converts file to HTML using the `markdown` command from the `discount`
        package and writes it to the specified `outputPath`.

        Args:
            outputPath (string): Path where the HTML file should be written.
        """
        f = open(outputPath, "w")

        #: calls `markdown` process
        p = subprocess.Popen(converterArgs, stdout=subprocess.PIPE)

        #: writes the results of the conversion to a file
        f.write(p.communicate()[0])

        #: closes file descriptor
        f.close()
