import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;

import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
import javax.swing.DefaultCellEditor;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.border.EmptyBorder;
import javax.swing.JLabel;
import javax.swing.JOptionPane;

import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.border.TitledBorder;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.border.LineBorder;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.URL;

import javax.swing.JTextArea;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JSeparator;
import javax.swing.JTable;

import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.imageio.ImageIO;
import javax.swing.AbstractButton;
import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;


import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.table.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.util.List;
import java.awt.*;

import java.awt.*;
import java.beans.DefaultPersistenceDelegate;
import java.beans.Encoder;
import java.beans.Statement;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;

import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;

// MVC   modelisation  visualization  command

public class jdialog01 extends JDialog 
{
	
	private static JTable table;
	
	private JTree tree;
	
	private static DefaultTableModel xxxmodel;
	
	private boolean close_Jdialog = false;
	
	private TreeModel model;
	
	private List<TreeNode[]> expanded;
	
	private JButton btn_load_tree;
	private JButton btn_save_tree;
	
	private JButton btn_load_table;
	private JButton btn_save_table;
		
	private File file = new File("TableModel.xml");	
	
	private DefaultTableModel tableModel; 
	 
	private JFileChooser myJFileChooser = new JFileChooser(new File(".")); 
	
	public jdialog01(JFrame f, boolean modal) 
	{
       super(f, modal);

       // fermer la fenetre
       addWindowListener(new java.awt.event.WindowAdapter() 
       {
           public void windowClosing(java.awt.event.WindowEvent e) 
           {
        	   //saveStatus();
               serialize();
               //System.exit(0);
        	   dispose();
           }
       });
       
       
       expanded = new ArrayList<TreeNode[]>();
   
       init();
     
	}

	// envoi les datas
	public void initialise( Object [] o)
	{
		
	}
 
	// retourne les datas
	public Object [] getDonnees()
	{
		return null;
	
	}
  
	private void init()
	{
	  	setResizable( false );
		setBounds(100, 100, 926, 550);
		setTitle("Properties");
		getContentPane().setLayout(null);
     	
		ImageIcon icon = null;
		icon = createImageIcon("resources/settings.png", "");
		setIconImage( icon.getImage() );
		
		setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
		setTitle("Test definition");
		
	
	    //model = tree.getModel();
	
		
		JScrollPane scrollPane_2 = new JScrollPane();
		scrollPane_2.setBounds(50, 39, 183, 194);
		getContentPane().add(scrollPane_2);
		
		
		DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
		
		tree = new JTree(root);
		
		
		
		scrollPane_2.setViewportView(tree);
		
		 tree.setComponentPopupMenu(new TreePopupMenu());
		
		 
		 tree.addMouseListener(new MouseAdapter() {
		      public void mouseClicked(MouseEvent me) {
		        doMouseClicked(me);
		      }
		    });
		
		JScrollPane scrollPane = new JScrollPane();
		scrollPane.setBounds(294, 39, 532, 159);
		getContentPane().add(scrollPane);
		
		String[] columnNames =       {"Season", "Sport" , "rien"};
	     
	    Object[][] data = {
				{null, null, null},
				{null, null, null},
				{null, null, null},
				{null, null, null},
				{null, null, null},
			};
		
		tableModel = new DefaultTableModel(data, columnNames);
	    table = new JTable(tableModel);
	
		//avant
/*		table = new JTable();
		table.setModel(new DefaultTableModel(
			new Object[][] {
				{null, null, null},
				{null, null, null},
				{null, null, null},
				{null, null, null},
				{null, null, null},
			},
			new String[] {
				"New column", "New column", "New column"
			}
		));
*/		
		
		scrollPane.setViewportView(table);

		//DefaultTableModel xxxmodel = new DefaultTableModel();
		 //DefaultTableModel xxxmodel = (DefaultTableModel) table.getModel();

		/*******************/
		
		btn_load_tree  = new JButton("Load");
		btn_load_tree.addActionListener(new ActionListener() 
		{
			public void actionPerformed(ActionEvent arg0) 
			{
				System.out.println("Btn load  \n");
				deSerialize();
		        //restoreStatus();
				
		     
			}
		});
		btn_load_tree.setBounds(50, 257, 89, 23);
		getContentPane().add(btn_load_tree);
				
		btn_save_tree = new JButton("Save");
		btn_save_tree.addActionListener(new ActionListener() 
		{
			public void actionPerformed(ActionEvent arg0) 
			{
				System.out.println("Btn save  \n");
				
				//saveStatus();
				serialize();
			
			}
		});
		btn_save_tree.setBounds(161, 257, 89, 23);
		getContentPane().add(btn_save_tree);
		
		/*******************/
		
		
		btn_load_table  = new JButton("Load");
		btn_load_table.addActionListener(new ActionListener() 
		{
			public void actionPerformed(ActionEvent arg0) 
			{
				System.out.println("Btn load table \n");
				loadTable(); 
		     
			}
		});
		btn_load_table.setBounds(450, 257, 89, 23);
		getContentPane().add(btn_load_table);
				
		btn_save_table = new JButton("Save");
		btn_save_table.addActionListener(new ActionListener() 
		{
			public void actionPerformed(ActionEvent arg0) 
			{
				System.out.println("Btn save table  \n");
	/*			
				 try 
				 {
				        File file = File.createTempFile("output", ".xml");


				        try (XMLEncoder xe = new XMLEncoder(new BufferedOutputStream(Files.newOutputStream(file.toPath())))) 
				        {
				        	xe.setPersistenceDelegate(DefaultTableModel.class, new DefaultTableModelPersistenceDelegate());
				        
				        	DefaultTableModel m = (DefaultTableModel) table.getModel();
				        	xe.writeObject(m);
				          // xe.flush();
				         //  xe.close();
				        }
				       
				        
				 } 
				 catch (IOException ex) 
				 {
				        ex.printStackTrace();
				 }
		*/		
/*				
				try
                {
                    OutputStream os = new BufferedOutputStream(new FileOutputStream(file));
                    XMLEncoder xe = new XMLEncoder(os);
                    xe.setPersistenceDelegate(DefaultTableModel.class, new DefaultTableModelPersistenceDelegate());
                    xe.writeObject(model);
                    xe.close();

                   
                }
                catch (IOException ex)
                {
                    ex.printStackTrace();
                }
*/				
				
				//File file = new File("TableModel.xml");	
				saveTable(file); 
			
			}
		});
		btn_save_table.setBounds(550, 257, 89, 23);
		getContentPane().add(btn_save_table);
		
		/*******************/
		
		
		JSeparator separator = new JSeparator();
		separator.setBounds(240, 302, 440, 2);
		getContentPane().add(separator);
	
	
		/*******************/
		
		JButton btn_ok = new JButton("OK");
		btn_ok.addActionListener(new ActionListener() 
		{
			public void actionPerformed(ActionEvent arg0) 
			{
				close_Jdialog = false;
				System.out.println("Btn Cancel  \n");
				dispose();
			}
		});
		btn_ok.setBounds(135, 391, 89, 23);
		getContentPane().add(btn_ok);
	
		/*******************/
		
		JButton btn_cancel = new JButton("Cancel");
		btn_cancel.addActionListener(new ActionListener() 
		{
			public void actionPerformed(ActionEvent e) 
			{
				close_Jdialog = false;
				System.out.println("Btn Cancel  \n");
				dispose();
				
				
			}
		});
		btn_cancel.setBounds(359, 391, 89, 23);
		getContentPane().add(btn_cancel);
		
		/*******************/
	}
	
	
	private void saveStatus() 
	{
	        //save the expanded Nodes:
	        expanded = new ArrayList<TreeNode[]>();
	        collectStatusInfo(model.getRoot());
	}

	private void restoreStatus() 
	{
	        //expand the saved Nodes:
	        for (int i = expanded.size() - 1; i > -1; i--) {
	            TreeNode[] ar = expanded.get(i);
	            tree.expandPath(new TreePath(ar));
	        }
	}

	private void collectStatusInfo(final Object o) 
	{
	        int cc = model.getChildCount(o);
	        for (int i = 0; i < cc; i++) {
	            DefaultMutableTreeNode child = (DefaultMutableTreeNode) model.getChild(o, i);
	            TreeNode[] ar = child.getPath();
	            if (!model.isLeaf(child) && tree.isExpanded(new TreePath(ar))) {
	                expanded.add(ar);
	                collectStatusInfo(child);
	            }
	        }
	}
	
	// https://wiki.byte-welt.net/wiki/JTree_Zustand_speichern
	public void serialize() 
	{
	        try {
	            XMLEncoder o = new XMLEncoder(new BufferedOutputStream(new FileOutputStream("tree.xml")));
	            o.writeObject(tree.getModel());
	            o.writeObject(expanded);
	            o.close();
	        } catch (IOException e) {
	            e.printStackTrace();
	        }
	}

	@SuppressWarnings("unchecked")
	private void deSerialize() 
	{
	        try {
	            XMLDecoder d = new XMLDecoder(new BufferedInputStream(new FileInputStream("tree.xml")));
	            tree.setModel((TreeModel) d.readObject());
	            expanded = (List<TreeNode[]>) d.readObject();
	            d.close();
	        } catch (FileNotFoundException ex) {
	        }
	}

    
	public ImageIcon createImageIcon(String path,  String description) 
	{
		URL imgURL = getClass().getResource(path);

		if (imgURL != null)
		{
  			return new ImageIcon(imgURL, description);
		} 
		else 
		{
			System.err.println("Couldn't find file: " + path);
			return null;
		}
	}
	
	/******************************************/
	
	private void saveTable() 
	{
        if (myJFileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION ) 
        {
            saveTable(myJFileChooser.getSelectedFile());
        }
    }
     
    private void saveTable(File file) 
    {
        try 
        {
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
            out.writeObject(tableModel.getDataVector());
            out.writeObject(getColumnNames());
            out.close();
        }
        catch (Exception ex) 
        {
                ex.printStackTrace();
        }
    }
    
    private Vector<String> getColumnNames() 
    {
        Vector<String> columnNames = new Vector<String>();
        for (int i = 0; i < table.getColumnCount(); i++)
            columnNames.add(table.getColumnName(i) );
            return columnNames;
    }
    
    private void loadTable() 
	{
    	if (myJFileChooser.showOpenDialog(this) ==  JFileChooser.APPROVE_OPTION )
	            loadTable(myJFileChooser.getSelectedFile());
    }
	 
    private void loadTable(File file) 
	{
	        try {
	            ObjectInputStream in = new ObjectInputStream( new FileInputStream(file));
	            Vector rowData = (Vector)in.readObject();
	            Vector columnNames = (Vector)in.readObject();
	            tableModel.setDataVector(rowData, columnNames);
	            in.close();
	        }
	        catch (Exception ex) {
	            ex.printStackTrace();
	        }
	    }
	
	/******************************************/
	
	 void doMouseClicked(MouseEvent me) 
	 {
		    TreePath tp = tree.getPathForLocation(me.getX(), me.getY());
		 //   if (tp != null)
		 //     jtf.setText(tp.toString());
		 //   else
		 //     jtf.setText("");
		 System.out.println(tp);   
		    
	}
	
  
}

class TreePopupMenu extends JPopupMenu 
{
	  protected TreePath path;
	  
	  private final Action addNodeAction = new AbstractAction("azeadd") 
	  {
	    @Override public void actionPerformed(ActionEvent e) 
	    {
	    	JTree tree = (JTree) getInvoker();
	    	DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
	    	DefaultMutableTreeNode parent = (DefaultMutableTreeNode) path.getLastPathComponent();
	    	DefaultMutableTreeNode child = new DefaultMutableTreeNode("New node");
	    	model.insertNodeInto(child, parent, parent.getChildCount());
	    	tree.scrollPathToVisible(new TreePath(child.getPath())); // https://ateraimemo.com/Swing/ScrollRectToVisible.html

	    	// ajouter dans la table
	    	// DefaultTableModel xxxmodel = (DefaultTableModel) table.getModel();
	    	
	    	
	   	// DefaultTableModel xxxmodel = (DefaultTableModel) table.getModel();
	    	
	     //    xxxmodel.addRow(new Object[]{"Column 1", "Column 2", "Column 3"});
	    
	    }
	  };
	  
	  private final Action addReloadAction = new AbstractAction("add & reload") 
	  {
	    @Override public void actionPerformed(ActionEvent e) 
	    {
	      JTree tree = (JTree) getInvoker();
	      DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
	      DefaultMutableTreeNode parent = (DefaultMutableTreeNode) path.getLastPathComponent();
	      DefaultMutableTreeNode child = new DefaultMutableTreeNode("New node");
	      parent.add(child);
	      model.reload(parent); // = model.nodeStructureChanged(parent);
	      tree.scrollPathToVisible(new TreePath(child.getPath()));
	    }
	  };
	  
	  private final Action editNodeAction = new AbstractAction("edit") 
	  {
	    protected final JTextField textField = new JTextField(24) 
	    {
	      protected transient AncestorListener listener;
	      
	      @Override public void updateUI() 
	      {
	        removeAncestorListener(listener);
	        super.updateUI();
	        
	        listener = new AncestorListener() 
	        {
	          @Override public void ancestorAdded(AncestorEvent e) 
	          {
	            requestFocusInWindow();
	          }

	          @Override public void ancestorMoved(AncestorEvent e) { /* not needed */ }

	          @Override public void ancestorRemoved(AncestorEvent e) { /* not needed */ }
	        };
	        addAncestorListener(listener);
	      }
	    };
	    
	    @Override public void actionPerformed(ActionEvent e) 
	    {
	      Object node = path.getLastPathComponent();
	      if (!(node instanceof DefaultMutableTreeNode)) 
	      {
	        return;
	      }
	      
	      DefaultMutableTreeNode leaf = (DefaultMutableTreeNode) node;
	      textField.setText(leaf.getUserObject().toString());
	      JTree tree = (JTree) getInvoker();
	      int ret = JOptionPane.showConfirmDialog(tree, textField, "edit", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
	      if (ret == JOptionPane.OK_OPTION) 
	      {
	        Optional.ofNullable(textField.getText())
	            .filter(str -> !str.trim().isEmpty())
	            .ifPresent(str -> {
	              DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
	              model.valueForPathChanged(path, str);
	              // leaf.setUserObject(str);
	              // model.nodeChanged(leaf);
	            });
	      }
	    }
	  };
	  
	  private final Action removeNodeAction = new AbstractAction("remove") 
	  {
	    @Override public void actionPerformed(ActionEvent e) {
	      DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
	      if (!node.isRoot()) {
	        JTree tree = (JTree) getInvoker();
	        DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
	        model.removeNodeFromParent(node);
	      }
	    }
	  };

	  protected TreePopupMenu() {
	    super();
	    add(addNodeAction);
	    add(addReloadAction);
	    add(editNodeAction);
	    addSeparator();
	    add(removeNodeAction);
	  }

	  @Override public void show(Component c, int x, int y) 
	  {
	    if (c instanceof JTree) {
	      JTree tree = (JTree) c;
	      // TreePath[] tsp = tree.getSelectionPaths();
	      path = tree.getPathForLocation(x, y);
	      // if (Objects.nonNull(path) && Arrays.asList(tsp).contains(path)) {
	      Optional.ofNullable(path).ifPresent(p -> {
	        tree.setSelectionPath(p);
	        super.show(c, x, y);
	      });
	    }
	  }
}

class xxxDefaultTableModelPersistenceDelegate extends DefaultPersistenceDelegate {
	  @Override protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder encoder) {
	    super.initialize(type, oldInstance, newInstance, encoder);
	    DefaultTableModel m = (DefaultTableModel) oldInstance;
	    // Vector v = m.getDataVector();
	    // for (int i = 0; i < m.getRowCount(); i++) {
	    //   encoder.writeStatement(new Statement(oldInstance, "addRow", new Object[] { (Vector) v.get(i) }));
	    // }
	    for (int row = 0; row < m.getRowCount(); row++) {
	      for (int col = 0; col < m.getColumnCount(); col++) {
	        Object[] o = {m.getValueAt(row, col), row, col};
	        encoder.writeStatement(new Statement(oldInstance, "setValueAt", o));
	      }
	    }
	  }
	}


class DefaultTableModelPersistenceDelegate extends DefaultPersistenceDelegate
{
    //  Initially creates an empty DefaultTableModel. The columns are created
    //  and finally each row of data is added to the model.

    @Override
    protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder encoder)
    {
        DefaultTableModel model= (DefaultTableModel)oldInstance;

        Vector<String> columnNames = new Vector<String>(model.getColumnCount());

        for (int i = 0; i < model.getColumnCount(); i++)
        {
            columnNames.add( model.getColumnName(i) );
        }

        Object[] columnNamesData = new Object[] { columnNames };
        encoder.writeStatement(new Statement(oldInstance, "setColumnIdentifiers", columnNamesData));

        Vector row = model.getDataVector();

        for (int i = 0; i < model.getRowCount(); i++)
        {
            Object[] rowData = new Object[] { row.get(i) };
            encoder.writeStatement(new Statement(oldInstance, "addRow", rowData));
        }
    }
}

// https://coderanch.com/t/340331/java/save-content-withen-table-XML

// https://ateraimemo.com/Swing/AddRow.html

// https://ateraimemo.com/Swing/TableModelEvent.html

// https://github.com/aterai/java-swing-tips/blob/master/PersistenceDelegate/src/java/example/MainPanel.java

// https://stackoverflow.com/questions/26250939/how-to-write-a-jtable-state-with-data-in-xml-file-using-xmlendcoder-in-java


// https://stackoverflow.com/questions/14166269/saving-jtable-as-text-file   a voir +++

// https://coderanch.com/t/620911/java/Saving-Loading-content-JTable

// https://www.hameister.org/JavaSwingTreeTable.html

// https://github.com/Sciss/TreeTable/tree/master/java/src/main/java/de/sciss/treetable/j

// http://esus.com/example-of-treetable/

//  https://ateraimemo.com/Swing.html   ++++++++ +++++++

// https://blogs.oracle.com/geertjan/swing-outline-component