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

import org.rstudio.core.client.StringUtil;
import org.rstudio.studio.client.workbench.views.source.editors.text.ace.Position;
import org.rstudio.studio.client.workbench.views.source.editors.text.ace.Range;
import org.rstudio.studio.client.workbench.views.source.editors.text.events.CursorChangedEvent;
import org.rstudio.studio.client.workbench.views.source.editors.text.events.CursorChangedHandler;

import com.google.gwt.event.shared.HandlerRegistration;

public class AceEditorEditLinesHelper implements CursorChangedHandler
{
   public AceEditorEditLinesHelper(AceEditor editor)
   {
      editor_ = editor;
   }
   
   public void editLinesFromStart()
   {
      int state = state_;
      state_ = (state_ + 1) % 4;
      
      if (cursorChangedHandler_ == null)
         cursorChangedHandler_ = editor_.addCursorChangedHandler(this);
      
      if (range_ == null)
         range_ = getSelectionExtent();
      
      isPerformingEditLinesAction_ = true;
      
      switch (state)
      {
      
      case 0:
         editLinesFromStart(false, false);
         break;
         
      case 1:
         editLinesFromStart(false, true);
         break;
         
      case 2:
         editLinesFromStart(true, false);
         break;
         
      case 3:
         editLinesFromStart(true, true);
         break;
         
      }
      
      isPerformingEditLinesAction_ = false;
   }
   
   private void editLinesFromStart(boolean ignoreBlankLines,
                                   boolean useCommonIndent)
   {
      if (editor_.inMultiSelectMode())
         editor_.exitMultiSelectMode();
      
      int startRow = range_.getStart().getRow();
      int endRow = range_.getEnd().getRow();
      
      if (ignoreBlankLines)
      {
         for (; startRow <= endRow; startRow++)
         {
            String line = editor_.getLine(startRow);
            if (line.trim().isEmpty())
               continue;

            break;
         }

         // Check to see that we found a non-blank line
         String line = editor_.getLine(startRow);
         if (line.trim().isEmpty())
            return;
      }
      
      String line = editor_.getLine(startRow);
      String indent = StringUtil.getIndent(line);
      int indentSize = indent.length();
      
      if (useCommonIndent)
      {
         for (int row = startRow + 1; row <= endRow; row++)
         {
            line = editor_.getLine(row);
            if (ignoreBlankLines && line.trim().isEmpty())
               continue;

            indent = StringUtil.getIndent(line);
            indentSize = Math.min(indentSize, indent.length());
         }
      }
      
      // Place first cursor at requested location
      editor_.setCursorPosition(Position.create(startRow, indentSize));
      
      // Add new cursors for items in range
      for (int row = startRow + 1; row <= endRow; row++)
      {
         line = editor_.getLine(row);
         if (ignoreBlankLines && line.trim().isEmpty())
            continue;
         
         if (!useCommonIndent)
         {
            indent = StringUtil.getIndent(line);
            indentSize = indent.length();
         }
         
         Range cursorRange = Range.create(row, indentSize, row, indentSize);
         editor_.getNativeSelection().addRange(cursorRange, true);
      }
   }
   
   private Range getSelectionExtent()
   {
      int startRow = editor_.getRowCount();
      int endRow   = 0;
      
      for (Range range : editor_.getNativeSelection().getAllRanges())
      {
         startRow = Math.min(startRow, range.getStart().getRow());
         endRow   = Math.max(endRow, range.getEnd().getRow());
      }
      
      return Range.create(startRow, 0, endRow, 0);
      
   }
   
   @Override
   public void onCursorChanged(CursorChangedEvent event)
   {
      if (!isPerformingEditLinesAction_)
      {
         state_ = 0;
         range_ = null;
         cursorChangedHandler_.removeHandler();
         cursorChangedHandler_ = null;
      }
   }
   
   private final AceEditor editor_;
   private HandlerRegistration cursorChangedHandler_;
   private Range range_ = null;
   private int state_ = 0;
   private boolean isPerformingEditLinesAction_;
}

@@ -660,16 +660,54 @@
 .text;%0A%0A
+import org.rstudio.core.client.Debug;%0A
 import o
@@ -1611,32 +1611,53 @@
 xtent();%0A      %0A
+      try%0A      %7B%0A   
       isPerformi
@@ -1696,16 +1696,19 @@
  %0A      
+   
 switch (
@@ -1724,17 +1724,17 @@
     
-%7B%0A
    
+%7B%0A%0A
    
-%0A
     
@@ -1743,16 +1743,19 @@
 case 0:%0A
+   
         
@@ -1798,32 +1798,35 @@
         
+   
 break;%0A
- 
+%0A
         
 %0A      c
@@ -1813,30 +1813,24 @@
 k;%0A%0A        
-%0A     
  case 1:%0A   
@@ -1826,16 +1826,19 @@
 case 1:%0A
+   
         
@@ -1872,39 +1872,43 @@
 true);%0A         
+   
 break;%0A
+%0A
          %0A      
@@ -1904,23 +1904,16 @@
     
-%0A      
 case 2:%0A
     
@@ -1908,16 +1908,19 @@
 case 2:%0A
+   
         
@@ -1945,32 +1945,35 @@
 t(true, false);%0A
+   
          break;%0A
@@ -1968,34 +1968,28 @@
      break;%0A
-      
+%0A
    
-%0A
       case 3
@@ -1990,16 +1990,19 @@
 case 3:%0A
+   
         
@@ -2035,39 +2035,43 @@
 true);%0A         
+ 
+  
 break;%0A
+%0A
          %0A      
@@ -2059,24 +2059,25 @@
 ;%0A%0A         
+%7D
 %0A      %7D%0A   
@@ -2076,24 +2076,130 @@
    %7D%0A      %0A
+      catch (Exception e)%0A      %7B%0A         Debug.logException(e);%0A      %7D%0A      %0A      finally%0A      %7B%0A   
       isPerf
@@ -2230,16 +2230,31 @@
  false;%0A
+      %7D%0A      %0A
    %7D%0A   

use try-catch to guard against exceptions