// ============================================================================
// File:               $File$
//
// Project:            
//
// Purpose:            
//
// Author:             Rammi
//
// Copyright Notice:   (c) 2008  Rammi (rammi@caff.de)
//                     This code is in the public domain.
//                     Use at own risk.
//                     No guarantees given.
//
// Latest change:      $Date$
//
// History:	       $Log$
//=============================================================================
package de.caff.asteroid.analysis;

import de.caff.asteroid.*;

import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;

/**
 *  Visual analyser tool for dump files.
 * 
 *  This class is part of a solution for a
 *  <a href="http://www.heise.de/ct/creativ/08/02/details/">competition by the German computer magazine c't</a>.
 */
public class AnalysisDisplay
        extends JFrame
{
  /**
   *  Create a title for the frame, using additional dump file info.
   *  @param dumpFile the dump file
   *  @return title
   */
  private static String makeTitle(DumpFile dumpFile)
  {
    StringBuilder b = new StringBuilder("Analysis of ");
    if (dumpFile == null) {
      b.append("???");
    }
    else {
      b.append(dumpFile.getFilename());
      if (dumpFile.getIpAddress() != null) {
        b.append("[").append(dumpFile.getIpAddress()).append(":").append(dumpFile.getClientPort());
        if (dumpFile.getPlayerName() != null) {
          b.append("; ").append(dumpFile.getPlayerName());
        }
        b.append("]");
      }
    }
    return b.toString();
  }

  /**
   *  Constructor.
   *  @param dumpFile dump file to analyse
   *  @param velocityPreparer preparer used to add velocities to game objects
   *  @param drawableProviderClass class name of class to use as additional drawable provider
   *  @param simple use simple display without info panels?
   *  @throws java.io.IOException on file format or read errors
   */
  public AnalysisDisplay(final DumpFile dumpFile, FramePreparer velocityPreparer, String drawableProviderClass, boolean simple) throws IOException
  {
    super(makeTitle(dumpFile));

    final TimeLine timeLine = new TimeLine(dumpFile);
    FrameKeyInfoDisplay frameKeyInfoDisplay = new FrameKeyInfoDisplay(timeLine, velocityPreparer,
                                                                      getDrawableProvider(drawableProviderClass),
                                                                      simple);

    timeLine.addFrameListener(new FrameListener()
    {
      public void frameReceived(FrameInfo frame)
      {
        setTitle(String.format("%s [%d]", dumpFile.getFilename(), timeLine.getCurrentIndex()));
      }
    });
    getContentPane().add(frameKeyInfoDisplay, BorderLayout.CENTER);
    getContentPane().add(timeLine, BorderLayout.SOUTH);
    if (simple) {
      pack();
    }
    else {
      setSize(1000, 750);
    }
  }

  /**
   *  Start analysis of dump file.
   *  @param args exactly one argument is necessary: the dumpfile path
   *  @throws IOException on early read errors (if the file is missing or the format is incorrect, but
   *                      not if later errors occur like a premature EOF after several frames)
   */
  public static void main(String[] args) throws IOException
  {
    String filename = null;
    String drawableProvider = "de.caff.asteroid.rammi.AsteroidPlayer";
    boolean simple = false;
    boolean nextIsDrawableProvider = false;
    for (String arg: args) {
      if (nextIsDrawableProvider) {
        nextIsDrawableProvider = false;
        drawableProvider = arg;
      }
      else if ("-s".equals(arg)) {
        simple = true;
      }
      else if ("-d".equals(arg)) {
        nextIsDrawableProvider = true;
      }
      else if (filename == null) {
        filename = arg;
      }
      else {
        System.err.println(String.format("Unexpected command line argument: '%s'", arg));
        System.exit(1);
      }
    }

    if (nextIsDrawableProvider) {
      System.err.println("Switch -d needs argument!");
    }
    if (filename == null) {
      System.err.println("Need dump file argument!");
      System.exit(1);
    }

    DumpFile dumpFile = openDumpFile(filename);
    AnalysisDisplay dpy = new AnalysisDisplay(dumpFile, getBestPreparer(dumpFile), drawableProvider, simple);
    dpy.setDefaultCloseOperation(AnalysisDisplay.EXIT_ON_CLOSE);
    dpy.setVisible(true);
    // System.out.println("Size: "+dpy.getContentPane().getSize());
  }

  /**
   *  Open a dump file from a given location.
   *  @param filename filename or URL
   *  @return dump file
   *  @throws IOException on various i/o errors
   */
  private static DumpFile openDumpFile(String filename)
          throws IOException
  {
    try {
      // trying to interprete filename as URL
      URL url = new URL(filename);
      return new DumpFile(url.openStream(), filename);
    } catch (MalformedURLException e) {
      // no URL, so try local file
      return new DumpFile(filename);
    }
  }

  private static FramePreparer getBestPreparer(PingKeyProvider pinky)
  {
    try {
      Class veloClass = Class.forName("de.caff.asteroid.rammi.ImprovedVelocityPreparer");
      Class dirClass  = Class.forName("de.caff.asteroid.rammi.ShootingDirectionFixer");
      Constructor constr = dirClass.getConstructor(PingKeyProvider.class);
      return new FramePreparerSequence((FramePreparer)veloClass.newInstance(),
                                       (FramePreparer)constr.newInstance(pinky));
    } catch (ClassNotFoundException e) {
    } catch (InstantiationException e) {
    } catch (IllegalAccessException e) {
    } catch (NoSuchMethodException e) {
    } catch (InvocationTargetException e) {
    }
    return new SimpleVelocityPreparer();
  }

  private static DrawableProvider getDrawableProvider(String className)
  {
    try {
      Class clazz = Class.forName(className);
      return (DrawableProvider)clazz.newInstance();
    } catch (ClassNotFoundException e) {
    } catch (InstantiationException e) {
    } catch (IllegalAccessException e) {
    }
    return null;
  }
}
