package com.jogamp.opengl.test.junit.jogl.acore.anim;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.lang.reflect.InvocationTargetException;

import com.jogamp.opengl.GLAnimatorControl;
import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLProfile;
import com.jogamp.opengl.awt.GLCanvas;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.WindowConstants;

import org.junit.Test;
import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters;

import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2;
import com.jogamp.opengl.test.junit.util.MiscUtils;
import com.jogamp.opengl.test.junit.util.UITestCase;
import com.jogamp.opengl.util.Animator;
import com.jogamp.opengl.util.FPSAnimator;

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@SuppressWarnings({ "unchecked", "rawtypes" })
public class TestAWTCardLayoutAnimatorStartStopBug532 extends UITestCase {
    static final String LABEL = "Label";
    static final String CANVAS = "GLCanvas";

    public enum AnimatorControlBehavior {
        StartStop, PauseResume, Continue;
    }

    static long durationPerTest = 200*4; // ms
    static boolean manual = false;
    static volatile boolean shouldStop = false;

    private String selected = LABEL;

    @Test
    public void testFPSAnimatorStartStop() throws InterruptedException, InvocationTargetException {
        testImpl(AnimatorControlBehavior.StartStop, true);
    }

    @Test
    public void testFPSAnimatorResumePause() throws InterruptedException, InvocationTargetException {
        testImpl(AnimatorControlBehavior.PauseResume, true);
    }

    @Test
    public void testFPSAnimatorContinue() throws InterruptedException, InvocationTargetException {
        testImpl(AnimatorControlBehavior.Continue, true);
    }

    @Test
    public void testAnimatorStartStop() throws InterruptedException, InvocationTargetException {
        testImpl(AnimatorControlBehavior.StartStop, false);
    }

    @Test
    public void testAnimatorResumePause() throws InterruptedException, InvocationTargetException {
        testImpl(AnimatorControlBehavior.PauseResume, false);
    }

    @Test
    public void testAnimatorContinue() throws InterruptedException, InvocationTargetException {
        testImpl(AnimatorControlBehavior.Continue, false);
    }

    private static String id(final Object obj) { return "0x" + ( null!=obj ? Integer.toHexString(obj.hashCode()) : "nil" ); }
    private static String str(final Component c) {
        return id(c)+": "+c.getClass().getSimpleName()+"[visible "+c.isVisible()+", showing "+c.isShowing()+", valid "+c.isValid()+
                ", displayable "+c.isDisplayable()+", "+c.getX()+"/"+c.getY()+" "+c.getWidth()+"x"+c.getHeight()+"]";
    }
    void testImpl(final AnimatorControlBehavior animCtrl, final boolean useFPSAnimator) throws InterruptedException, InvocationTargetException {
      final GLProfile glp = GLProfile.get(GLProfile.GL2);
      final GLCapabilities caps = new GLCapabilities(glp);
      final GLCanvas canvas = new GLCanvas(caps);
      canvas.setPreferredSize(new Dimension(640, 480));

      final GLAnimatorControl animatorCtrl = useFPSAnimator ? new FPSAnimator(canvas, 60) : new Animator(canvas);
      animatorCtrl.setUpdateFPSFrames(60, null);// System.err);
      switch (animCtrl) {
          case PauseResume:
              animatorCtrl.start();
              animatorCtrl.pause();
              break;
          case Continue:
              animatorCtrl.start();
              break;
          default:
      }

      canvas.addGLEventListener(new GearsES2(1));
      /* if(Platform.OS_TYPE == Platform.OSType.WINDOWS) {
          canvas.addGLEventListener(new GLEventListener() {
            public void init(GLAutoDrawable drawable) { }
            public void dispose(GLAutoDrawable drawable) { }
            public void display(GLAutoDrawable drawable) {
                final NativeWindow win = (NativeWindow) drawable.getNativeSurface();
                long hdc = win.getSurfaceHandle();
                long hdw = win.getWindowHandle();
                long hdw_hdc = GDI.WindowFromDC(hdc);
                System.err.println("*** hdc 0x"+Long.toHexString(hdc)+", hdw(hdc) 0x"+Long.toHexString(hdw_hdc)+", hdw 0x"+Long.toHexString(hdw) + " - " + Thread.currentThread().getName() + ", " + animatorCtrl);
                // System.err.println(drawable.getNativeSurface().toString());
            }
            public void reshape(GLAutoDrawable drawable, int x, int y, int width,
                    int height) { }
          });
      } */

      final JFrame frame = new JFrame();
      frame.setTitle(getSimpleTestName(" - "));
      frame.addWindowListener(new WindowAdapter() {
         @Override
        public void windowClosing(final WindowEvent e) {
            animatorCtrl.stop();
            shouldStop = true;
         }
      });
      frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

      final JLabel label = new JLabel("A label to cover the canvas");

      final JPanel cards = new JPanel(new CardLayout());
      final JPanel comboBoxPanel = new JPanel(); // nicer look ..
      final JComboBox comboBox = new JComboBox(new String[] { LABEL, CANVAS });
      comboBox.setEditable(false);
      comboBox.addItemListener(new ItemListener() {
        @Override
        public void itemStateChanged(final ItemEvent evt) {
            final CardLayout cl = (CardLayout)(cards.getLayout());
            final String newSelection = (String)evt.getItem();
            if(!newSelection.equals(selected)) {
                final String oldSelected = selected;
                if(newSelection.equals(CANVAS)) {
                    System.err.println("XXX Card.SHOW Canvas PRE: ");
                    System.err.println("   CANVAS "+str(canvas));
                    System.err.println("   LABEL  "+str(label));
                    cl.show(cards, CANVAS);
                    System.err.println("XXX Card.SHOW Canvas POST: ");
                    System.err.println("   CANVAS "+str(canvas));
                    System.err.println("   LABEL  "+str(label));
                    switch (animCtrl) {
                       case StartStop:
                           animatorCtrl.start();
                           break;
                       case PauseResume:
                           animatorCtrl.resume();
                           break;
                       default:
                    }
                    selected = CANVAS;
                } else if(newSelection.equals(LABEL)) {
                    switch (animCtrl) {
                       case StartStop:
                           animatorCtrl.stop();
                           break;
                       case PauseResume:
                           animatorCtrl.pause();
                           break;
                       default:
                    }
                    System.err.println("XXX Card.SHOW Label PRE: ");
                    System.err.println("   CANVAS "+str(canvas));
                    System.err.println("   LABEL  "+str(label));
                    cl.show(cards, LABEL);
                    System.err.println("XXX Card.SHOW Label POST: ");
                    System.err.println("   CANVAS "+str(canvas));
                    System.err.println("   LABEL  "+str(label));
                    selected = LABEL;
                } else {
                    throw new RuntimeException("oops .. unexpected item: "+evt);
                }
                System.err.println("Item Change: "+oldSelected+" -> "+selected+", "+animatorCtrl);
            } else {
                System.err.println("Item Stays: "+selected+", "+animatorCtrl);
            }
        }
      });
      comboBoxPanel.add(comboBox);

      cards.add(label, LABEL);
      cards.add(canvas, CANVAS);

      frame.add(comboBoxPanel, BorderLayout.PAGE_START);
      frame.add(cards, BorderLayout.CENTER);

      javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
        @Override
        public void run() {
            frame.pack();
            frame.setVisible(true);
        }});

      if(manual) {
          for(long w=durationPerTest; !shouldStop && w>0; w-=100) {
              Thread.sleep(100);
          }
      } else {
          javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
            @Override
            public void run() {
                comboBox.setSelectedItem(LABEL);
            }});
          Thread.sleep(durationPerTest/4);

          javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
            @Override
            public void run() {
                comboBox.setSelectedItem(CANVAS);
            }});
          Thread.sleep(durationPerTest/4);

          javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
            @Override
            public void run() {
                comboBox.setSelectedItem(LABEL);
            }});
          Thread.sleep(durationPerTest/4);

          javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
            @Override
            public void run() {
                comboBox.setSelectedItem(CANVAS);
            }});
          Thread.sleep(durationPerTest/4);
      }

      javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
        @Override
        public void run() {
            frame.setVisible(false);
            frame.dispose();
        }});

    }

    public static void main(final String args[]) {
        for(int i=0; i<args.length; i++) {
            if(args[i].equals("-time")) {
                durationPerTest = MiscUtils.atoi(args[++i], (int)durationPerTest);
            } else if(args[i].equals("-manual")) {
                manual = true;
            }
        }
        org.junit.runner.JUnitCore.main(TestAWTCardLayoutAnimatorStartStopBug532.class.getName());
    }
}
