/*
 * ConsoleDispatcher.java
 *
 * Copyright (C) 2009-12 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.common;

import org.rstudio.core.client.StringUtil;
import org.rstudio.core.client.files.FileSystemItem;
import org.rstudio.core.client.widget.ProgressIndicator;
import org.rstudio.core.client.widget.ProgressOperationWithInput;
import org.rstudio.studio.client.application.events.EventBus;
import org.rstudio.studio.client.common.filetypes.TextFileType;
import org.rstudio.studio.client.workbench.WorkbenchContext;
import org.rstudio.studio.client.workbench.commands.Commands;
import org.rstudio.studio.client.workbench.model.RemoteFileSystemContext;
import org.rstudio.studio.client.workbench.model.Session;
import org.rstudio.studio.client.workbench.views.console.events.SendToConsoleEvent;

import com.google.inject.Inject;
import com.google.inject.Singleton;


@Singleton
public class ConsoleDispatcher
{
   @Inject
   public ConsoleDispatcher(EventBus eventBus,
                            Commands commands,
                            FileDialogs fileDialogs,
                            WorkbenchContext workbenchContext,
                            Session session,
                            RemoteFileSystemContext fsContext)
   {
      eventBus_ = eventBus;
      commands_ = commands;
      fileDialogs_ = fileDialogs;
      workbenchContext_ = workbenchContext;
      session_ = session;
      fsContext_ = fsContext;
   }

   public void executeSetWd(FileSystemItem dir, boolean activateConsole)
   {
      String escaped = dir.getPath().replaceAll("\\\\", "\\\\\\\\");
      if (escaped.equals("~"))
         escaped = "~/";
      eventBus_.fireEvent(
            new SendToConsoleEvent("setwd(\"" + escaped + "\")", true));
      
      if (activateConsole)
         commands_.activateConsole().execute();
   }
   
   public void executeCommand(String command, FileSystemItem targetFile)
   {
      executeCommand(command, targetFile.getPath());
   }
   
   public void executeCommand(String command, String argument)
   {
      String code = command + "(\"" + argument + "\")";
      eventBus_.fireEvent(new SendToConsoleEvent(code, true));
   }
   
   public void executeCommandWithFileEncoding(String command,
                                              String path,
                                              String encoding,
                                              boolean contentIsAscii)
   {
      String escapedPath = escapedPath(path);
      String systemEncoding = session_.getSessionInfo().getSystemEncoding();
      boolean isSystemEncoding =
       normalizeEncoding(encoding).equals(normalizeEncoding(systemEncoding));
      
      StringBuilder code = new StringBuilder();
      code.append(command + "(" + escapedPath);
      
      if (!isSystemEncoding && !contentIsAscii)
      {
         code.append(", encoding = '" +
               (!StringUtil.isNullOrEmpty(encoding) ? encoding : "UTF-8") +
               "'");
      }
      code.append(")");
      eventBus_.fireEvent(new SendToConsoleEvent(code.toString(), true));
   }
   
   
   public void saveFileAsThenExecuteCommand(String caption,
                                            final String defaultExtension,
                                            boolean forceExtension,
                                            final String command)
   {
      fileDialogs_.saveFile(
            caption,
            fsContext_,
            workbenchContext_.getCurrentWorkingDir(),
            defaultExtension,
            forceExtension,
            new ProgressOperationWithInput<FileSystemItem>()
            {
               public void execute(
                     FileSystemItem input,
                     ProgressIndicator indicator)
               {
                  if (input == null)
                     return;
                  
                  executeCommand(command, input);
                  indicator.onCompleted();
               }
            });
   }
   
   public void chooseFileThenExecuteCommand(String caption, 
                                            final String command)
   {
      fileDialogs_.openFile(
            caption,
            fsContext_,
            workbenchContext_.getCurrentWorkingDir(),
            new ProgressOperationWithInput<FileSystemItem>()
            {
               public void execute(FileSystemItem input, ProgressIndicator indicator)
               {
                  if (input == null)
                     return;
                  
                  executeCommand(command, input);
                  indicator.onCompleted();
               }
            });
      
   }
  
   
   public void executeSourceCommand(String path,
                                    TextFileType fileType,
                                    String encoding, 
                                    boolean contentKnownToBeAscii,
                                    boolean echo,
                                    boolean focus,
                                    boolean debug)
   {
       
      StringBuilder code = new StringBuilder();
      
      if (fileType.isCpp())
      {
         // use a relative path if possible
         String relativePath = FileSystemItem.createFile(path).getPathRelativeTo(
                                       workbenchContext_.getCurrentWorkingDir());
         if (relativePath != null)
            path = relativePath;
       
         code.append("Rcpp::sourceCpp(" + escapedPath(path) + ")");
      }
      else
      {
         String escapedPath = escapedPath(path);
         String systemEncoding = session_.getSessionInfo().getSystemEncoding();
         boolean isSystemEncoding =
          normalizeEncoding(encoding).equals(normalizeEncoding(systemEncoding));
         
         if (contentKnownToBeAscii || isSystemEncoding)
            code.append((debug ? "debugSource" : "source") + 
                     "(" + escapedPath);
         else
         {
            code.append((debug ? "debugSource" : "source") + 
                  "(" + escapedPath + ", encoding = '" +
                  (!StringUtil.isNullOrEmpty(encoding) ? encoding : "UTF-8") +
                  "'");
         }
         
         if (echo)
            code.append(", echo=TRUE");
         code.append(")");
      }
      
      
      eventBus_.fireEvent(new SendToConsoleEvent(code.toString(), true));
      
      if (focus)
         commands_.activateConsole().execute();
   }
   
   private String escapedPath(String path)
   {
      String escapedPath = "'" +
                           path.replace("\\", "\\\\").replace("'", "\\'") +
                           "'";
      return escapedPath;
   }
   
   private String normalizeEncoding(String str)
   {
      return StringUtil.notNull(str).replaceAll("[- ]", "").toLowerCase();
   }
   
   
   private final EventBus eventBus_;
   private final Commands commands_;
   private final FileDialogs fileDialogs_;
   private final WorkbenchContext workbenchContext_;
   private final Session session_;
   private final RemoteFileSystemContext fsContext_;
}
