/*
 * ShowAddinsDialog.java
 *
 * Copyright (C) 2009-15 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;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.cellview.client.ColumnSortEvent;
import com.google.gwt.user.cellview.client.DataGrid;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.cellview.client.TextHeader;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.ProvidesKey;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SingleSelectionModel;
import com.google.inject.Inject;

import org.rstudio.core.client.ListUtil;
import org.rstudio.core.client.StringUtil;
import org.rstudio.core.client.ListUtil.FilterPredicate;
import org.rstudio.core.client.js.JsUtil;
import org.rstudio.core.client.theme.RStudioDataGridResources;
import org.rstudio.core.client.theme.RStudioDataGridStyle;
import org.rstudio.core.client.widget.FilterWidget;
import org.rstudio.core.client.widget.ModalDialog;
import org.rstudio.core.client.widget.ModifyKeyboardShortcutsWidget;
import org.rstudio.core.client.widget.OperationWithInput;
import org.rstudio.core.client.widget.ThemedButton;
import org.rstudio.studio.client.RStudioGinjector;
import org.rstudio.studio.client.common.HelpLink;
import org.rstudio.studio.client.workbench.addins.Addins.AddinExecutor;
import org.rstudio.studio.client.workbench.addins.Addins.RAddin;
import org.rstudio.studio.client.workbench.addins.Addins.RAddins;
import org.rstudio.studio.client.workbench.addins.AddinsCommandManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class BrowseAddinsDialog extends ModalDialog<Command>
{
   public BrowseAddinsDialog(OperationWithInput<Command> operation)
   {
      super("Addins", operation);
      RStudioGinjector.INSTANCE.injectMembers(this);
      
      setOkButtonCaption("Execute");
      
      filterWidget_ = new FilterWidget()
      {
         @Override
         public void filter(String query)
         {
            BrowseAddinsDialog.this.filter(query);
         }
      };
      filterWidget_.getElement().getStyle().setFloat(Style.Float.LEFT);
      
      helpLink_ = new HelpLink("Using RStudio Addins", "rstudio_addins", false);
      helpLink_.getElement().getStyle().setFloat(Style.Float.RIGHT);
      
      keyProvider_ = new ProvidesKey<RAddin>()
      {
         @Override
         public Object getKey(RAddin addin)
         {
            return addin.hashCode();
         }
      };
      
      table_ = new DataGrid<RAddin>(1000, RES, keyProvider_);
      table_.setWidth("500px");
      table_.setHeight("400px");
      
      selectionModel_ = new SingleSelectionModel<RAddin>();
      selectionModel_.addSelectionChangeHandler(new SelectionChangeEvent.Handler()
      {
         @Override
         public void onSelectionChange(SelectionChangeEvent event)
         {
            selection_ = selectionModel_.getSelectedObject();
         }
      });
      
      table_.setSelectionModel(selectionModel_);
      table_.setEmptyTableWidget(emptyTableLabel("Loading addins..."));
      
      addColumns();
      
      dataProvider_ = new ListDataProvider<RAddin>();
      dataProvider_.addDataDisplay(table_);
      
      originalData_ = new ArrayList<RAddin>();
      
      // sync to current addins
      addins_ = addinsCommandManager_.getRAddins();
      List<RAddin> data = new ArrayList<RAddin>();
      for (String key : JsUtil.asIterable(addins_.keys()))
         data.add(addins_.get(key));
      dataProvider_.setList(data);
      originalData_ = data;
      table_.setEmptyTableWidget(emptyTableLabel("No addins available"));
      
      addLeftWidget(new ThemedButton("Keyboard Shortcuts...", new ClickHandler()
      {
         @Override
         public void onClick(ClickEvent event)
         {
            ModifyKeyboardShortcutsWidget widget = new ModifyKeyboardShortcutsWidget("addin");
            widget.showModal();
         }
      }));
      
      FlowPanel headerPanel = new FlowPanel();
      headerPanel.add(filterWidget_);
      headerPanel.add(helpLink_);
      
      container_ = new VerticalPanel();
      container_.add(headerPanel);
      container_.add(new VerticalSeparator("4px"));
      container_.add(table_);
   }
   
   private static class VerticalSeparator extends Composite
   {
      public VerticalSeparator(String size)
      {
         FlowPanel panel = new FlowPanel();
         panel.setHeight(size);
         initWidget(panel);
      }
   }
   
   @Inject
   private void initialize(AddinsCommandManager addinsCommandManager)
   {
      addinsCommandManager_ = addinsCommandManager;
   }
   
   private void addColumns()
   {
      // Package ----
      pkgColumn_ = new TextColumn<RAddin>()
      {
         @Override
         public String getValue(RAddin addin)
         {
            return addin.getPackage();
         }
      };
      pkgColumn_.setSortable(true);
      table_.addColumn(pkgColumn_, new TextHeader("Package"));
      table_.setColumnWidth(pkgColumn_, "120px");
            
      // Name ----
      nameColumn_ = new TextColumn<RAddin>()
      {
         @Override
         public String getValue(RAddin addin)
         {
            return StringUtil.truncate(addin.getName(), 120, "...");
         }
      };
      nameColumn_.setSortable(true);
      table_.addColumn(nameColumn_, new TextHeader("Name"));
      table_.setColumnWidth(nameColumn_, "120px");
      
      // Description ----
      descColumn_ = new TextColumn<RAddin>()
      {
         @Override
         public String getValue(RAddin addin)
         {
            return addin.getDescription();
         }
      };
      descColumn_.setSortable(true);
      table_.addColumn(descColumn_, new TextHeader("Description"));
     
      table_.addColumnSortHandler(new ColumnSortEvent.Handler()
      {
         @Override
         public void onColumnSort(ColumnSortEvent event)
         {
            int index = -1;
            if (event.getColumn().equals(pkgColumn_))
               index = 0;
            else if (event.getColumn().equals(nameColumn_))
               index = 1;
            else if (event.getColumn().equals(descColumn_))
               index = 2;
            
            if (index == -1)
               return;
            
            sort(index, event.isSortAscending());
         }
      });
   }
   
   private void sort(final int index, final boolean forward)
   {
      Collections.sort(dataProvider_.getList(), new Comparator<RAddin>()
      {
         @Override
         public int compare(RAddin o1, RAddin o2)
         {
            String f1 = "";
            String f2 = "";
            
            if (index == 0)
            {
               f1 = o1.getPackage();
               f2 = o2.getPackage();
            }
            else if (index == 1)
            {
               f1 = o1.getName();
               f2 = o2.getName();
            }
            else if (index == 2)
            {
               f1 = o1.getDescription();
               f2 = o2.getDescription();
            }
            
            return forward
                  ? f1.compareTo(f2)
                  : f2.compareTo(f1);
         }
         
      });
   }
   
   private void filter(String query)
   {
      final String[] splat = query.toLowerCase().split("\\s+");
      List<RAddin> data = ListUtil.filter(originalData_, new FilterPredicate<RAddin>()
      {
         @Override
         public boolean test(RAddin object)
         {
            for (String el : splat)
            {
               boolean match =
                     object.getName().toLowerCase().contains(el) ||
                     object.getPackage().toLowerCase().contains(el);
               
               if (!match)
                  return false;
            }
            return true;
         }
      });
      dataProvider_.setList(data);
   }
   
   @Override
   protected Command collectInput()
   {
      if (selection_ == null)
      {
         return new Command()
         {
            @Override
            public void execute()
            {
            }
         };
      }
      
      return new ExecuteAddinCommand(
            addins_.get(selection_.getId()),
            new AddinExecutor());
   }
   
   private static class ExecuteAddinCommand implements Command
   {
      public ExecuteAddinCommand(RAddin addin, AddinExecutor executor)
      {
         addin_ = addin;
         executor_ = executor;
      }
      
      @Override
      public void execute()
      {
         executor_.execute(addin_);
      }
      
      private final RAddin addin_;
      private final AddinExecutor executor_;
   }

   @Override
   protected Widget createMainWidget()
   {
      return container_;
   }
   
   private Label emptyTableLabel(String caption)
   {
      Label label = new Label(caption);
      label.getElement().getStyle().setMarginTop(20, Unit.PX);
      label.getElement().getStyle().setColor("#888");
      return label;
   }
   
   // Private members ----
   private final VerticalPanel container_;
   private final FilterWidget filterWidget_;
   private final HelpLink helpLink_;
   
   private final DataGrid<RAddin> table_;
   private TextColumn<RAddin> pkgColumn_;
   private TextColumn<RAddin> nameColumn_;
   private TextColumn<RAddin> descColumn_;
   
   private final ProvidesKey<RAddin> keyProvider_;
   private final ListDataProvider<RAddin> dataProvider_;
   private final SingleSelectionModel<RAddin> selectionModel_;
   
   private List<RAddin> originalData_;
   private RAddins addins_;
   private RAddin selection_;
   
   // Injected ----
   private AddinsCommandManager addinsCommandManager_;
   
   // Resources, etc ----
   public interface Resources extends RStudioDataGridResources
   {
      @Source({RStudioDataGridStyle.RSTUDIO_DEFAULT_CSS})
      Styles dataGridStyle();
   }
   
   public interface Styles extends RStudioDataGridStyle
   {
   }
   
   private static final Resources RES = GWT.create(Resources.class);
   
   static {
      RES.dataGridStyle().ensureInjected();
   }
   
}
