/*
    This library 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.
*/

package org.gjt.jclasslib.browser;

import org.gjt.jclasslib.browser.config.classpath.FindResult;
import org.gjt.jclasslib.browser.config.window.BrowserPath;
import org.gjt.jclasslib.browser.config.window.WindowState;
import org.gjt.jclasslib.io.ClassFileReader;
import org.gjt.jclasslib.mdi.BasicDesktopManager;
import org.gjt.jclasslib.mdi.BasicInternalFrame;
import org.gjt.jclasslib.structures.ClassFile;
import org.gjt.jclasslib.util.GUIHelper;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.beans.PropertyVetoException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * A child window of the class file browser application.
 *
 * @author <a href="mailto:jclasslib@ej-technologies.com">Ingo Kegel</a>
 *
 */
public class BrowserInternalFrame extends BasicInternalFrame
        implements BrowserServices {


    /**
     * Constructor for creating a derived <tt>BasicInternalFrame</tt> with
     * an initialization parameter.
     */
    public static final Class[] CONSTRUCTOR_ARGUMENTS =
            new Class[]{BasicDesktopManager.class, WindowState.class};

    private String fileName;
    private ClassFile classFile;

    // Visual Components

    private BrowserComponent browserComponent;

    /**
     * Constructor.
     *
     * @param desktopManager the associated desktop manager
     * @param windowState    the window state object. The frame will load the class file from
     *                       information present within this object.
     */
    public BrowserInternalFrame(BasicDesktopManager desktopManager, WindowState windowState) throws IOException {
        super(desktopManager, windowState.getFileName());
        this.fileName = windowState.getFileName();

        setFrameIcon(BrowserMDIFrame.ICON_APPLICATION_16);
        readClassFile();
        setupInternalFrame(windowState.getBrowserPath());
    }

    public Object getInitParam() {
        return new WindowState(fileName, browserComponent.getBrowserPath());
    }

    // Browser services

    public ClassFile getClassFile() {
        return classFile;
    }

    public void activate() {

        // force sync of toolbar state with this frame
        desktopManager.getDesktopPane().setSelectedFrame(this);
    }

    public BrowserComponent getBrowserComponent() {
        return browserComponent;
    }

    public Action getActionBackward() {
        return getParentFrame().getActionBackward();
    }

    public Action getActionForward() {
        return getParentFrame().getActionForward();
    }

    public void openClassFile(String className, BrowserPath browserPath) {

        FindResult findResult = getParentFrame().getConfig().findClass(className);
        while (findResult == null) {
            int result = GUIHelper.showOptionDialog(getParentFrame(),
                    "The class " + className + " could not be found.\n" +
                    "You can check your classpath configuration and try again.",
                    new String[]{"Setup classpath", "Cancel"},
                    JOptionPane.WARNING_MESSAGE);
            if (result == 0) {
                getParentFrame().getActionSetupClasspath().actionPerformed(new ActionEvent(this, 0, null));
                findResult = getParentFrame().getConfig().findClass(className);
            } else {
                return;
            }
        }

        BrowserInternalFrame frame = (BrowserInternalFrame)desktopManager.getOpenFrame(new WindowState(findResult.getFileName()));
        if (frame != null) {
            try {
                frame.setSelected(true);
                frame.browserComponent.setBrowserPath(browserPath);
                desktopManager.scrollToVisible(frame);
            } catch (PropertyVetoException e) {
            }
        } else {
            WindowState windowState = new WindowState(findResult.getFileName(), browserPath);
            try {
                frame = new BrowserInternalFrame(desktopManager, windowState);
                if (isMaximum()) {
                    try {
                        frame.setMaximum(true);
                    } catch (PropertyVetoException ex) {
                    }
                } else {
                    desktopManager.scrollToVisible(frame);
                }
            } catch (IOException e) {
                GUIHelper.showMessage(desktopManager.getParentFrame(), e.getMessage(), JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    public boolean canOpenClassFiles() {
        return true;
    }

    public void showURL(String urlSpec) {
        GUIHelper.showURL(urlSpec);
    }

    /**
     * Reload class file.
     */
    public void reload() throws IOException {
        readClassFile();
        browserComponent.rebuild();
    }

    /**
     * Get the file name for the displayed class file.
     *
     * @return the file name
     */
    public String getFileName() {
        return fileName;
    }

    private void setupInternalFrame(BrowserPath browserPath) {

        setTitle(fileName);

        Container contentPane = getContentPane();
        contentPane.setLayout(new BorderLayout());

        browserComponent = new BrowserComponent(this);
        contentPane.add(browserComponent, BorderLayout.CENTER);

        setupInternalFrame();
        browserComponent.setBrowserPath(browserPath);

    }

    private BrowserMDIFrame getParentFrame() {
        return (BrowserMDIFrame)desktopManager.getParentFrame();
    }

    private void readClassFile() throws IOException {
        try {
            int index = fileName.indexOf('!');
            if (index > -1) {
                String jarFileName = fileName.substring(0, index);
                String classFileName = fileName.substring(index + 1);
                JarFile jarFile = new JarFile(jarFileName);
                JarEntry jarEntry = jarFile.getJarEntry(classFileName);
                if (jarEntry != null) {
                    classFile = ClassFileReader.readFromInputStream(jarFile.getInputStream(jarEntry));
                }
            } else {
                classFile = ClassFileReader.readFromFile(new File(fileName));
            }
        } catch (FileNotFoundException ex) {
            throw new IOException("The file " + fileName + " was not found");
        } catch (IOException ex) {
            throw new IOException("An error occurred while reading " + fileName);
        } catch (Exception ex) {
            throw new IOException("The file " + fileName + " does not seem to contain a class file");
        }
    }

}
