/*
 * BackgroundImageWizardTest.java 22 sept. 2008
 * 
 * Copyright (c) 2008 Emmanuel PUYBARET / eTeks <info@eteks.com>. All Rights Reserved.
 * 
 * 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.junit;

import java.awt.KeyboardFocusManager;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JSpinner;

import junit.extensions.abbot.ComponentTestFixture;
import abbot.finder.AWTHierarchy;
import abbot.finder.ComponentSearchException;
import abbot.tester.JComponentTester;

import com.eteks.sweethome3d.io.DefaultUserPreferences;
import com.eteks.sweethome3d.model.BackgroundImage;
import com.eteks.sweethome3d.model.Content;
import com.eteks.sweethome3d.model.Home;
import com.eteks.sweethome3d.model.LengthUnit;
import com.eteks.sweethome3d.model.RecorderException;
import com.eteks.sweethome3d.model.UserPreferences;
import com.eteks.sweethome3d.swing.BackgroundImageWizardStepsPanel;
import com.eteks.sweethome3d.swing.HomePane;
import com.eteks.sweethome3d.swing.SwingViewFactory;
import com.eteks.sweethome3d.swing.WizardPane;
import com.eteks.sweethome3d.tools.URLContent;
import com.eteks.sweethome3d.viewcontroller.BackgroundImageWizardController;
import com.eteks.sweethome3d.viewcontroller.ContentManager;
import com.eteks.sweethome3d.viewcontroller.HomeController;
import com.eteks.sweethome3d.viewcontroller.HomeView;
import com.eteks.sweethome3d.viewcontroller.View;
import com.eteks.sweethome3d.viewcontroller.ViewFactory;

/**
 * Tests background image wizard.
 * @author Emmanuel Puybaret
 */
public class BackgroundImageWizardTest extends ComponentTestFixture {
  public void testBackgroundImageWizard() throws ComponentSearchException, InterruptedException, 
      NoSuchFieldException, IllegalAccessException, InvocationTargetException {
    final UserPreferences preferences = new DefaultUserPreferences();
    // Ensure we use centimeter unit
    preferences.setUnit(LengthUnit.CENTIMETER);
    final URL testedImageName = BackgroundImageWizardTest.class.getResource("resources/test.png");
    // Create a dummy content manager
    final ContentManager contentManager = new ContentManager() {
      public Content getContent(String contentName) throws RecorderException {
        try {
          // Let's consider contentName is a URL
          return new URLContent(new URL(contentName));
        } catch (IOException ex) {
          fail();
          return null;
        }
      }

      public String getPresentationName(String contentName, ContentType contentType) {
        return "test";
      }

      public boolean isAcceptable(String contentName, ContentType contentType) {
        return true;
      }

      public String showOpenDialog(View parentView, String dialogTitle, ContentType contentType) {
        // Return tested model name URL
        return testedImageName.toString();
      }

      public String showSaveDialog(View parentView, String dialogTitle, ContentType contentType, String name) {
        return null;
      }      
    };
    Home home = new Home();
    ViewFactory viewFactory = new SwingViewFactory();    
    final HomeController controller = new HomeController(home, preferences, viewFactory, contentManager);
    final JComponent homeView = (JComponent)controller.getView();

    // 1. Create a frame that displays a home view 
    JFrame frame = new JFrame("Background Image Wizard Test");    
    frame.add(homeView);
    frame.pack();

    // Show home plan frame
    showWindow(frame);
    JComponentTester tester = new JComponentTester();
    tester.waitForIdle();    
    // Check home background image is empty
    assertEquals("Home background image isn't empty", null, home.getBackgroundImage());

    // 2. Open wizard to import a background image
    runAction(controller, HomeView.ActionType.IMPORT_BACKGROUND_IMAGE, tester);
    // Wait for import furniture view to be shown
    tester.waitForFrameShowing(new AWTHierarchy(), preferences.getLocalizedString(
        BackgroundImageWizardController.class, "wizard.title"));
    // Check dialog box is displayed
    JDialog wizardDialog = (JDialog)TestUtilities.findComponent(frame, JDialog.class);
    assertTrue("Wizard view dialog not showing", wizardDialog.isShowing());

    // Retrieve ImportedFurnitureWizardStepsPanel components
    BackgroundImageWizardStepsPanel panel = (BackgroundImageWizardStepsPanel)TestUtilities.findComponent(
        wizardDialog, BackgroundImageWizardStepsPanel.class);
    JButton imageChoiceOrChangeButton = (JButton)TestUtilities.getField(panel, "imageChoiceOrChangeButton");
    JSpinner scaleDistanceSpinner = (JSpinner)TestUtilities.getField(panel, "scaleDistanceSpinner");
    JSpinner xOriginSpinner = (JSpinner)TestUtilities.getField(panel, "xOriginSpinner");
    JSpinner yOriginSpinner = (JSpinner)TestUtilities.getField(panel, "yOriginSpinner");
    
    // Check current step is image
    assertStepShowing(panel, true, false, false);    
    
    // 3. Choose tested image
    String imageChoiceOrChangeButtonText = imageChoiceOrChangeButton.getText();
    tester.click(imageChoiceOrChangeButton);
    // Wait 100 ms to let time to Java to load the image
    Thread.sleep(100);
    // Check choice button text changed
    assertFalse("Choice button text didn't change", 
        imageChoiceOrChangeButtonText.equals(imageChoiceOrChangeButton.getText()));
    // Click on next button
    WizardPane view = (WizardPane)TestUtilities.findComponent(wizardDialog, WizardPane.class);
    // Retrieve wizard view next button
    final JButton nextFinishOptionButton = (JButton)TestUtilities.getField(view, "nextFinishOptionButton"); 
    assertTrue("Next button isn't enabled", nextFinishOptionButton.isEnabled());
    tester.click(nextFinishOptionButton);
    // Check current step is scale
    assertStepShowing(panel, false, true, false);
    
    // 4. Check scale distance spinner value is empty
    assertEquals("Scale distance spinner isn't empty", null, scaleDistanceSpinner.getValue());
    assertFalse("Next button is enabled", nextFinishOptionButton.isEnabled());
    // Check scale spinner field has focus
    tester.waitForIdle();
    assertSame("Scale spinner doesn't have focus", ((JSpinner.DefaultEditor)scaleDistanceSpinner.getEditor()).getTextField(),
        KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner());    
    // Enter as scale
    tester.actionKeyString("100");    
    // Check next button is enabled 
    assertTrue("Next button isn't enabled", nextFinishOptionButton.isEnabled());
    tester.click(nextFinishOptionButton);
    // Check current step is origin
    assertStepShowing(panel, false, false, true);
    
    // 5. Check origin x and y spinners value is 0
    assertEquals("Wrong origin x spinner value", new Float(0), xOriginSpinner.getValue());
    assertEquals("Wrong origin y spinner value", new Float(0), yOriginSpinner.getValue());
    assertTrue("Next button isn't enabled", nextFinishOptionButton.isEnabled());
    tester.waitForIdle();
    assertSame("Origin x spinner doesn't have focus", ((JSpinner.DefaultEditor)xOriginSpinner.getEditor()).getTextField(),
        KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner());    
    // Change origin
    tester.actionKeyString("10");    
    assertEquals("Wrong origin x spinner value", 10f, xOriginSpinner.getValue());

    tester.click(nextFinishOptionButton);
    // Check home has a background image
    BackgroundImage backgroundImage = home.getBackgroundImage();
    assertTrue("No background image in home", backgroundImage != null);
    assertEquals("Background image wrong scale", 100f, backgroundImage.getScaleDistance());
    assertEquals("Background image wrong x origin", 10f, backgroundImage.getXOrigin());
    assertEquals("Background image wrong y origin", 0f, backgroundImage.getYOrigin());
        
    // 6. Undo background image choice in home
    runAction(controller, HomeView.ActionType.UNDO, tester);
    // Check home background image is empty
    assertEquals("Home background image isn't empty", null, home.getBackgroundImage());
    // Redo
    runAction(controller, HomeView.ActionType.REDO, tester);
    // Check home background image is back
    assertSame("No background image in home", backgroundImage, home.getBackgroundImage());
    
    // 7. Delete background image
    runAction(controller, HomeView.ActionType.DELETE_BACKGROUND_IMAGE, tester);
    // Check home background image is empty
    assertEquals("Home background image isn't empty", null, home.getBackgroundImage());
  }

  /**
   * Asserts if each <code>panel</code> step preview component is showing or not. 
   */
  private void assertStepShowing(BackgroundImageWizardStepsPanel panel,
                                 boolean imageStepShwing,
                                 boolean scaleStepShowing,
                                 boolean originStepShowing) throws NoSuchFieldException, IllegalAccessException {
    assertEquals("Wrong image step visibility", imageStepShwing,
        ((JComponent)TestUtilities.getField(panel, "imageChoicePreviewComponent")).isShowing());
    assertEquals("Wrong scale step visibility", scaleStepShowing,
        ((JComponent)TestUtilities.getField(panel, "scalePreviewComponent")).isShowing());
    assertEquals("Wrong origin step visibility", originStepShowing,
        ((JComponent)TestUtilities.getField(panel, "originPreviewComponent")).isShowing());
  }

  /**
   * Runs <code>actionPerformed</code> method matching <code>actionType</code> 
   * in <code>controller</code> view. 
   */
  private void runAction(final HomeController controller, 
                         final HomePane.ActionType actionType,
                         JComponentTester tester) {
    tester.invokeAndWait(new Runnable() {
      public void run() {
        ((JComponent)controller.getView()).getActionMap().get(actionType).actionPerformed(null);
      }
    });
  }
}
