/*
 *  Copyright (C) 2010-2014 JPEXS
 * 
 *  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 3 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, see <http://www.gnu.org/licenses/>.
 */
package com.jpexs.decompiler.flash.gui;

import com.jpexs.decompiler.flash.ApplicationInfo;
import com.jpexs.decompiler.flash.EventListener;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFBundle;
import com.jpexs.decompiler.flash.SWFSourceInfo;
import com.jpexs.decompiler.flash.SearchMode;
import com.jpexs.decompiler.flash.Version;
import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.ClassPath;
import com.jpexs.decompiler.flash.abc.ScriptPack;
import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
import com.jpexs.decompiler.flash.abc.types.Multiname;
import com.jpexs.decompiler.flash.abc.types.Namespace;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.console.CommandLineArgumentParser;
import com.jpexs.decompiler.flash.console.ContextMenuTools;
import com.jpexs.decompiler.flash.gui.debugger.Debugger;
import com.jpexs.decompiler.flash.gui.proxy.ProxyFrame;
import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin;
import com.jpexs.decompiler.flash.helpers.collections.MyEntry;
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.treeitems.SWFList;
import com.jpexs.helpers.Cache;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import com.jpexs.helpers.ProgressListener;
import com.jpexs.helpers.Stopwatch;
import com.jpexs.helpers.streams.SeekableInputStream;
import com.sun.jna.Platform;
import com.sun.jna.platform.win32.Advapi32Util;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinReg;
import java.awt.AWTException;
import java.awt.Frame;
import java.awt.GraphicsEnvironment;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.net.Socket;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map.Entry;
import java.util.Random;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.filechooser.FileFilter;

/**
 * Main executable class
 *
 * @author JPEXS
 */
public class Main {

    public static ProxyFrame proxyFrame;
    private static List<SWFSourceInfo> sourceInfos = new ArrayList<>();
    public static LoadingDialog loadingDialog;
    public static ModeFrame modeFrame;
    private static boolean working = false;
    private static TrayIcon trayIcon;
    private static MenuItem stopMenuItem;
    private static MainFrame mainFrame;
    public static final int UPDATE_SYSTEM_MAJOR = 1;
    public static final int UPDATE_SYSTEM_MINOR = 2;
    public static LoadFromMemoryFrame loadFromMemoryFrame;
    public static LoadFromCacheFrame loadFromCacheFrame;
    private static final Logger logger = Logger.getLogger(Main.class.getName());
    private static Debugger debugger;
    public static DebugLogDialog debugDialog;
    public static final String DEBUGGER_PACKAGE = "com.jpexs.decompiler.flash.debugger";

    private static ABCContainerTag getDebuggerABCTag(SWF swf) {
        for (ABCContainerTag ac : swf.abcList) {
            ABC a = ac.getABC();
            for (MyEntry<ClassPath, ScriptPack> m : a.getScriptPacks()) {
                if (isDebuggerClass(m.getKey().packageStr, null)) {
                    return ac;
                }
            }
        }
        return null;
    }

    private static String getDebuggerPackage(SWF swf) {
        ABCContainerTag ac = getDebuggerABCTag(swf);
        if (ac == null) {
            return null;
        }
        ABC a = ac.getABC();
        for (MyEntry<ClassPath, ScriptPack> m : a.getScriptPacks()) {
            if (isDebuggerClass(m.getKey().packageStr, null)) {
                return m.getKey().packageStr;
            }
        }
        return null;
    }

    public static boolean hasDebugger(SWF swf) {
        return getDebuggerABCTag(swf) != null;
    }

    public static void ensureMainFrame() {
        if (mainFrame == null) {
            if (Configuration.useRibbonInterface.get()) {
                mainFrame = new MainFrameRibbon();
            } else {
                mainFrame = new MainFrameClassic();
            }
            mainFrame.getPanel().setErrorState(ErrorLogFrame.getInstance().getErrorState());
        }
    }

    public static MainFrame getMainFrame() {
        return mainFrame;
    }

    public static void loadFromCache() {
        if (loadFromCacheFrame == null) {
            loadFromCacheFrame = new LoadFromCacheFrame();
        }
        loadFromCacheFrame.setVisible(true);
    }

    public static void loadFromMemory() {
        if (loadFromMemoryFrame == null) {
            loadFromMemoryFrame = new LoadFromMemoryFrame(mainFrame);
        }
        loadFromMemoryFrame.setVisible(true);
    }

    public static void setSubLimiter(boolean value) {
        if (value) {
            AVM2Code.toSourceLimit = Configuration.sublimiter.get();
        } else {
            AVM2Code.toSourceLimit = -1;
        }
    }

    public static boolean isWorking() {
        return working;
    }

    public static void startProxy(int port) {
        View.execInEventDispatch(new Runnable() {
            @Override
            public void run() {
                if (proxyFrame == null) {
                    proxyFrame = new ProxyFrame(mainFrame);
                }
            }
        });
        proxyFrame.setPort(port);
        addTrayIcon();
        switchProxy();
    }

    public static void showProxy() {
        if (proxyFrame == null) {
            proxyFrame = new ProxyFrame(mainFrame);
        }
        proxyFrame.setVisible(true);
        proxyFrame.setState(Frame.NORMAL);
    }

    public static void startWork(String name) {
        startWork(name, -1, null);
    }

    public static void startWork(String name, int percent) {
        startWork(name, percent, null);
    }

    public static void startWork(String name, CancellableWorker worker) {
        startWork(name, -1, worker);
    }

    public static void startWork(final String name, final int percent, final CancellableWorker worker) {
        working = true;
        View.execInEventDispatchLater(new Runnable() {
            @Override
            public void run() {
                if (mainFrame != null) {
                    mainFrame.getPanel().setWorkStatus(name, worker);
                    if (percent == -1) {
                        mainFrame.getPanel().hidePercent();
                    } else {
                        mainFrame.getPanel().setPercent(percent);
                    }
                }
                if (loadingDialog != null) {
                    loadingDialog.setDetail(name);
                    if (percent == -1) {
                        loadingDialog.hidePercent();
                    } else {
                        loadingDialog.setPercent(percent);
                    }
                }
                if (CommandLineArgumentParser.isCommandLineMode()) {
                    System.out.println(name);
                }
            }
        });

    }

    public static void stopWork() {
        working = false;
        View.execInEventDispatchLater(new Runnable() {
            @Override
            public void run() {
                if (mainFrame != null) {
                    mainFrame.getPanel().setWorkStatus("", null);
                }
                if (loadingDialog != null) {
                    loadingDialog.setDetail("");
                }
            }
        });
    }

    public static SWFList parseSWF(SWFSourceInfo sourceInfo) throws Exception {
        SWFList result = new SWFList();

        InputStream inputStream = sourceInfo.getInputStream();
        SWFBundle bundle = null;
        FileInputStream fis = null;
        if (inputStream == null) {
            inputStream = new BufferedInputStream(fis = new FileInputStream(sourceInfo.getFile()));
            bundle = sourceInfo.getBundle(false, SearchMode.ALL);
            logger.log(Level.INFO, "Load file: {0}", sourceInfo.getFile());
        } else if (inputStream instanceof SeekableInputStream
                || inputStream instanceof BufferedInputStream) {
            try {
                inputStream.reset();
            } catch (IOException ex) {
                logger.log(Level.SEVERE, null, ex);
            }
            logger.log(Level.INFO, "Load stream: {0}", sourceInfo.getFileTitle());
        }

        Stopwatch sw = Stopwatch.startNew();
        if (bundle != null) {
            result.isBundle = true;
            result.bundleClass = bundle.getClass();
            result.name = new File(sourceInfo.getFileTitleOrName()).getName();
            for (Entry<String, SeekableInputStream> streamEntry : bundle.getAll().entrySet()) {
                InputStream stream = streamEntry.getValue();
                stream.reset();
                SWF swf = new SWF(stream, new ProgressListener() {
                    @Override
                    public void progress(int p) {
                        startWork(AppStrings.translate("work.reading.swf"), p);
                    }
                }, Configuration.parallelSpeedUp.get());
                swf.fileTitle = streamEntry.getKey();
                swf.readOnly = true;
                result.add(swf);
            }
        } else {
            SWF swf = new SWF(inputStream, new ProgressListener() {
                @Override
                public void progress(int p) {
                    startWork(AppStrings.translate("work.reading.swf"), p);
                }
            }, Configuration.parallelSpeedUp.get());
            swf.file = sourceInfo.getFile();
            swf.fileTitle = sourceInfo.getFileTitle();
            result.add(swf);
        }

        if (fis != null) {
            logger.log(Level.INFO, "File loaded in {0} seconds.", (sw.getElapsedMilliseconds() / 1000));
            fis.close();
        } else {
            logger.log(Level.INFO, "Stream loaded in {0} seconds.", (sw.getElapsedMilliseconds() / 1000));
        }

        result.sourceInfo = sourceInfo;
        for (SWF swf : result) {
            logger.log(Level.INFO, "");
            logger.log(Level.INFO, "== File information ==");
            logger.log(Level.INFO, "Size: {0}", Helper.formatFileSize(swf.fileSize));
            logger.log(Level.INFO, "Flash version: {0}", swf.version);
            int width = (int) ((swf.displayRect.Xmax - swf.displayRect.Xmin) / SWF.unitDivisor);
            int height = (int) ((swf.displayRect.Ymax - swf.displayRect.Ymin) / SWF.unitDivisor);
            logger.log(Level.INFO, "Width: {0}", width);
            logger.log(Level.INFO, "Height: {0}", height);

            swf.swfList = result;
            swf.addEventListener(new EventListener() {
                @Override
                public void handleEvent(String event, Object data) {
                    if (event.equals("exporting")) {
                        startWork((String) data);
                    }
                    if (event.equals("getVariables")) {
                        startWork(AppStrings.translate("work.gettingvariables") + "..." + (String) data);
                    }
                    if (event.equals("deobfuscate")) {
                        startWork(AppStrings.translate("work.deobfuscating") + "..." + (String) data);
                    }
                    if (event.equals("rename")) {
                        startWork(AppStrings.translate("work.renaming") + "..." + (String) data);
                    }
                }
            });
        }

        return result;
    }

    public static void saveFile(SWF swf, String outfile) throws IOException {
        saveFile(swf, outfile, SaveFileMode.SAVE);
    }

    public static void saveFile(SWF swf, String outfile, SaveFileMode mode) throws IOException {
        if (mode == SaveFileMode.SAVEAS) {
            swf.file = outfile;
        }
        File outfileF = new File(outfile);
        File tmpFile = new File(outfile + ".tmp");
        FileOutputStream fos = new FileOutputStream(tmpFile);
        if (mode == SaveFileMode.EXE) {
            InputStream exeStream = View.class.getClassLoader().getResourceAsStream("com/jpexs/helpers/resource/Swf2Exe.bin");
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = exeStream.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
            int width = swf.displayRect.Xmax - swf.displayRect.Xmin;
            int height = swf.displayRect.Ymax - swf.displayRect.Ymin;
            fos.write(width & 0xff);
            fos.write((width >> 8) & 0xff);
            fos.write((width >> 16) & 0xff);
            fos.write((width >> 24) & 0xff);
            fos.write(height & 0xff);
            fos.write((height >> 8) & 0xff);
            fos.write((height >> 16) & 0xff);
            fos.write((height >> 24) & 0xff);
            fos.write(Configuration.saveAsExeScaleMode.get());
        }
        swf.saveTo(fos);
        if (tmpFile.exists()) {
            if (tmpFile.length() > 0) {
                outfileF.delete();
                if (!tmpFile.renameTo(outfileF)) {
                    tmpFile.delete();
                    throw new IOException("Cannot access " + outfile);
                }
            } else {
                throw new IOException("Output is empty");
            }
        }
    }

    private static class OpenFileWorker extends SwingWorker {

        private final SWFSourceInfo[] sourceInfos;

        public OpenFileWorker(SWFSourceInfo sourceInfo) {
            this.sourceInfos = new SWFSourceInfo[]{sourceInfo};
        }

        public OpenFileWorker(SWFSourceInfo[] sourceInfos) {
            this.sourceInfos = sourceInfos;
        }

        @Override
        protected Object doInBackground() throws Exception {
            boolean first = true;
            SWF firstSWF = null;
            for (final SWFSourceInfo sourceInfo : sourceInfos) {
                SWFList swfs = null;
                try {
                    Main.startWork(AppStrings.translate("work.reading.swf") + "...");
                    swfs = parseSWF(sourceInfo);
                } catch (OutOfMemoryError ex) {
                    logger.log(Level.SEVERE, null, ex);
                    View.showMessageDialog(null, "Cannot load SWF file. Out of memory.");
                } catch (Exception ex) {
                    logger.log(Level.SEVERE, null, ex);
                    View.showMessageDialog(null, "Cannot load SWF file.");
                }

                final SWFList swfs1 = swfs;
                final boolean first1 = first;
                first = false;
                if (firstSWF == null) {
                    firstSWF = swfs1.get(0);
                }
                try {
                    Main.startWork(AppStrings.translate("work.creatingwindow") + "...");
                    View.execInEventDispatch(new Runnable() {
                        @Override
                        public void run() {
                            ensureMainFrame();
                            mainFrame.getPanel().load(swfs1, first1);
                        }
                    });

                } catch (Exception ex) {
                    logger.log(Level.SEVERE, null, ex);
                }
            }

            loadingDialog.setVisible(false);
            final SWF fswf = firstSWF;
            View.execInEventDispatch(new Runnable() {
                @Override
                public void run() {
                    if (mainFrame != null) {
                        mainFrame.setVisible(true);
                    }
                    Main.stopWork();
                    if (mainFrame != null && Configuration.gotoMainClassOnStartup.get()) {
                        mainFrame.getPanel().gotoDocumentClass(fswf);
                    }
                }
            });

            return true;
        }
    }

    public static boolean reloadSWFs() {
        CancellableWorker.cancelBackgroundThreads();
        if (mainFrame != null) {
            mainFrame.getPanel().closeAll();
        }
        if (Main.sourceInfos.isEmpty()) {
            Helper.freeMem();
            showModeFrame();
            return true;
        } else {
            SWFSourceInfo[] sourceInfosCopy = new SWFSourceInfo[sourceInfos.size()];
            sourceInfos.toArray(sourceInfosCopy);
            sourceInfos.clear();
            openFile(sourceInfosCopy);
            return true;
        }
    }

    public static void reloadApp() {
        if (debugDialog != null) {
            debugDialog.setVisible(false);
            debugDialog = null;
        }
        if (loadingDialog != null) {
            loadingDialog.setVisible(false);
            loadingDialog = null;
        }
        if (proxyFrame != null) {
            proxyFrame.setVisible(false);
            proxyFrame = null;
        }
        if (loadFromMemoryFrame != null) {
            loadFromMemoryFrame.setVisible(false);
            loadFromMemoryFrame = null;
        }
        if (loadFromCacheFrame != null) {
            loadFromCacheFrame.setVisible(false);
            loadFromCacheFrame = null;
        }
        if (mainFrame != null) {
            mainFrame.setVisible(false);
            mainFrame = null;
        }
        FontTag.reload();
        Cache.clearAll();
        initGui();
        reloadSWFs();
    }

    public static OpenFileResult openFile(String swfFile, String fileTitle) {
        try {
            File file = new File(swfFile);
            if (!file.exists()) {
                View.showMessageDialog(null, AppStrings.translate("open.error.fileNotFound"), AppStrings.translate("open.error"), JOptionPane.ERROR_MESSAGE);
                return OpenFileResult.NOT_FOUND;
            }
            swfFile = file.getCanonicalPath();
            Configuration.addRecentFile(swfFile);
            SWFSourceInfo sourceInfo = new SWFSourceInfo(null, swfFile, fileTitle);
            OpenFileResult openResult = openFile(sourceInfo);
            return openResult;
        } catch (IOException ex) {
            View.showMessageDialog(null, AppStrings.translate("open.error.cannotOpen"), AppStrings.translate("open.error"), JOptionPane.ERROR_MESSAGE);
            return OpenFileResult.ERROR;
        }
    }

    public static OpenFileResult openFile(SWFSourceInfo sourceInfo) {
        return openFile(new SWFSourceInfo[]{sourceInfo});
    }

    public static OpenFileResult openFile(SWFSourceInfo[] newSourceInfos) {
        if (mainFrame != null && !Configuration.openMultipleFiles.get()) {
            sourceInfos.clear();
            mainFrame.getPanel().closeAll();
            mainFrame.setVisible(false);
            Helper.freeMem();
        }

        View.execInEventDispatch(new Runnable() {
            @Override
            public void run() {
                if (Main.loadingDialog == null || Main.loadingDialog.getOwner() == null) {
                    Main.loadingDialog = new LoadingDialog(mainFrame == null ? null : mainFrame.getWindow());
                }
            }
        });

        Main.loadingDialog.setVisible(true);
        OpenFileWorker wrk = new OpenFileWorker(newSourceInfos);
        wrk.execute();
        sourceInfos.addAll(Arrays.asList(newSourceInfos));
        return OpenFileResult.OK;
    }

    public static void closeFile(SWFList swf) {
        sourceInfos.remove(swf.sourceInfo);
        mainFrame.getPanel().close(swf);
    }

    public static void closeAll() {
        sourceInfos.clear();
        mainFrame.getPanel().closeAll();
    }

    public static boolean saveFileDialog(SWF swf, final SaveFileMode mode) {
        JFileChooser fc = new JFileChooser();
        fc.setCurrentDirectory(new File(Configuration.lastSaveDir.get()));
        String ext = ".swf";
        switch (mode) {
            case SAVE:
            case SAVEAS:
                if (swf.file != null) {
                    ext = Path.getExtension(swf.file);
                }
                break;
            case EXE:
                ext = ".exe";
                break;
        }
        final String extension = ext;
        FileFilter swfFilter = new FileFilter() {
            @Override
            public boolean accept(File f) {
                return (f.getName().toLowerCase().endsWith(".swf")) || (f.isDirectory());
            }

            @Override
            public String getDescription() {
                return AppStrings.translate("filter.swf");
            }
        };
        if (!swf.gfx) {
            fc.setFileFilter(swfFilter);
        } else {
            fc.addChoosableFileFilter(swfFilter);
        }
        FileFilter gfxFilter = new FileFilter() {
            @Override
            public boolean accept(File f) {
                return (f.getName().toLowerCase().endsWith(".gfx")) || (f.isDirectory());
            }

            @Override
            public String getDescription() {
                return AppStrings.translate("filter.gfx");
            }
        };
        if (mode == SaveFileMode.SAVE || mode == SaveFileMode.SAVEAS) {
            if (swf.gfx) {
                fc.setFileFilter(gfxFilter);
            } else {
                fc.addChoosableFileFilter(gfxFilter);
            }
        }
        fc.setAcceptAllFileFilterUsed(false);
        JFrame f = new JFrame();
        View.setWindowIcon(f);
        if (fc.showSaveDialog(f) == JFileChooser.APPROVE_OPTION) {
            File file = Helper.fixDialogFile(fc.getSelectedFile());
            FileFilter selFilter = fc.getFileFilter();
            try {
                String fileName = file.getAbsolutePath();
                if (selFilter == swfFilter) {
                    if (!fileName.toLowerCase().endsWith(extension)) {
                        fileName += extension;
                    }
                    swf.gfx = false;
                }
                if (selFilter == gfxFilter) {
                    if (!fileName.toLowerCase().endsWith(".gfx")) {
                        fileName += ".gfx";
                    }
                    swf.gfx = true;
                }
                Main.saveFile(swf, fileName, mode);
                Configuration.lastSaveDir.set(file.getParentFile().getAbsolutePath());
                return true;
            } catch (IOException ex) {
                View.showMessageDialog(null, AppStrings.translate("error.file.write"));
            }
        }
        return false;
    }

    public static boolean openFileDialog() {
        JFileChooser fc = new JFileChooser();
        if (Configuration.openMultipleFiles.get()) {
            fc.setMultiSelectionEnabled(true);
        }
        fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get()));
        FileFilter allSupportedFilter = new FileFilter() {
            private final String[] supportedExtensions = new String[]{".swf", ".gfx", ".swc", ".zip"};

            @Override
            public boolean accept(File f) {
                String name = f.getName().toLowerCase();
                for (String ext : supportedExtensions) {
                    if (name.endsWith(ext)) {
                        return true;
                    }
                }
                return f.isDirectory();
            }

            @Override
            public String getDescription() {
                String exts = Helper.joinStrings(supportedExtensions, "*%s", "; ");
                return AppStrings.translate("filter.supported") + " (" + exts + ")";
            }
        };
        fc.setFileFilter(allSupportedFilter);
        FileFilter swfFilter = new FileFilter() {
            @Override
            public boolean accept(File f) {
                return (f.getName().toLowerCase().endsWith(".swf")) || (f.isDirectory());
            }

            @Override
            public String getDescription() {
                return AppStrings.translate("filter.swf");
            }
        };
        fc.addChoosableFileFilter(swfFilter);

        FileFilter swcFilter = new FileFilter() {
            @Override
            public boolean accept(File f) {
                return (f.getName().toLowerCase().endsWith(".swc")) || (f.isDirectory());
            }

            @Override
            public String getDescription() {
                return AppStrings.translate("filter.swc");
            }
        };
        fc.addChoosableFileFilter(swcFilter);

        FileFilter gfxFilter = new FileFilter() {
            @Override
            public boolean accept(File f) {
                return (f.getName().toLowerCase().endsWith(".gfx")) || (f.isDirectory());
            }

            @Override
            public String getDescription() {
                return AppStrings.translate("filter.gfx");
            }
        };
        fc.addChoosableFileFilter(gfxFilter);

        FileFilter zipFilter = new FileFilter() {
            @Override
            public boolean accept(File f) {
                return (f.getName().toLowerCase().endsWith(".zip")) || (f.isDirectory());
            }

            @Override
            public String getDescription() {
                return AppStrings.translate("filter.zip");
            }
        };
        fc.addChoosableFileFilter(zipFilter);

        FileFilter binaryFilter = new FileFilter() {
            @Override
            public boolean accept(File f) {
                return true;
            }

            @Override
            public String getDescription() {
                return AppStrings.translate("filter.binary");
            }
        };
        fc.addChoosableFileFilter(binaryFilter);

        fc.setAcceptAllFileFilterUsed(false);
        JFrame f = new JFrame();
        View.setWindowIcon(f);
        int returnVal = fc.showOpenDialog(f);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath());
            File[] selFiles = fc.getSelectedFiles();
            for (File file : selFiles) {
                File selfile = Helper.fixDialogFile(file);
                Main.openFile(selfile.getAbsolutePath(), null);
            }
            return true;
        } else {
            return false;
        }
    }

    public static void displayErrorFrame() {
        ErrorLogFrame.getInstance().setVisible(true);
    }

    private static void initGui() {
        if (GraphicsEnvironment.isHeadless()) {
            System.err.println("Error: Your system does not support Graphic User Interface");
            exit();
        }
        if (Configuration.useRibbonInterface.get()) {
            View.setLookAndFeel();
        } else {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                logger.log(Level.SEVERE, null, ex);
            }
        }
        autoCheckForUpdates();
        offerAssociation();
    }

    public static void showModeFrame() {
        View.execInEventDispatch(new Runnable() {
            @Override
            public void run() {
                ensureMainFrame();
                mainFrame.setVisible(true);
            }
        });

    }

    private static void offerAssociation() {
        boolean offered = Configuration.offeredAssociation.get();
        if (!offered) {
            if (Platform.isWindows()) {
                if ((!ContextMenuTools.isAddedToContextMenu()) && View.showConfirmDialog(null, "Do you want to add FFDec to context menu of SWF files?\n(Can be changed later from main menu)", "Context menu", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) {
                    ContextMenuTools.addToContextMenu(true, false);
                }
            }
        }
        Configuration.offeredAssociation.set(true);
    }

    public static void initLang() {
        if (GraphicsEnvironment.isHeadless()) { //No GUI in OS
            return;
        }
        if (!Configuration.locale.hasValue()) {
            if (Platform.isWindows()) {
                //Load from Installer
                String uninstKey = "{E618D276-6596-41F4-8A98-447D442A77DB}_is1";
                uninstKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + uninstKey;
                try {
                    if (Advapi32Util.registryKeyExists(WinReg.HKEY_LOCAL_MACHINE, uninstKey)) {
                        if (Advapi32Util.registryValueExists(WinReg.HKEY_LOCAL_MACHINE, uninstKey, "NSIS: Language")) {
                            String installedLoc = Advapi32Util.registryGetStringValue(WinReg.HKEY_LOCAL_MACHINE, uninstKey, "NSIS: Language");
                            int lcid = Integer.parseInt(installedLoc);
                            char buf[] = new char[9];
                            int cnt = Kernel32.INSTANCE.GetLocaleInfo(lcid, Kernel32.LOCALE_SISO639LANGNAME, buf, 9);
                            String langCode = new String(buf, 0, cnt).trim().toLowerCase();

                            cnt = Kernel32.INSTANCE.GetLocaleInfo(lcid, Kernel32.LOCALE_SISO3166CTRYNAME, buf, 9);
                            String countryCode = new String(buf, 0, cnt).trim().toLowerCase();

                            List<String> langs = Arrays.asList(SelectLanguageDialog.getAvailableLanguages());
                            for (int i = 0; i < langs.size(); i++) {
                                langs.set(i, langs.get(i).toLowerCase());
                            }

                            String selectedLang = null;

                            if (langs.contains(langCode + "-" + countryCode)) {
                                selectedLang = SelectLanguageDialog.getAvailableLanguages()[langs.indexOf(langCode + "-" + countryCode)];
                            } else if (langs.contains(langCode)) {
                                selectedLang = SelectLanguageDialog.getAvailableLanguages()[langs.indexOf(langCode)];
                            }
                            if (selectedLang != null) {
                                Configuration.locale.set(selectedLang);
                            }
                        }
                    }
                } catch (Exception ex) {
                    //ignore
                }
            }
        }
        Locale.setDefault(Locale.forLanguageTag(Configuration.locale.get()));
        AppStrings.updateLanguage();
        ErrorLogFrame.createNewInstance();

        try {
            Class<?> cl = Class.forName("org.pushingpixels.substance.api.SubstanceLookAndFeel");
            Field field = cl.getDeclaredField("LABEL_BUNDLE");
            field.setAccessible(true);
            field.set(null, null);
        } catch (Throwable ex) {
            logger.log(Level.SEVERE, null, ex);
        }

        UIManager.put("OptionPane.okButtonText", AppStrings.translate("button.ok"));
        UIManager.put("OptionPane.yesButtonText", AppStrings.translate("button.yes"));
        UIManager.put("OptionPane.noButtonText", AppStrings.translate("button.no"));
        UIManager.put("OptionPane.cancelButtonText", AppStrings.translate("button.cancel"));
        UIManager.put("OptionPane.messageDialogTitle", AppStrings.translate("dialog.message.title"));
        UIManager.put("OptionPane.titleText", AppStrings.translate("dialog.select.title"));

        UIManager.put("FileChooser.acceptAllFileFilterText", AppStrings.translate("FileChooser.acceptAllFileFilterText"));
        UIManager.put("FileChooser.lookInLabelText", AppStrings.translate("FileChooser.lookInLabelText"));
        UIManager.put("FileChooser.cancelButtonText", AppStrings.translate("button.cancel"));
        UIManager.put("FileChooser.cancelButtonToolTipText", AppStrings.translate("button.cancel"));
        UIManager.put("FileChooser.openButtonText", AppStrings.translate("FileChooser.openButtonText"));
        UIManager.put("FileChooser.openButtonToolTipText", AppStrings.translate("FileChooser.openButtonToolTipText"));
        UIManager.put("FileChooser.filesOfTypeLabelText", AppStrings.translate("FileChooser.filesOfTypeLabelText"));
        UIManager.put("FileChooser.fileNameLabelText", AppStrings.translate("FileChooser.fileNameLabelText"));
        UIManager.put("FileChooser.listViewButtonToolTipText", AppStrings.translate("FileChooser.listViewButtonToolTipText"));
        UIManager.put("FileChooser.listViewButtonAccessibleName", AppStrings.translate("FileChooser.listViewButtonAccessibleName"));
        UIManager.put("FileChooser.detailsViewButtonToolTipText", AppStrings.translate("FileChooser.detailsViewButtonToolTipText"));
        UIManager.put("FileChooser.detailsViewButtonAccessibleName", AppStrings.translate("FileChooser.detailsViewButtonAccessibleName"));
        UIManager.put("FileChooser.upFolderToolTipText", AppStrings.translate("FileChooser.upFolderToolTipText"));
        UIManager.put("FileChooser.upFolderAccessibleName", AppStrings.translate("FileChooser.upFolderAccessibleName"));
        UIManager.put("FileChooser.homeFolderToolTipText", AppStrings.translate("FileChooser.homeFolderToolTipText"));
        UIManager.put("FileChooser.homeFolderAccessibleName", AppStrings.translate("FileChooser.homeFolderAccessibleName"));
        UIManager.put("FileChooser.fileNameHeaderText", AppStrings.translate("FileChooser.fileNameHeaderText"));
        UIManager.put("FileChooser.fileSizeHeaderText", AppStrings.translate("FileChooser.fileSizeHeaderText"));
        UIManager.put("FileChooser.fileTypeHeaderText", AppStrings.translate("FileChooser.fileTypeHeaderText"));
        UIManager.put("FileChooser.fileDateHeaderText", AppStrings.translate("FileChooser.fileDateHeaderText"));
        UIManager.put("FileChooser.fileAttrHeaderText", AppStrings.translate("FileChooser.fileAttrHeaderText"));
        UIManager.put("FileChooser.openDialogTitleText", AppStrings.translate("FileChooser.openDialogTitleText"));
        UIManager.put("FileChooser.directoryDescriptionText", AppStrings.translate("FileChooser.directoryDescriptionText"));
        UIManager.put("FileChooser.directoryOpenButtonText", AppStrings.translate("FileChooser.directoryOpenButtonText"));
        UIManager.put("FileChooser.directoryOpenButtonToolTipText", AppStrings.translate("FileChooser.directoryOpenButtonToolTipText"));
        UIManager.put("FileChooser.fileDescriptionText", AppStrings.translate("FileChooser.fileDescriptionText"));
        UIManager.put("FileChooser.fileNameLabelText", AppStrings.translate("FileChooser.fileNameLabelText"));
        UIManager.put("FileChooser.helpButtonText", AppStrings.translate("FileChooser.helpButtonText"));
        UIManager.put("FileChooser.helpButtonToolTipText", AppStrings.translate("FileChooser.helpButtonToolTipText"));
        UIManager.put("FileChooser.newFolderAccessibleName", AppStrings.translate("FileChooser.newFolderAccessibleName"));
        UIManager.put("FileChooser.newFolderErrorText", AppStrings.translate("FileChooser.newFolderErrorText"));
        UIManager.put("FileChooser.newFolderToolTipText", AppStrings.translate("FileChooser.newFolderToolTipText"));
        UIManager.put("FileChooser.other.newFolder", AppStrings.translate("FileChooser.other.newFolder"));
        UIManager.put("FileChooser.other.newFolder.subsequent", AppStrings.translate("FileChooser.other.newFolder.subsequent"));
        UIManager.put("FileChooser.win32.newFolder", AppStrings.translate("FileChooser.win32.newFolder"));
        UIManager.put("FileChooser.win32.newFolder.subsequent", AppStrings.translate("FileChooser.win32.newFolder.subsequent"));
        UIManager.put("FileChooser.saveButtonText", AppStrings.translate("FileChooser.saveButtonText"));
        UIManager.put("FileChooser.saveButtonToolTipText", AppStrings.translate("FileChooser.saveButtonToolTipText"));
        UIManager.put("FileChooser.saveDialogTitleText", AppStrings.translate("FileChooser.saveDialogTitleText"));
        UIManager.put("FileChooser.saveInLabelText", AppStrings.translate("FileChooser.saveInLabelText"));
        UIManager.put("FileChooser.updateButtonText", AppStrings.translate("FileChooser.updateButtonText"));
        UIManager.put("FileChooser.updateButtonToolTipText", AppStrings.translate("FileChooser.updateButtonToolTipText"));

        UIManager.put("FileChooser.detailsViewActionLabel.textAndMnemonic", AppStrings.translate("FileChooser.detailsViewActionLabel.textAndMnemonic"));
        UIManager.put("FileChooser.detailsViewButtonToolTip.textAndMnemonic", AppStrings.translate("FileChooser.detailsViewButtonToolTip.textAndMnemonic"));
        UIManager.put("FileChooser.fileAttrHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileAttrHeader.textAndMnemonic"));
        UIManager.put("FileChooser.fileDateHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileDateHeader.textAndMnemonic"));
        UIManager.put("FileChooser.fileNameHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileNameHeader.textAndMnemonic"));
        UIManager.put("FileChooser.fileNameLabel.textAndMnemonic", AppStrings.translate("FileChooser.fileNameLabel.textAndMnemonic"));
        UIManager.put("FileChooser.fileSizeHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileSizeHeader.textAndMnemonic"));
        UIManager.put("FileChooser.fileTypeHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileTypeHeader.textAndMnemonic"));
        UIManager.put("FileChooser.filesOfTypeLabel.textAndMnemonic", AppStrings.translate("FileChooser.filesOfTypeLabel.textAndMnemonic"));
        UIManager.put("FileChooser.folderNameLabel.textAndMnemonic", AppStrings.translate("FileChooser.folderNameLabel.textAndMnemonic"));
        UIManager.put("FileChooser.homeFolderToolTip.textAndMnemonic", AppStrings.translate("FileChooser.homeFolderToolTip.textAndMnemonic"));
        UIManager.put("FileChooser.listViewActionLabel.textAndMnemonic", AppStrings.translate("FileChooser.listViewActionLabel.textAndMnemonic"));
        UIManager.put("FileChooser.listViewButtonToolTip.textAndMnemonic", AppStrings.translate("FileChooser.listViewButtonToolTip.textAndMnemonic"));
        UIManager.put("FileChooser.lookInLabel.textAndMnemonic", AppStrings.translate("FileChooser.lookInLabel.textAndMnemonic"));
        UIManager.put("FileChooser.newFolderActionLabel.textAndMnemonic", AppStrings.translate("FileChooser.newFolderActionLabel.textAndMnemonic"));
        UIManager.put("FileChooser.newFolderToolTip.textAndMnemonic", AppStrings.translate("FileChooser.newFolderToolTip.textAndMnemonic"));
        UIManager.put("FileChooser.refreshActionLabel.textAndMnemonic", AppStrings.translate("FileChooser.refreshActionLabel.textAndMnemonic"));
        UIManager.put("FileChooser.saveInLabel.textAndMnemonic", AppStrings.translate("FileChooser.saveInLabel.textAndMnemonic"));
        UIManager.put("FileChooser.upFolderToolTip.textAndMnemonic", AppStrings.translate("FileChooser.upFolderToolTip.textAndMnemonic"));
        UIManager.put("FileChooser.viewMenuButtonAccessibleName", AppStrings.translate("FileChooser.viewMenuButtonAccessibleName"));
        UIManager.put("FileChooser.viewMenuButtonToolTipText", AppStrings.translate("FileChooser.viewMenuButtonToolTipText"));
        UIManager.put("FileChooser.viewMenuLabel.textAndMnemonic", AppStrings.translate("FileChooser.viewMenuLabel.textAndMnemonic"));
        UIManager.put("FileChooser.newFolderActionLabelText", AppStrings.translate("FileChooser.newFolderActionLabelText"));
        UIManager.put("FileChooser.listViewActionLabelText", AppStrings.translate("FileChooser.listViewActionLabelText"));
        UIManager.put("FileChooser.detailsViewActionLabelText", AppStrings.translate("FileChooser.detailsViewActionLabelText"));
        UIManager.put("FileChooser.refreshActionLabelText", AppStrings.translate("FileChooser.refreshActionLabelText"));
        UIManager.put("FileChooser.sortMenuLabelText", AppStrings.translate("FileChooser.sortMenuLabelText"));
        UIManager.put("FileChooser.viewMenuLabelText", AppStrings.translate("FileChooser.viewMenuLabelText"));
        UIManager.put("FileChooser.fileSizeKiloBytes", AppStrings.translate("FileChooser.fileSizeKiloBytes"));
        UIManager.put("FileChooser.fileSizeMegaBytes", AppStrings.translate("FileChooser.fileSizeMegaBytes"));
        UIManager.put("FileChooser.fileSizeGigaBytes", AppStrings.translate("FileChooser.fileSizeGigaBytes"));
        UIManager.put("FileChooser.folderNameLabelText", AppStrings.translate("FileChooser.folderNameLabelText"));

        UIManager.put("ColorChooser.okText", AppStrings.translate("ColorChooser.okText"));
        UIManager.put("ColorChooser.cancelText", AppStrings.translate("ColorChooser.cancelText"));
        UIManager.put("ColorChooser.resetText", AppStrings.translate("ColorChooser.resetText"));
        UIManager.put("ColorChooser.previewText", AppStrings.translate("ColorChooser.previewText"));
        UIManager.put("ColorChooser.swatchesNameText", AppStrings.translate("ColorChooser.swatchesNameText"));
        UIManager.put("ColorChooser.swatchesRecentText", AppStrings.translate("ColorChooser.swatchesRecentText"));
        UIManager.put("ColorChooser.sampleText", AppStrings.translate("ColorChooser.sampleText"));

        Helper.decompilationErrorAdd = AppStrings.translate(Configuration.autoDeobfuscate.get() ? "deobfuscation.comment.failed" : "deobfuscation.comment.tryenable");
    }

    /**
     * @param args the command line arguments
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {

        String pluginPath = Configuration.pluginPath.get();
        if (pluginPath != null && !pluginPath.isEmpty()) {
            try {
                SWFDecompilerPlugin.loadPlugin(pluginPath);
            } catch (Throwable e) {
                View.showMessageDialog(null, "Failed to load plugin: " + pluginPath);
            }
        }

        AppStrings.setResourceClass(MainFrame.class);
        initLogging(Configuration.debugMode.get());
        initLang();

        if (Configuration.cacheOnDisk.get()) {
            Cache.setStorageType(Cache.STORAGE_FILES);
        } else {
            Cache.setStorageType(Cache.STORAGE_MEMORY);
        }

        if (args.length == 0) {
            initGui();
            showModeFrame();
        } else {
            String fileToOpen = CommandLineArgumentParser.parseArguments(args);
            if (fileToOpen != null) {
                initGui();
                openFile(fileToOpen, null);
            }
        }
    }

    public static String tempFile(String url) throws IOException {
        File f = new File(Configuration.getFFDecHome() + "saved" + File.separator);
        if (!f.exists()) {
            if (!f.mkdirs()) {
                if (!f.exists()) {
                    throw new IOException("cannot create directory " + f);
                }
            }
        }
        return Configuration.getFFDecHome() + "saved" + File.separator + "asdec_" + Integer.toHexString(url.hashCode()) + ".tmp";

    }

    public static void removeTrayIcon() {
        if (SystemTray.isSupported()) {
            SystemTray tray = SystemTray.getSystemTray();
            if (trayIcon != null) {
                tray.remove(trayIcon);
                trayIcon = null;
            }
        }
    }

    public static void switchProxy() {
        proxyFrame.switchState();
        if (stopMenuItem != null) {
            if (proxyFrame.isRunning()) {
                stopMenuItem.setLabel(AppStrings.translate("proxy.stop"));
            } else {
                stopMenuItem.setLabel(AppStrings.translate("proxy.start"));
            }
        }
    }

    public static void addTrayIcon() {
        if (trayIcon != null) {
            return;
        }
        if (SystemTray.isSupported()) {
            SystemTray tray = SystemTray.getSystemTray();
            trayIcon = new TrayIcon(View.loadImage("proxy16"), ApplicationInfo.VENDOR + " " + ApplicationInfo.SHORT_APPLICATION_NAME + " " + AppStrings.translate("proxy"));
            trayIcon.setImageAutoSize(true);
            PopupMenu trayPopup = new PopupMenu();

            ActionListener trayListener = new ActionListener() {
                /**
                 * Invoked when an action occurs.
                 */
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (e.getActionCommand().equals("EXIT")) {
                        Main.exit();
                    }
                    if (e.getActionCommand().equals("SHOW")) {
                        Main.showProxy();
                    }
                    if (e.getActionCommand().equals("SWITCH")) {
                        Main.switchProxy();
                    }
                }
            };

            MenuItem showMenuItem = new MenuItem(AppStrings.translate("proxy.show"));
            showMenuItem.setActionCommand("SHOW");
            showMenuItem.addActionListener(trayListener);
            trayPopup.add(showMenuItem);
            stopMenuItem = new MenuItem(AppStrings.translate("proxy.start"));
            stopMenuItem.setActionCommand("SWITCH");
            stopMenuItem.addActionListener(trayListener);
            trayPopup.add(stopMenuItem);
            trayPopup.addSeparator();
            MenuItem exitMenuItem = new MenuItem(AppStrings.translate("exit"));
            exitMenuItem.setActionCommand("EXIT");
            exitMenuItem.addActionListener(trayListener);
            trayPopup.add(exitMenuItem);

            trayIcon.setPopupMenu(trayPopup);
            trayIcon.addMouseListener(new MouseAdapter() {
                /**
                 * {@inheritDoc}
                 */
                @Override
                public void mouseClicked(MouseEvent e) {
                    if (e.getButton() == MouseEvent.BUTTON1) {
                        Main.showProxy();
                    }
                }
            });
            try {
                tray.add(trayIcon);
            } catch (AWTException ex) {
            }
        }
    }

    public static void exit() {
        Configuration.saveConfig();
        /*if (proxyFrame != null && Main.proxyFrame.isVisible()) {
         return;
         }
         if (loadFromMemoryFrame != null && Main.loadFromMemoryFrame.isVisible()) {
         return;
         }
         if (loadFromCacheFrame != null && loadFromCacheFrame.isVisible()) {
         return;
         }*/
        if (mainFrame != null && mainFrame.getPanel() != null) {
            mainFrame.getPanel().unloadFlashPlayer();
        }
        System.exit(0);
    }

    public static void about() {
        (new AboutDialog()).setVisible(true);
    }

    public static void advancedSettings() {
        (new AdvancedSettingsDialog()).setVisible(true);
    }

    private static boolean isDebuggerClass(String tested, String cls) {
        if (tested == null) {
            return false;
        }
        if (cls == null) {
            cls = "";
        } else {
            cls = "\\." + Pattern.quote(cls);
        }
        return tested.matches(Pattern.quote(DEBUGGER_PACKAGE) + "(\\.pkg[a-f0-9]+)?" + cls);
    }

    private static String byteArrayToHex(byte[] a) {
        StringBuilder sb = new StringBuilder(a.length * 2);
        for (byte b : a) {
            sb.append(String.format("%02x", b & 0xff));
        }
        return sb.toString();
    }

    public static void replaceTraceCalls(String fname) {
        SWF swf = getMainFrame().getPanel().getCurrentSwf();
        if (hasDebugger(swf)) {
            String debuggerPkg = getDebuggerPackage(swf);
            //change trace to fname
            for (ABCContainerTag ct : swf.abcList) {
                ABC a = ct.getABC();
                for (int i = 1; i < a.constants.constant_multiname.size(); i++) {
                    Multiname m = a.constants.constant_multiname.get(i);
                    if ("trace".equals(m.getNameWithNamespace(a.constants, true))) {
                        m.namespace_index = a.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, a.constants.getStringId(debuggerPkg, true)), 0, true);
                        m.name_index = a.constants.getStringId(fname, true);
                        ((Tag) ct).setModified(true);
                    }
                }
            }
        }
    }

    public static void switchDebugger() {
        int port = Configuration.debuggerPort.get();
        SWF swf = getMainFrame().getPanel().getCurrentSwf();
        ABCContainerTag found = getDebuggerABCTag(swf);
        if (found != null) {
            swf.tags.remove((Tag) found);
            swf.abcList.remove(found);

            //Change all debugger calls to normal trace
            for (ABCContainerTag ct : swf.abcList) {
                ABC a = ct.getABC();
                for (int i = 1; i < a.constants.constant_multiname.size(); i++) {
                    Multiname m = a.constants.constant_multiname.get(i);
                    if (isDebuggerClass(m.getNameWithNamespace(a.constants, true), "debugTrace")
                            || isDebuggerClass(m.getNameWithNamespace(a.constants, true), "debugAlert")
                            || isDebuggerClass(m.getNameWithNamespace(a.constants, true), "debugSocket")
                            || isDebuggerClass(m.getNameWithNamespace(a.constants, true), "debugConsole")) {
                        m.name_index = a.constants.getStringId("trace", true);
                        m.namespace_index = a.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, a.constants.getStringId("", true)), 0, true);
                        ((Tag) ct).setModified(true);
                    }
                }
            }
        } else {
            Random rnd = new Random();
            byte rb[] = new byte[16];
            rnd.nextBytes(rb);
            String rhex = byteArrayToHex(rb);
            try {
                //load debug swf
                SWF debugSWF = new SWF(Main.class.getClassLoader().getResourceAsStream("com/jpexs/decompiler/flash/gui/debugger/debug.swf"), false);

                ABCContainerTag firstAbc = swf.abcList.get(0);
                String newdebuggerpkg = DEBUGGER_PACKAGE;

                if (Configuration.randomDebuggerPackage.get()) {
                    newdebuggerpkg += ".pkg" + rhex;
                }

                //add debug ABC tags to main SWF
                for (ABCContainerTag ds : debugSWF.abcList) {
                    ABC a = ds.getABC();
                    //Append random hex to Debugger package name
                    for (int i = 1; i < a.constants.constant_namespace.size(); i++) {
                        if (a.constants.constant_namespace.get(i).hasName(DEBUGGER_PACKAGE, a.constants)) {
                            a.constants.constant_namespace.get(i).name_index = a.constants.getStringId(newdebuggerpkg, true);
                        }
                    }
                    //Set debugger port to actually set port
                    for (int i = 0; i < a.constants.constant_int.size(); i++) {
                        if (a.constants.constant_int.get(i) == 123456L) {
                            a.constants.constant_int.set(i, (long) port);
                        }
                    }
                    //Add to target SWF
                    ((Tag) ds).setSwf(swf);
                    swf.tags.add(swf.tags.indexOf(firstAbc), (Tag) ds);
                    swf.abcList.add(swf.abcList.indexOf(firstAbc), ds);
                    ((Tag) ds).setModified(true);
                }

            } catch (Exception ex) {
                Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Error while attaching debugger", ex);
                //ignore
            }

        }
        initDebugger();
    }

    private static void initDebugger() {
        if (debugger == null) {
            debugger = new Debugger(Configuration.debuggerPort.get());
            debugger.start();
        }
    }

    public static void debuggerShowLog() {
        initDebugger();
        if (debugDialog == null) {
            debugDialog = new DebugLogDialog(debugger);
        }
        debugDialog.setVisible(true);
    }

    public static void autoCheckForUpdates() {
        if (Configuration.checkForUpdatesAuto.get()) {
            Calendar lastUpdatesCheckDate = Configuration.lastUpdatesCheckDate.get();
            if ((lastUpdatesCheckDate == null) || (lastUpdatesCheckDate.getTime().getTime() < Calendar.getInstance().getTime().getTime() - Configuration.checkForUpdatesDelay.get())) {
                new SwingWorker() {
                    @Override
                    protected Object doInBackground() throws Exception {
                        checkForUpdates();
                        return null;
                    }
                }.execute();
            }
        }
    }

    public static boolean checkForUpdates() {
        String currentVersion = ApplicationInfo.version;
        if (currentVersion.equals("unknown")) {
            // sometimes during development the version information is not available
            return false;
        }

        List<String> accepted = new ArrayList<>();
        if (Configuration.checkForUpdatesStable.get()) {
            accepted.add("stable");
        }
        if (Configuration.checkForUpdatesNightly.get()) {
            accepted.add("nightly");
        }

        if (accepted.isEmpty()) {
            return false;
        }

        String acceptVersions = "";
        for (String a : accepted) {
            if (!acceptVersions.isEmpty()) {
                acceptVersions += ",";
            }
            acceptVersions += a;
        }
        try {
            Socket sock = new Socket("www.free-decompiler.com", 80);
            OutputStream os = sock.getOutputStream();
            String currentLoc = Configuration.locale.get("en");
            os.write(("GET /flash/update.html?action=check&currentVersion=" + URLEncoder.encode(currentVersion, "UTF-8")
                    + "&currentRevision=" + URLEncoder.encode(ApplicationInfo.revision, "UTF-8")
                    + "&currentVersionMajor=" + URLEncoder.encode("" + ApplicationInfo.version_major, "UTF-8")
                    + "&currentVersionMinor=" + URLEncoder.encode("" + ApplicationInfo.version_minor, "UTF-8")
                    + "&currentVersionRelease=" + URLEncoder.encode("" + ApplicationInfo.version_release, "UTF-8")
                    + "&currentVersionBuild=" + URLEncoder.encode("" + ApplicationInfo.version_build, "UTF-8")
                    + "&currentNightly=" + (ApplicationInfo.nightly ? "1" : "0") + " HTTP/1.1\r\n"
                    + "Host: www.free-decompiler.com\r\n"
                    + "X-Accept-Versions: " + acceptVersions + "\r\n"
                    + "X-Update-Major: " + UPDATE_SYSTEM_MAJOR + "\r\n"
                    + "X-Update-Minor: " + UPDATE_SYSTEM_MINOR + "\r\n"
                    + "User-Agent: " + ApplicationInfo.shortApplicationVerName + "\r\n"
                    + "Accept-Language: " + currentLoc + ("en".equals(currentLoc) ? "" : ", en;q=0.8") + "\r\n"
                    + "Connection: close\r\n"
                    + "\r\n").getBytes());
            BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream()));
            String s;
            boolean start = false;
            final java.util.List<Version> versions = new ArrayList<>();
            String header = "";
            Pattern headerPat = Pattern.compile("\\[([a-zA-Z0-9]+)\\]");
            int updateMajor;
            int updateMinor;
            Version ver = null;
            while ((s = br.readLine()) != null) {
                if (start) {
                    Matcher m = headerPat.matcher(s);
                    if (m.matches()) {
                        header = m.group(1);
                        if (header.equals("version")) {
                            ver = new Version();
                            versions.add(ver);
                        }
                        if (header.equals("noversion")) {
                            break;
                        }
                    } else {
                        if (s.contains("=")) {
                            String key = s.substring(0, s.indexOf('='));
                            String val = s.substring(s.indexOf('=') + 1);
                            if ("updateSystem".equals(header)) {
                                if (key.equals("majorVersion")) {
                                    updateMajor = Integer.parseInt(val);
                                    if (updateMajor > UPDATE_SYSTEM_MAJOR) {
                                        break;
                                    }
                                }
                                if (key.equals("minorVersion")) {
                                    updateMinor = Integer.parseInt(val);
                                }
                            }
                            if ("version".equals(header) && (ver != null)) {
                                if (key.equals("versionId")) {
                                    ver.versionId = Integer.parseInt(val);
                                }
                                if (key.equals("versionName")) {
                                    ver.versionName = val;
                                }
                                if (key.equals("nightly")) {
                                    ver.nightly = val.equals("true");
                                }
                                if (key.equals("revision")) {
                                    ver.revision = val;
                                }
                                if (key.equals("build")) {
                                    ver.build = Integer.parseInt(val);
                                }
                                if (key.equals("major")) {
                                    ver.major = Integer.parseInt(val);
                                }
                                if (key.equals("minor")) {
                                    ver.minor = Integer.parseInt(val);
                                }
                                if (key.equals("release")) {
                                    ver.release = Integer.parseInt(val);
                                }
                                if (key.equals("longVersionName")) {
                                    ver.longVersionName = val;
                                }
                                if (key.equals("releaseDate")) {
                                    ver.releaseDate = val;
                                }
                                if (key.equals("appName")) {
                                    ver.appName = val;
                                }
                                if (key.equals("appFullName")) {
                                    ver.appFullName = val;
                                }
                                if (key.equals("updateLink")) {
                                    ver.updateLink = val;
                                }
                                if (key.equals("change[]")) {
                                    String changeType = val.substring(0, val.indexOf('|'));
                                    String change = val.substring(val.indexOf('|') + 1);
                                    if (!ver.changes.containsKey(changeType)) {
                                        ver.changes.put(changeType, new ArrayList<String>());
                                    }
                                    List<String> chlist = ver.changes.get(changeType);
                                    chlist.add(change);
                                }
                            }
                        }
                    }
                }
                if (s.isEmpty()) {
                    start = true;
                }
            }

            if (!versions.isEmpty()) {
                View.execInEventDispatch(new Runnable() {
                    @Override
                    public void run() {
                        NewVersionDialog newVersionDialog = new NewVersionDialog(versions);
                        newVersionDialog.setVisible(true);
                        Configuration.lastUpdatesCheckDate.set(Calendar.getInstance());
                    }
                });

                return true;
            }
        } catch (IOException | NumberFormatException ex) {
            return false;
        }
        Configuration.lastUpdatesCheckDate.set(Calendar.getInstance());
        return false;
    }
    private static FileHandler fileTxt;

    public static void clearLogFile() {
        Logger logger = Logger.getLogger("");
        if (fileTxt != null) {
            fileTxt.flush();
            fileTxt.close();
            logger.removeHandler(fileTxt);
        }

        String fileName;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-hh-mm-ss");

        try {
            fileName = Configuration.getFFDecHome() + File.separator + "logs" + File.separator;
            if (Configuration.useDetailedLogging.get()) {
                fileName += "log-" + sdf.format(new Date()) + ".txt";
            } else {
                fileName += "log.txt";
            }
            File f = new File(fileName).getParentFile();
            if (!f.exists()) {
                f.mkdir();
            }
            fileTxt = new FileHandler(fileName);
        } catch (IOException | SecurityException ex) {
            logger.log(Level.SEVERE, null, ex);
        }

        Formatter formatterTxt = new LogFormatter();
        fileTxt.setFormatter(formatterTxt);
        logger.addHandler(fileTxt);

        if (!GraphicsEnvironment.isHeadless()) {
            ErrorLogFrame.getInstance().clearErrorState();
        }

        sdf = new SimpleDateFormat("yyyy-MM-dd");
        logger.log(Level.INFO, "Date: {0}", sdf.format(new Date()));
        logger.log(Level.INFO, ApplicationInfo.applicationVerName);
        logger.log(Level.INFO, "{0} {1} {2}", new Object[]{
            System.getProperty("os.name"), System.getProperty("os.version"), System.getProperty("os.arch")});
        logger.log(Level.INFO, "{0} {1} {2}", new Object[]{
            System.getProperty("java.version"), System.getProperty("java.vendor"), System.getProperty("os.arch")});
    }

    public static void initLogging(boolean debug) {
        try {
            Logger logger = Logger.getLogger("");
            logger.setLevel(Configuration.logLevel);

            int handlerCount = logger.getHandlers().length;
            for (int i = handlerCount - 1; i >= 0; i--) {
                logger.removeHandler(logger.getHandlers()[i]);
            }
            ConsoleHandler conHan = new ConsoleHandler();
            conHan.setLevel(debug ? Level.CONFIG : Level.WARNING);
            SimpleFormatter formatterTxt = new SimpleFormatter();
            conHan.setFormatter(formatterTxt);
            logger.addHandler(conHan);
            clearLogFile();

        } catch (Exception ex) {
            throw new RuntimeException("Problems with creating the log files");
        }
    }
}
