/*
 * NewRMarkdownDialog.java
 *
 * Copyright (C) 2009-14 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */
package org.rstudio.studio.client.workbench.views.source.editors.text.ui;

import java.util.ArrayList;
import java.util.List;

import org.rstudio.core.client.files.FileSystemItem;
import org.rstudio.core.client.widget.ModalDialog;
import org.rstudio.core.client.widget.OperationWithInput;
import org.rstudio.core.client.widget.WidgetListBox;
import org.rstudio.studio.client.common.HelpLink;
import org.rstudio.studio.client.rmarkdown.model.RMarkdownContext;
import org.rstudio.studio.client.rmarkdown.model.RmdChosenTemplate;
import org.rstudio.studio.client.rmarkdown.model.RmdFrontMatter;
import org.rstudio.studio.client.rmarkdown.model.RmdTemplate;
import org.rstudio.studio.client.rmarkdown.model.RmdTemplateData;
import org.rstudio.studio.client.rmarkdown.model.RmdTemplateFormat;
import org.rstudio.studio.client.rmarkdown.ui.RmdTemplateChooser;
import org.rstudio.studio.client.workbench.WorkbenchContext;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiFactory;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;

public class NewRMarkdownDialog extends ModalDialog<NewRMarkdownDialog.Result>
{
   public static class RmdNewDocument
   {  
      public RmdNewDocument(String template, String author, String title, 
                            String format, boolean isShiny)
      {
         template_ = template;
         title_ = title;
         author_ = author;
         isShiny_ = isShiny;
         format_ = format;
         result_ = toJSO(author, title, format, isShiny);
      }
      
      public String getTemplate()
      {
         return template_;
      }
      
      public String getAuthor()
      {
         return author_;
      }
      
      public String getTitle()
      {
         return title_;
      }
      
      public boolean isShiny()
      {
         return isShiny_;
      }
      
      public String getFormat()
      {
         return format_;
      }
      
      public JavaScriptObject getJSOResult()
      {
         return result_;
      }
      
      private final JavaScriptObject toJSO(String author, 
                                           String title, 
                                           String format, 
                                           boolean isShiny)
      {
         RmdFrontMatter result = RmdFrontMatter.create();
         result.applyCreateOptions(author, title, format, isShiny);
         return result;
      }

      private final String template_;
      private final String author_;
      private final String title_;
      private final boolean isShiny_;
      private final String format_;
      private final JavaScriptObject result_;
   }
   
   public static class Result
   {
      public Result (RmdNewDocument newDocument, RmdChosenTemplate fromTemplate,
                     boolean isNewDocument)
      {
         newDocument_ = newDocument;
         fromTemplate_ = fromTemplate;
         isNewDocument_ = isNewDocument;
      }
      
      public RmdNewDocument getNewDocument()
      {
         return newDocument_;
      }
      
      public RmdChosenTemplate getFromTemplate()
      {
         return fromTemplate_;
      }
      
      public boolean isNewDocument()
      {
         return isNewDocument_;
      }
      
      private final RmdNewDocument newDocument_;
      private final RmdChosenTemplate fromTemplate_;
      private final boolean isNewDocument_;
   }

   public interface Binder extends UiBinder<Widget, NewRMarkdownDialog>
   {
   }
   
   public interface NewRmdStyle extends CssResource
   {
      String outputFormat();
      String outputFormatName();
      String outputFormatChoice();
      String outputFormatDetails();
      String outputFormatIcon();
   }

   public interface Resources extends ClientBundle
   {
      @Source("MarkdownPresentationIcon.png")
      ImageResource presentationIcon();

      @Source("MarkdownDocumentIcon.png")
      ImageResource documentIcon();

      @Source("MarkdownOptionsIcon.png")
      ImageResource optionsIcon();
      
      @Source("MarkdownTemplateIcon.png")
      ImageResource templateIcon();

      @Source("MarkdownShinyIcon.png")
      ImageResource shinyIcon();
   }

   public NewRMarkdownDialog(
         RMarkdownContext context,
         WorkbenchContext workbench,
         String author,
         OperationWithInput<Result> operation)
   {
      super("New R Markdown", operation);
      context_ = context;
      mainWidget_ = GWT.<Binder>create(Binder.class).createAndBindUi(this);
      formatOptions_ = new ArrayList<RadioButton>();
      style.ensureInjected();
      txtAuthor_.setText(author);
      txtTitle_.setText("Untitled");
      listTemplates_.addChangeHandler(new ChangeHandler()
      {
         @Override
         public void onChange(ChangeEvent event)
         {
            updateOptions(getSelectedTemplate());
            txtTitle_.setFocus(true);
         }
      });

      templates_ = RmdTemplateData.getTemplates();
      for (int i = 0; i < templates_.length(); i++)
      {
         String templateName = templates_.get(i).getName();
         TemplateMenuItem menuItem = new TemplateMenuItem(templateName);
         
         ImageResource img = null;

         // Special treatment for built-in templates with known names
         if (templateName.equals(RmdTemplateData.DOCUMENT_TEMPLATE))
         {
            img = resources.documentIcon();
         } 
         else if (templateName.equals(RmdTemplateData.PRESENTATION_TEMPLATE))
         {
            img = resources.presentationIcon();
         }

         // Add an image if we have one
         if (img != null)
         {
            menuItem.addIcon(img);
         }

         listTemplates_.addItem(menuItem);
      }
      
      // Add the Shiny template
      TemplateMenuItem shinyItem = new TemplateMenuItem(TEMPLATE_SHINY);
      shinyItem.addIcon(resources.shinyIcon());
      listTemplates_.addItem(shinyItem);
       
      // Add the "From Template" item at the end of the list
      TemplateMenuItem templateItem = 
            new TemplateMenuItem(TEMPLATE_CHOOSE_EXISTING);
      templateItem.addIcon(resources.templateIcon());
      listTemplates_.addItem(templateItem);
      
      // Save templates to the current project directory if available, and the
      // current working directory if not
      FileSystemItem dir = workbench.getActiveProjectDir();
      if (dir == null)
         dir = workbench.getCurrentWorkingDir();
      templateChooser_.setTargetDirectory(dir.getPath());

      updateOptions(getSelectedTemplate());
   }
   
   @UiFactory
   public HelpLink makeHelpCaption()
   {
      return new HelpLink("Using Shiny with R Markdown",
                          "using_rmarkdown_shiny");
   }
   
   @Override
   protected void onDialogShown()
   {
      // when dialog is finished booting, focus the title so it's ready to
      // accept input
      super.onDialogShown();
      txtTitle_.setSelectionRange(0, txtTitle_.getText().length());
      txtTitle_.setFocus(true);
   }

   @Override
   protected Result collectInput()
   {
      String formatName = "";
      boolean isShiny = getSelectedTemplate().equals(TEMPLATE_SHINY);
      for (int i = 0; i < formatOptions_.size(); i++)
      {
         if (formatOptions_.get(i).getValue())
         {
            // for Shiny documents, manually choose the underlying format to
            // represent the document
            if (isShiny)
            {
               String option = formatOptions_.get(i).getText();
               if (option.equals(SHINY_DOC_NAME))
                  formatName = "html_document";
               else if (option.equals(SHINY_PRESENTATION_NAME))
                  formatName = "ioslides_presentation";
            }
            // for other documents, read the format from the template
            else
            {
               formatName = currentTemplate_.getFormats().get(i).getName();
            }
            break;
         }
      }
      return new Result(
            new RmdNewDocument(getSelectedTemplate(), 
                               txtAuthor_.getText().trim(), 
                               txtTitle_.getText().trim(), 
                               formatName,
                               isShiny),
            templateChooser_.getChosenTemplate(),
            !getSelectedTemplate().equals(TEMPLATE_CHOOSE_EXISTING));
   }

   @Override
   protected boolean validate(Result input)
   {
      // the dialog isn't valid if the user's chosen to create a document from
      // a template but hasn't chosen a template. 
      if (input.isNewDocument() || 
          input.getFromTemplate().getTemplatePath() != null)
         return true;
      return false;
   }

   @Override
   protected Widget createMainWidget()
   {
      return mainWidget_;
   }
   
   private String getSelectedTemplate() 
   {
      int idx = listTemplates_.getSelectedIndex();
      TemplateMenuItem item = listTemplates_.getItemAtIdx(idx);
      if (item.getName().equals(TEMPLATE_CHOOSE_EXISTING) ||
          item.getName().equals(TEMPLATE_SHINY))
         return item.getName();
      else
         return templates_.get(idx).getName();
   }
   
   private void updateOptions(String selectedTemplate)
   {
      boolean existing = selectedTemplate.equals(TEMPLATE_CHOOSE_EXISTING);
      boolean shiny = selectedTemplate.equals(TEMPLATE_SHINY);

      // toggle visibility of UI elements based on which section of the dialog
      // we're in 
      newTemplatePanel_.setVisible(!existing);
      existingTemplatePanel_.setVisible(existing);
      shinyInfoPanel_.setVisible(shiny);
      
      if (existing)
      {
         if (templateChooser_.getState() == RmdTemplateChooser.STATE_EMPTY)
         {
            populateTemplates();
         }
         return;
      }
      
      templateFormatPanel_.clear();
      formatOptions_.clear();
      
      if (shiny)
      {
         templateFormatPanel_.add(createFormatOption(SHINY_DOC_NAME, 
               "Create an HTML document with interactive Shiny components."));
         templateFormatPanel_.add(createFormatOption(SHINY_PRESENTATION_NAME, 
               "Create an IOSlides presentation with interactive Shiny components."));
      }
      else 
      {
         
         currentTemplate_ = RmdTemplate.getTemplate(templates_,
                                                    selectedTemplate);
         if (currentTemplate_ == null)
            return;
         
         // Add each format to the dialog
         JsArray<RmdTemplateFormat> formats = currentTemplate_.getFormats();
         for (int i = 0; i < formats.length(); i++)
         {
            templateFormatPanel_.add(createFormatOption(formats.get(i)));
         }
      }
         
      // Select the first format by default
      if (formatOptions_.size() > 0)
         formatOptions_.get(0).setValue(true);
   }
   
   private void populateTemplates()
   {
      templateChooser_.populateTemplates();
   }

   private Widget createFormatOption(RmdTemplateFormat format)
   {
      return createFormatOption(format.getUiName(), format.getNotes());
   }
   
   private Widget createFormatOption(String name, String description)
   {
      HTMLPanel formatWrapper = new HTMLPanel("");
      formatWrapper.setStyleName(style.outputFormat());
      SafeHtmlBuilder sb = new SafeHtmlBuilder();
      sb.appendHtmlConstant("<span class=\"" + style.outputFormatName() + 
                            "\">");
      sb.appendEscaped(name);
      sb.appendHtmlConstant("</span>");
      RadioButton button = new RadioButton("DefaultOutputFormat", 
                                           sb.toSafeHtml().asString(), true);
      button.addStyleName(style.outputFormatChoice());
      formatOptions_.add(button);
      formatWrapper.add(button);
      Label label = new Label(description);
      label.setStyleName(style.outputFormatDetails());
      formatWrapper.add(label);
      return formatWrapper;
   }
   
   @UiField TextBox txtAuthor_;
   @UiField TextBox txtTitle_;
   @UiField WidgetListBox<TemplateMenuItem> listTemplates_;
   @UiField NewRmdStyle style;
   @UiField Resources resources;
   @UiField HTMLPanel templateFormatPanel_;
   @UiField HTMLPanel newTemplatePanel_;
   @UiField HTMLPanel existingTemplatePanel_;
   @UiField RmdTemplateChooser templateChooser_;
   @UiField HTMLPanel shinyInfoPanel_;
   @UiField Label outputFormatLabel_;

   private final Widget mainWidget_;
   private List<RadioButton> formatOptions_;
   private JsArray<RmdTemplate> templates_;
   private RmdTemplate currentTemplate_;

   @SuppressWarnings("unused")
   private final RMarkdownContext context_;
   
   private final static String TEMPLATE_CHOOSE_EXISTING = "From Template";
   private final static String TEMPLATE_SHINY = "Shiny";
   private final static String SHINY_DOC_NAME = "Shiny Document";
   private final static String SHINY_PRESENTATION_NAME = "Shiny Presentation";
}
