/*
 * ProjectTemplateWidget.java
 *
 * Copyright (C) 2022 by Posit Software, PBC
 *
 * Unless you have received this program directly from Posit Software pursuant
 * to the terms of a commercial license agreement with Posit Software, 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.projects.model;

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

import org.rstudio.core.client.Debug;
import org.rstudio.core.client.ElementIds;
import org.rstudio.core.client.StringUtil;
import org.rstudio.core.client.dom.DomUtils;
import org.rstudio.core.client.js.JsObject;
import org.rstudio.core.client.widget.FileChooserTextBox;
import org.rstudio.core.client.widget.LayoutGrid;
import org.rstudio.core.client.widget.SelectWidget;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

public class ProjectTemplateWidget extends Composite
{
   private interface Collector
   {
      public void collectInput(JsObject receiver);
   }
   
   private static class ProjectTemplateWidgetItem extends Composite
   {
      public ProjectTemplateWidgetItem(Widget widget, Collector collector)
      {
         collector_ = collector;
         initWidget(widget);
      }
      
      public void collectInput(JsObject receiver)
      {
         if (collector_ != null)
            collector_.collectInput(receiver);
      }
      
      private final Collector collector_;
   }
   
   public ProjectTemplateWidget(ProjectTemplateDescription description)
   {
      widgets_ = new ArrayList<>();
      
      // initialize widgets
      JsArray<ProjectTemplateWidgetDescription> descriptions = description.getWidgetDescription();
      int n = descriptions.length();
      for (int i = 0; i < n; i++)
         widgets_.add(createWidget(descriptions.get(i)));
      
      // initialize panel
      VerticalPanel leftPanel = new VerticalPanel();
      VerticalPanel rightPanel = new VerticalPanel();
      for (int i = 0; i < n; i++)
      {
         String position = StringUtil.notNull(descriptions.get(i).getPosition());
         Widget widget = widgets_.get(i);
         if ("right".equals(position.toLowerCase()))
            rightPanel.add(widget);
         else
            leftPanel.add(widget);
      }
      
      int nLeft = leftPanel.getWidgetCount();
      int nRight = rightPanel.getWidgetCount();
      
      Widget primaryWidget;
      
      if (nLeft == 0 && nRight == 0)
         primaryWidget = new FlowPanel();
      else if (nRight == 0)
         primaryWidget = leftPanel;
      else if (nLeft == 0)
         primaryWidget = rightPanel;
      else
      {
         // both sides have widgets -- populate appropriately
         HorizontalPanel panel = new HorizontalPanel();
         panel.add(leftPanel);
         panel.add(rightPanel);
         primaryWidget = panel;
      }
      
      primaryWidget.setWidth("100%");
      initWidget(primaryWidget);
   }
   
   public JsObject collectInput()
   {
      JsObject object = JsObject.createJsObject();
      for (ProjectTemplateWidgetItem widget : widgets_)
         widget.collectInput(object);
      return object;
   }
   
   private ProjectTemplateWidgetItem createWidget(ProjectTemplateWidgetDescription description)
   {
      String type = description.getType().toLowerCase();
      if (type.equals(TYPE_CHECKBOX_INPUT))
         return checkBoxInput(description);
      else if (type.equals(TYPE_SELECT_INPUT))
         return selectBoxInput(description);
      else if (type.equals(TYPE_TEXT_INPUT))
         return textInput(description);
      else if (type.equals(TYPE_FILE_INPUT))
         return fileInput(description);
      
      Debug.log("Unexpected widget type '" + type + "'");
      return new ProjectTemplateWidgetItem(new FlowPanel(), null);
   }
   
   private ProjectTemplateWidgetItem checkBoxInput(final ProjectTemplateWidgetDescription description)
   {
      final CheckBox widget = new CheckBox(description.getLabel());
      
      // set default value
      String defaultValue = description.getDefault();
      if (!StringUtil.isNullOrEmpty(defaultValue))
         widget.setValue(isTruthy(defaultValue));
      
      return new ProjectTemplateWidgetItem(widget, new Collector()
      {
         @Override
         public void collectInput(JsObject receiver)
         {
            boolean value = widget.getValue();
            receiver.setBoolean(description.getParameter(), value);
         }
      });
   }
   
   private ProjectTemplateWidgetItem selectBoxInput(final ProjectTemplateWidgetDescription description)
   {
      String[] fields = readSelectBoxFields(description);
      String label = ensureEndsWithColon(description.getLabel());
      final SelectWidget widget = new SelectWidget(label, fields);
      
      String defaultValue = description.getDefault();
      if (!StringUtil.isNullOrEmpty(defaultValue))
         widget.setValue(defaultValue);
      
      return new ProjectTemplateWidgetItem(widget, new Collector()
      {
         @Override
         public void collectInput(JsObject receiver)
         {
            String value = widget.getValue();
            receiver.setString(description.getParameter(), value);
         }
      });
   }
   
   private ProjectTemplateWidgetItem textInput(final ProjectTemplateWidgetDescription description)
   {
      final TextBox primaryWidget = new TextBox();
      primaryWidget.setWidth("180px");
      
      String defaultValue = description.getDefault();
      if (!StringUtil.isNullOrEmpty(defaultValue))
         primaryWidget.setText(defaultValue);
      
      LayoutGrid grid = new LayoutGrid(1, 2);
      DomUtils.disableSpellcheck(primaryWidget);
      grid.setWidget(0, 0, new Label(ensureEndsWithColon(description.getLabel())));
      grid.setWidget(0, 1, primaryWidget);
      
      return new ProjectTemplateWidgetItem(grid, new Collector()
      {
         @Override
         public void collectInput(JsObject receiver)
         {
            String value = primaryWidget.getValue();
            receiver.setString(description.getParameter(), value);
         }
      });
   }
   
   private ProjectTemplateWidgetItem fileInput(final ProjectTemplateWidgetDescription description)
   {
      final FileChooserTextBox widget = new FileChooserTextBox(description.getLabel(), "",
         ElementIds.TextBoxButtonId.PROJECT_TEMPLATE, false, null, null);
      
      String defaultValue = description.getDefault();
      if (!StringUtil.isNullOrEmpty(defaultValue))
         widget.setText(defaultValue);
      
      return new ProjectTemplateWidgetItem(widget, new Collector()
      {
         @Override
         public void collectInput(JsObject receiver)
         {
            String value = widget.getText();
            receiver.setString(description.getParameter(), value);
         }
      });
   }
   
   private String[] readSelectBoxFields(ProjectTemplateWidgetDescription description)
   {
      JsArrayString jsFields = description.getFields();
      int n = jsFields.length();
      
      String[] fields = new String[n];
      for (int i = 0; i < n; i++)
         fields[i] = jsFields.get(i);
      return fields;
   }
   
   private String ensureEndsWithColon(String string)
   {
      return string.endsWith(":")
            ? string
            : string + ":";
   }
   
   private boolean isTruthy(String value)
   {
      String lower = value.toLowerCase();
      return
            lower.equals("true") ||
            lower.equals("yes") ||
            lower.equals("on") ||
            lower.equals("1");
   }
   
   private final List<ProjectTemplateWidgetItem> widgets_;
   
   public static final String TYPE_CHECKBOX_INPUT = "checkboxinput";
   public static final String TYPE_SELECT_INPUT   = "selectinput";
   public static final String TYPE_FILE_INPUT     = "fileinput";
   public static final String TYPE_TEXT_INPUT     = "textinput";
   
}
