/*
 *  Copyright (C) 2010-2021 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.abc;

import com.jpexs.decompiler.flash.abc.types.Multiname;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.gui.AppStrings;
import com.jpexs.decompiler.flash.gui.DebuggerHandler;
import com.jpexs.decompiler.flash.gui.HeaderLabel;
import com.jpexs.decompiler.flash.gui.Main;
import com.jpexs.decompiler.flash.gui.TagEditorPanel;
import com.jpexs.decompiler.flash.gui.View;
import com.jpexs.decompiler.flash.gui.ViewMessages;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.util.HashMap;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.border.BevelBorder;

/**
 *
 * @author JPEXS
 */
public class DetailPanel extends JPanel implements TagEditorPanel {

    public MethodTraitDetailPanel methodTraitPanel;

    public JPanel unsupportedTraitPanel;

    public SlotConstTraitDetailPanel slotConstTraitPanel;

    public static final String METHOD_GETTER_SETTER_TRAIT_CARD = "abc.detail.methodtrait";

    public static final String UNSUPPORTED_TRAIT_CARD = "abc.detail.unsupported";

    public static final String SLOT_CONST_TRAIT_CARD = "abc.detail.slotconsttrait";

    private final JPanel innerPanel;

    public JButton saveButton = new JButton(AppStrings.translate("button.save"), View.getIcon("save16"));

    public JButton editButton = new JButton(AppStrings.translate("button.edit.script.disassembled"), View.getIcon("edit16"));

    public JButton cancelButton = new JButton(AppStrings.translate("button.cancel"), View.getIcon("cancel16"));

    private final HashMap<String, JComponent> cardMap = new HashMap<>();

    private String selectedCard;

    private final JLabel selectedLabel;

    private final JPanel buttonsPanel;

    private final ABCPanel abcPanel;

    private final JLabel traitNameLabel;

    private boolean debugRunning = false;

    private boolean buttonsShouldBeShown = false;

    private final DebuggerHandler.ConnectionListener conListener;

    public DetailPanel(ABCPanel abcPanel) {
        this.abcPanel = abcPanel;
        innerPanel = new JPanel();
        CardLayout layout = new CardLayout();
        innerPanel.setLayout(layout);
        methodTraitPanel = new MethodTraitDetailPanel(abcPanel);
        cardMap.put(METHOD_GETTER_SETTER_TRAIT_CARD, methodTraitPanel);

        unsupportedTraitPanel = new JPanel(new BorderLayout());
        JLabel unsup = new JLabel("<html>" + AppStrings.translate("info.selecttrait") + "</html>", SwingConstants.CENTER);
        unsupportedTraitPanel.add(unsup, BorderLayout.CENTER);

        cardMap.put(UNSUPPORTED_TRAIT_CARD, unsupportedTraitPanel);

        slotConstTraitPanel = new SlotConstTraitDetailPanel(abcPanel.decompiledTextArea);
        cardMap.put(SLOT_CONST_TRAIT_CARD, slotConstTraitPanel);

        for (String key : cardMap.keySet()) {
            innerPanel.add(cardMap.get(key), key);
        }

        setLayout(new BorderLayout());
        add(innerPanel, BorderLayout.CENTER);

        editButton.setMargin(new Insets(3, 3, 3, 10));
        saveButton.setMargin(new Insets(3, 3, 3, 10));
        cancelButton.setMargin(new Insets(3, 3, 3, 10));

        buttonsPanel = new JPanel();
        buttonsPanel.setLayout(new FlowLayout());
        saveButton.addActionListener(this::saveButtonActionPerformed);
        editButton.addActionListener(this::editButtonActionPerformed);
        cancelButton.addActionListener(this::cancelButtonActionPerformed);
        saveButton.setVisible(false);
        cancelButton.setVisible(false);
        buttonsPanel.setBorder(new BevelBorder(BevelBorder.RAISED));
        buttonsPanel.add(editButton);
        buttonsPanel.add(saveButton);
        buttonsPanel.add(cancelButton);
        add(buttonsPanel, BorderLayout.SOUTH);
        selectedCard = UNSUPPORTED_TRAIT_CARD;
        layout.show(innerPanel, UNSUPPORTED_TRAIT_CARD);
        buttonsPanel.setVisible(false);

        conListener = new DebuggerHandler.ConnectionListener() {
            @Override
            public void connected() {
                synchronized (DetailPanel.this) {
                    debugRunning = true;
                    if (buttonsPanel != null) {
                        buttonsPanel.setVisible(false);
                    }
                }
            }

            @Override
            public void disconnected() {
                synchronized (DetailPanel.this) {
                    debugRunning = false;

                    if (buttonsPanel != null) {
                        buttonsPanel.setVisible(buttonsShouldBeShown);
                    }
                }
            }
        };
        Main.getDebugHandler().addConnectionListener(conListener);

        selectedLabel = new HeaderLabel("");
        selectedLabel.setText(selectedCard);
        //selectedLabel.setBorder(new BevelBorder(BevelBorder.RAISED));
        selectedLabel.setHorizontalAlignment(SwingConstants.CENTER);
        JPanel topPanel = new JPanel(new BorderLayout());
        topPanel.add(selectedLabel, BorderLayout.NORTH);
        traitNameLabel = new JLabel("");
        JPanel traitInfoPanel = new JPanel();
        traitInfoPanel.setLayout(new BoxLayout(traitInfoPanel, BoxLayout.LINE_AXIS));
        //traitInfoPanel.add(new JLabel("  " + translate("abc.detail.traitname")));
        traitInfoPanel.add(traitNameLabel);
        topPanel.add(traitInfoPanel, BorderLayout.CENTER);
        methodTraitPanel.methodCodePanel.getSourceTextArea().addTextChangedListener(this::editorTextChanged);
        slotConstTraitPanel.slotConstEditor.addTextChangedListener(this::editorTextChanged);
        add(topPanel, BorderLayout.NORTH);
    }

    private void editorTextChanged() {
        setModified(true);
    }

    private boolean isModified() {
        return saveButton.isVisible() && saveButton.isEnabled();
    }

    private void setModified(boolean value) {
        saveButton.setEnabled(value);
        cancelButton.setEnabled(value);
    }

    public void setEditMode(boolean val) {
        slotConstTraitPanel.setEditMode(val);
        methodTraitPanel.setEditMode(val);
        saveButton.setVisible(val);
        saveButton.setEnabled(false);
        editButton.setVisible(!val);
        cancelButton.setVisible(val);
        selectedLabel.setIcon(val ? View.getIcon("editing16") : null);
    }

    public void showCard(final String name, final Trait trait, int traitIndex) {
        View.execInEventDispatch(() -> {
            CardLayout layout = (CardLayout) innerPanel.getLayout();
            layout.show(innerPanel, name);
            boolean b = cardMap.get(name) instanceof TraitDetail;
            boolean drun;
            synchronized (this) {
                buttonsShouldBeShown = b;
                drun = debugRunning;
            }
            buttonsPanel.setVisible(b && !drun);

            TraitDetail newDetail = null;
            if (b) {
                newDetail = (TraitDetail) cardMap.get(name);
            }
            for (JComponent v : cardMap.values()) {
                if (v instanceof TraitDetail) {
                    if (v != newDetail) {
                        TraitDetail oldDetail = (TraitDetail) v;
                        oldDetail.setActive(false);
                    }
                }
            }
            if (newDetail != null) {
                newDetail.setActive(true);
            }

            selectedCard = name;
            String detailTitleStr = AppStrings.translate("panel.disassembled") + AppStrings.translate("abc.detail.split") + "%pcode_type%";
            String traitStr = AppStrings.translate("abc.detail.trait");
            String pcodeTypeStr = null;

            if (trait != null) {
                String traitTypeLang;
                switch (trait.kindType) {
                    case Trait.TRAIT_CLASS:
                        traitTypeLang = "abc.detail.trait.class";
                        break;
                    case Trait.TRAIT_CONST:
                        traitTypeLang = "abc.detail.trait.const";
                        break;
                    case Trait.TRAIT_FUNCTION:
                        traitTypeLang = "abc.detail.trait.function";
                        break;
                    case Trait.TRAIT_GETTER:
                        traitTypeLang = "abc.detail.trait.getter";
                        break;
                    case Trait.TRAIT_METHOD:
                        traitTypeLang = "abc.detail.trait.method";
                        break;
                    case Trait.TRAIT_SETTER:
                        traitTypeLang = "abc.detail.trait.setter";
                        break;
                    case Trait.TRAIT_SLOT:
                        traitTypeLang = "abc.detail.trait.slot";
                        break;
                    default:
                        traitTypeLang = "abc.detail.unsupported";
                }
                traitStr = traitStr.replace("%trait_type%", AppStrings.translate(traitTypeLang));
                pcodeTypeStr = traitStr;
            }
            String specialMethodTypeLang = null;
            switch (traitIndex) {
                case GraphTextWriter.TRAIT_SCRIPT_INITIALIZER:
                    specialMethodTypeLang = "abc.detail.specialmethod.scriptinitializer";
                    break;
                case GraphTextWriter.TRAIT_CLASS_INITIALIZER:
                    specialMethodTypeLang = "abc.detail.specialmethod.classinitializer";
                    break;
                case GraphTextWriter.TRAIT_INSTANCE_INITIALIZER:
                    specialMethodTypeLang = "abc.detail.specialmethod.instanceinitializer";
                    break;
            }
            if (specialMethodTypeLang != null) {
                String specialMethodStr = AppStrings.translate("abc.detail.specialmethod").replace("%specialmethod_type%", AppStrings.translate(specialMethodTypeLang));
                pcodeTypeStr = specialMethodStr;
            }
            if (pcodeTypeStr == null) {
                if (METHOD_GETTER_SETTER_TRAIT_CARD.equals(name) && trait == null) {
                    pcodeTypeStr = AppStrings.translate("abc.detail.innerfunction");
                } else {
                    pcodeTypeStr = AppStrings.translate("abc.detail.unsupported");
                }
            }
            detailTitleStr = detailTitleStr.replace("%pcode_type%", pcodeTypeStr);

            selectedLabel.setText(detailTitleStr);
            if (trait == null) {
                traitNameLabel.setText("-");
            } else if (abcPanel != null) {
                Multiname traitName = trait.getName(abcPanel.abc);
                String traitNameStr = traitName == null ? "" : traitName.getName(abcPanel.abc.constants, null, false, true);
                traitNameLabel.setText(traitNameStr);
            }
        });

    }

    private void editButtonActionPerformed(ActionEvent evt) {
        setEditMode(true);
        methodTraitPanel.methodCodePanel.focusEditor();
    }

    private void cancelButtonActionPerformed(ActionEvent evt) {
        setEditMode(false);
        abcPanel.decompiledTextArea.resetEditing();
    }

    private void saveButtonActionPerformed(ActionEvent evt) {
        if (cardMap.get(selectedCard) instanceof TraitDetail) {
            if (((TraitDetail) cardMap.get(selectedCard)).save()) {
                DecompiledEditorPane decompiledTextArea = abcPanel.decompiledTextArea;
                int lastTrait = decompiledTextArea.lastTraitIndex;

                Runnable reloadComplete = new Runnable() {
                    @Override
                    public void run() {
                        decompiledTextArea.removeScriptListener(this);
                        if (lastTrait == GraphTextWriter.TRAIT_UNKNOWN) {
                            decompiledTextArea.gotoLastMethod();
                        } else {
                            decompiledTextArea.gotoTrait(lastTrait);
                        }
                        setEditMode(false);
                        ViewMessages.showMessageDialog(DetailPanel.this, AppStrings.translate("message.trait.saved"), AppStrings.translate("dialog.message.title"), JOptionPane.INFORMATION_MESSAGE, Configuration.showTraitSavedMessage);
                    }
                };

                decompiledTextArea.addScriptListener(reloadComplete);
                decompiledTextArea.reloadClass();
            }
        }
    }

    @Override
    public boolean tryAutoSave() {
        // todo: implement
        return false;
    }

    @Override
    public boolean isEditing() {
        return saveButton.isVisible() && saveButton.isEnabled();
    }
}
