/*
 * HomeAppletController.java 11 Oct. 2008
 *
 * Sweet Home 3D, Copyright (c) 2008 Emmanuel PUYBARET / eTeks <info@eteks.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package com.eteks.sweethome3d.applet;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.Callable;

import com.eteks.sweethome3d.io.HomeFileRecorder;
import com.eteks.sweethome3d.model.Home;
import com.eteks.sweethome3d.model.HomeApplication;
import com.eteks.sweethome3d.model.HomeRecorder;
import com.eteks.sweethome3d.model.InterruptedRecorderException;
import com.eteks.sweethome3d.model.RecorderException;
import com.eteks.sweethome3d.plugin.HomePluginController;
import com.eteks.sweethome3d.plugin.PluginManager;
import com.eteks.sweethome3d.swing.FileContentManager;
import com.eteks.sweethome3d.swing.SwingTools;
import com.eteks.sweethome3d.viewcontroller.ContentManager;
import com.eteks.sweethome3d.viewcontroller.HomeController;
import com.eteks.sweethome3d.viewcontroller.HomeView;
import com.eteks.sweethome3d.viewcontroller.ThreadedTaskController;
import com.eteks.sweethome3d.viewcontroller.UserPreferencesController;
import com.eteks.sweethome3d.viewcontroller.ViewFactory;

/**
 * Home applet pane controller.
 * @author Emmanuel Puybaret
 */
public class HomeAppletController extends HomePluginController {
  private final Home               home;
  private final HomeApplication    application;
  private final ViewFactory        viewFactory;
  private final ContentManager     contentManager;
  private final boolean            newHomeEnabledByDefault;
  private final boolean            openEnabledByDefault;
  private final boolean            saveEnabledByDefault;
  private final boolean            saveAsEnabledByDefault;
  private final long               homeMaximumLength;
  private HomeView                 homeView;
  
  private static Map<Home, String> importedHomeNames = new WeakHashMap<Home, String>();

  public HomeAppletController(Home home, 
                              HomeApplication application, 
                              ViewFactory     viewFactory,
                              ContentManager  contentManager,
                              PluginManager   pluginManager,
                              boolean newHomeEnabled, 
                              boolean openEnabled, 
                              boolean saveEnabled, 
                              boolean saveAsEnabled) {
    this(home, application, viewFactory, contentManager, pluginManager, newHomeEnabled, openEnabled, saveEnabled, saveAsEnabled, -1);
  }
  
  public HomeAppletController(Home home, 
                              HomeApplication application, 
                              ViewFactory     viewFactory,
                              ContentManager  contentManager,
                              PluginManager   pluginManager,
                              boolean newHomeEnabled, 
                              boolean openEnabled, 
                              boolean saveEnabled, 
                              boolean saveAsEnabled,
                              long    homeMaximumLength) {
    super(home, application, viewFactory, contentManager, pluginManager);
    this.home = home;
    this.application = application;
    this.viewFactory = viewFactory;
    this.contentManager = contentManager;
    this.newHomeEnabledByDefault = newHomeEnabled;
    this.openEnabledByDefault = openEnabled;
    this.saveEnabledByDefault = saveEnabled;
    this.saveAsEnabledByDefault = saveAsEnabled;
    this.homeMaximumLength = homeMaximumLength;
  }
  
  /**
   * Returns the view associated with this controller.
   */
  public HomeView getView() {
    if (this.homeView == null) {
      this.homeView = super.getView();
      
      // Update home view actions only once HomeAppletController instance is initialized in case it's subclassed
      this.homeView.setEnabled(HomeView.ActionType.EXIT, false);
      this.homeView.setEnabled(HomeView.ActionType.NEW_HOME, this.newHomeEnabledByDefault);
      this.homeView.setEnabled(HomeView.ActionType.OPEN, this.openEnabledByDefault);
      this.homeView.setEnabled(HomeView.ActionType.SAVE, this.saveEnabledByDefault);
      this.homeView.setEnabled(HomeView.ActionType.SAVE_AS, this.saveAsEnabledByDefault);
      
      // By default disabled Print to PDF, Export to SVG, Export to OBJ and Create photo actions 
      this.homeView.setEnabled(HomeView.ActionType.PRINT_TO_PDF, false);
      this.homeView.setEnabled(HomeView.ActionType.EXPORT_TO_SVG, false);
      this.homeView.setEnabled(HomeView.ActionType.EXPORT_TO_OBJ, false);
      this.homeView.setEnabled(HomeView.ActionType.CREATE_PHOTO, false);
      
      this.homeView.setEnabled(HomeView.ActionType.DETACH_3D_VIEW, false);
    }
    return this.homeView;
  }

  /**
   * Creates a new home after saving and deleting the current home.
   */
  @Override
  public void newHome() {
    close(new Runnable() {
        public void run() {
          HomeAppletController.super.newHome();
        }
      });
  }

  /**
   * Opens a home after saving and deleting the current home.
   */
  @Override
  public void open() {
    close(new Runnable() {
      public void run() {
        HomeAppletController.super.open();
      }
    });
  }
  
  /**
   * Forces a save as operation for imported homes.
   */
  @Override
  public void save() {
    if (this.home.getName() != null) {
      chekHomeLengthAndSave(new Runnable() {
          public void run() {
            HomeAppletController.super.save();
          }
        });
    } else {
      super.saveAs();
    }
  }
  
  /**
   * Prompts the user to choose a name for the edited home, 
   * suggesting the imported file name after an import.
   */
  @Override
  protected void saveAs(final HomeRecorder.Type recorderType, 
                        final Runnable postSaveTask) {
    chekHomeLengthAndSave(new Runnable() {
        public void run() {
          String homeName = importedHomeNames.get(home);
          if (homeName != null) {
            // Suggest imported home name
            home.setName(homeName);
            HomeAppletController.super.saveAs(recorderType, new Runnable () {
                public void run() {
                  if (postSaveTask != null) {
                    postSaveTask.run();
                  }
                  importedHomeNames.remove(home);
                }
              });
            // Reset name to null in case the user gave up
            home.setName(null);
          } else {
            HomeAppletController.super.saveAs(recorderType, postSaveTask);
          }
        }
      });
  }
  
  /**
   * Checks the length of data and executes <code>saveTask</code> if length is ok. 
   */
  private void chekHomeLengthAndSave(final Runnable saveTask) {
    if (this.homeMaximumLength > 0) {
      // Check home length in a threaded task
      Callable<Void> exportToObjTask = new Callable<Void>() {
          public Void call() throws RecorderException {
            final long homeLength = ((HomeAppletRecorder)application.getHomeRecorder()).getHomeLength(home);
            getView().invokeLater(new Runnable() {
                public void run() {
                  if (homeLength > homeMaximumLength) {
                    String message = getHomeLengthMessage(homeLength);
                    getView().showError(message);
                  } else {
                    saveTask.run();
                  }
                }
              });
  
            return null;
          }
        };
      ThreadedTaskController.ExceptionHandler exceptionHandler = 
          new ThreadedTaskController.ExceptionHandler() {
            public void handleException(Exception ex) {
              if (!(ex instanceof InterruptedRecorderException)) {
                if (ex instanceof RecorderException) {
                  String message = application.getUserPreferences().getLocalizedString(
                      HomeController.class, "saveError", home.getName());
                  getView().showError(message);
                } else {
                  ex.printStackTrace();
                }
              }
            }
          };
      new ThreadedTaskController(exportToObjTask, 
          this.application.getUserPreferences().getLocalizedString(HomeAppletController.class, "chekHomeLengthMessage"), exceptionHandler, 
          this.application.getUserPreferences(), viewFactory).executeTask(getView());
    } else {
      saveTask.run();
    }
  }

  /**
   * Displays Sweet Home user guide in a navigator window.
   */
  @Override
  public void help() {
    try { 
      String helpIndex = this.application.getUserPreferences().getLocalizedString(HomeAppletController.class, "helpIndex");
      SwingTools.showDocumentInBrowser(new URL(helpIndex)); 
    } catch (MalformedURLException ex) {
      ex.printStackTrace();
    } 
  }

  /**
   * Controls the export of home to a SH3D file.
   */
  public void exportToSH3D() {
    final String sh3dName = new FileContentManager(this.application.getUserPreferences()).showSaveDialog(getView(),
        this.application.getUserPreferences().getLocalizedString(HomeAppletController.class, "exportToSH3DDialog.title"), 
        ContentManager.ContentType.SWEET_HOME_3D, home.getName());    
    if (sh3dName != null) {
      // Export home in a threaded task
      Callable<Void> exportToObjTask = new Callable<Void>() {
          public Void call() throws RecorderException {
            new HomeFileRecorder(9).writeHome(home, sh3dName);
            return null;
          }
        };
      ThreadedTaskController.ExceptionHandler exceptionHandler = 
          new ThreadedTaskController.ExceptionHandler() {
            public void handleException(Exception ex) {
              if (!(ex instanceof InterruptedRecorderException)) {
                if (ex instanceof RecorderException) {
                  String message = application.getUserPreferences().getLocalizedString(
                      HomeAppletController.class, "exportToSH3DError", sh3dName);
                  getView().showError(message);
                } else {
                  ex.printStackTrace();
                }
              }
            }
          };
      new ThreadedTaskController(exportToObjTask, 
          this.application.getUserPreferences().getLocalizedString(HomeAppletController.class, "exportToSH3DMessage"), exceptionHandler, 
          this.application.getUserPreferences(), viewFactory).executeTask(getView());
    }
  }
  
  /**
   * Controls the import of home from a SH3D file.
   */
  public void importFromSH3D() {
    close(new Runnable() {
      public void run() {
        final String sh3dName = new FileContentManager(application.getUserPreferences()).showOpenDialog(getView(),
            application.getUserPreferences().getLocalizedString(HomeAppletController.class, "importFromSH3DDialog.title"), 
            ContentManager.ContentType.SWEET_HOME_3D);    
        if (sh3dName != null) {
          // Import home in a threaded task
          Callable<Void> exportToObjTask = new Callable<Void>() {
              public Void call() throws RecorderException {
                final Home openedHome = new HomeFileRecorder(9, true, application.getUserPreferences(), true).readHome(sh3dName);
                String name = new File(sh3dName).getName();
                name = name.substring(0, name.lastIndexOf("."));
                importedHomeNames.put(openedHome, name);
                openedHome.setName(null);
                openedHome.setModified(true);
                final long homeLength = homeMaximumLength > 0
                    ? ((HomeAppletRecorder)application.getHomeRecorder()).getHomeLength(openedHome)
                    : -1;
                getView().invokeLater(new Runnable() {
                    public void run() {
                      application.addHome(openedHome);
                      if (homeLength > homeMaximumLength) {
                        String message = getHomeLengthMessage(homeLength);
                        getView().showMessage(message);
                      } 
                    }
                  });
                return null;
              }
            };
          ThreadedTaskController.ExceptionHandler exceptionHandler = 
              new ThreadedTaskController.ExceptionHandler() {
                public void handleException(Exception ex) {
                  if (!(ex instanceof InterruptedRecorderException)) {
                    if (ex instanceof RecorderException) {
                      String message = application.getUserPreferences().getLocalizedString(
                          HomeAppletController.class, "importFromSH3DError", sh3dName);
                      getView().showError(message);
                    } else {
                      ex.printStackTrace();
                    }
                  }
                }
              };
          new ThreadedTaskController(exportToObjTask, 
              application.getUserPreferences().getLocalizedString(HomeAppletController.class, "importFromSH3DMessage"), exceptionHandler, 
              application.getUserPreferences(), viewFactory).executeTask(getView());
        }
      }
    });
  }
  
  /**
   * Returns a message stating that the home length is too large.
   */
  private String getHomeLengthMessage(long homeLength) {
    DecimalFormat decimalFormat = new DecimalFormat("#.#");
    String homeLengthText = decimalFormat.format(Math.max(0.1f, homeLength / 1048576f));
    String homeMaximumLengthText = decimalFormat.format(Math.max(0.1f, this.homeMaximumLength / 1048576f));
    return application.getUserPreferences().getLocalizedString(
        HomeAppletController.class, "homeLengthError", homeLengthText, homeMaximumLengthText);
  }
  
  /**
   * Edits preferences relevant to applet version.
   */
  @Override
  public void editPreferences() {
    new UserPreferencesController(this.application.getUserPreferences(), 
        this.viewFactory, this.contentManager) {
      public boolean isPropertyEditable(UserPreferencesController.Property property) {
        switch (property) {
          case CHECK_UPDATES_ENABLED :
          case AUTO_SAVE_DELAY_FOR_RECOVERY :
          case AUTO_SAVE_FOR_RECOVERY_ENABLED :
            // No check updates and auto recovery with applet
            return false;
          default :
            return super.isPropertyEditable(property);
        }
      }
    }.displayView(getView());
  }
}

