package chrono;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JTabbedPane;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileFilter;

import chronoDivers.ButtonTabComponent;
import signals.AsyncBus;
import signals.AsyncSignal;
import signals.Clock;
import signals.Signals;
import signals.SyncBus;
import signals.SyncSignal;
import chronoSave.filter;
import chronoSave.save;

public class ChronoFrame  extends JFrame  implements KeyListener, ChangeListener
{
	private static final long serialVersionUID = 1L;
	public static int WIDTH = 800;
	public static int HEIGHT = 500;
	public static ArrayList<ChronoPanel> chrono = new ArrayList();
	public static int active = 0;
	public static JTabbedPane tabs;
	private JMenuBar menuBar = new JMenuBar();
	JFileChooser docChooser = new JFileChooser();
	JFileChooser imgChooser = new JFileChooser();
	private JMenu file = new JMenu("File");
	private JMenuItem file_new = new JMenuItem("New");
	private JMenuItem file_open = new JMenuItem("Open");
	private JMenuItem file_save = new JMenuItem("Save");
	private JMenuItem file_save_as = new JMenuItem("Save As..");
	private JMenuItem file_export = new JMenuItem(" to..");
	private JMenuItem file_quit = new JMenuItem("Quit");
	private JMenu add = new JMenu("Add");
	private JMenuItem add_clock = new JMenuItem("Clock");
	private JMenuItem add_sync_signal = new JMenuItem("Synchronous Signal");
	private JMenuItem add_sync_bus = new JMenuItem("Synchronous Bus");
	private JMenuItem add_async_signal = new JMenuItem("Asynchronous Signal");
	private JMenuItem add_async_bus = new JMenuItem("Asynchronous Bus");
	private JMenu tools = new JMenu("Tools");
	private JMenu tools_timeLapse = new JMenu("Time Lapse");
	private JMenuItem tools_timeLapse_add = new JMenuItem("Add");
	private JMenuItem tools_timeLapse_removeAll = new JMenuItem("Remove all");
	private JMenu tools_back_lines = new JMenu("Background Lines");
	private JMenuItem tools_back_lines_show = new JMenuItem("Show/Hide");
	private JMenu tools_antialiasing = new JMenu("Antialiasing");
	private JMenuItem tools_antialiasing_on = new JMenuItem("On/Off");
	private JMenu tools_assertion = new JMenu("Assertion");
	private JMenuItem tools_assertion_add = new JMenuItem("Add");
	private JMenuItem tools_assertion_delete_all = new JMenuItem("Delete All");
  
	private String version = "1.0";
  
	
	
	public ChronoFrame()
	{
		setTitle("Chronogram Editor " + version);
		setSize(WIDTH, HEIGHT);
		setLocationRelativeTo(null);
		addKeyListener(this);
		
		UIManager.put("TabbedPane.borderHightlightColor", Color.BLACK);
		UIManager.put("TabbedPane.contentAreaColor", Color.WHITE);
		UIManager.put("TabbedPane.selected", Color.WHITE);
    
		tabs = new JTabbedPane();
		tabs.addChangeListener(this);
		tabs.setFocusable(false);
    
		// this.docChooser.addChoosableFileFilter(new Filter(".chr", "Chronogram File"));
		// this.imgChooser.addChoosableFileFilter(new Filter(".jpg, .png, .gif", ""));
    
		addComponentListener(new ComponentAdapter()
		{
			public void componentResized(ComponentEvent e)
			{
				String sizeA = e.paramString().split(" ")[2];
				String size = "";
				for (int i = 0; i < sizeA.length(); i++) 
				{
					if (sizeA.charAt(i) != ')') 
					{
						size = size + sizeA.charAt(i);
					}
				}
        
				ChronoFrame.WIDTH = Integer.parseInt(size.split("x")[0]);
				ChronoFrame.HEIGHT = Integer.parseInt(size.split("x")[1]);
				ChronoFrame.this.repaint();
			}
		});
		
		setDefaultCloseOperation(0);
    
		addWindowListener(new WindowAdapter()
		{
			public void windowClosing(WindowEvent e)
			{
				ChronoFrame.this.quit();
			}
		});
    
		this.file_new.setAccelerator(KeyStroke.getKeyStroke(78, 2));
	    this.file.add(this.file_new);
	    this.file_open.setAccelerator(KeyStroke.getKeyStroke(79, 2));
	    this.file.add(this.file_open);
	    this.file_save.setAccelerator(KeyStroke.getKeyStroke(83, 2));
	    this.file.add(this.file_save);
	    this.file.add(this.file_save_as);
	    this.file.addSeparator();
	    this.file_export.setAccelerator(KeyStroke.getKeyStroke(69, 2));
	    this.file.add(this.file_export);
	    this.file.addSeparator();
	    this.file.add(this.file_quit);
	    
	    this.add.add(this.add_clock);
	    this.add.addSeparator();
	    this.add.add(this.add_sync_signal);
	    this.add.add(this.add_sync_bus);
	    this.add.addSeparator();
	    this.add.add(this.add_async_signal);
	    this.add.add(this.add_async_bus);
	    
	    this.tools_timeLapse.add(this.tools_timeLapse_add);
	    this.tools_timeLapse.add(this.tools_timeLapse_removeAll);
	    this.tools.add(this.tools_timeLapse);
	    this.tools_back_lines.add(this.tools_back_lines_show);
	    this.tools.add(this.tools_back_lines);
	    this.tools_antialiasing.add(this.tools_antialiasing_on);
	    this.tools.add(this.tools_antialiasing);
	    this.tools_assertion.add(this.tools_assertion_add);
	    this.tools_assertion.add(this.tools_assertion_delete_all);
	    this.tools.add(this.tools_assertion);
    
	    initButtons();
    
	    this.menuBar.add(this.file);
	    this.menuBar.add(this.add);
	    this.menuBar.add(this.tools);
	    
	    setJMenuBar(this.menuBar);
	    getContentPane().add(tabs);
	    
	    setVisible(true);
  }
  
  public void initButtons()
  {
	  this.file_new.addActionListener(new ActionListener()
	  {
		  public void actionPerformed(ActionEvent arg0)
		  {
			  ChronoFrame.this.newChrono();
			  ChronoFrame.this.repaint();
		  }
	  });
	  
/*	  
	  this.file_open.addActionListener(new ActionListener()
	  {
		  public void actionPerformed(ActionEvent arg0)
		  {
			  ChronoFrame.this.docChooser.setCurrentDirectory(new File("."));
			  if (ChronoFrame.this.docChooser.showOpenDialog(null) == 0)
			  {
				  File choosenFile = ChronoFrame.this.docChooser.getSelectedFile();
				  if (ChronoFrame.this.docChooser.getFileFilter().accept(choosenFile))
				  {
					  ChronoFrame.this.newChrono();
					  Slot.load(choosenFile);
					  int maxId = 0;
					  for (int i = 0; i < ((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).signals.size(); i++) 
					  {
						  maxId = Math.max(maxId, ((Signals)((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).signals.get(i)).id);
					  }
					  Signals.setIdBank(maxId + 1);
					  ((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).openedFile = choosenFile;
					  ((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).saveFileOk = true;
					  ChronoFrame.this.changeActiveTabTitle(choosenFile.getName());
				  }
				  else
				  {
					  JOptionPane.showMessageDialog(null, "File extension did not match \nExptected .chr", "Error", 0);
				  }
			  }
			  ChronoFrame.this.repaint();
		  }
    });
*/   

	  this.file_save.addActionListener(new ActionListener()
	  {
		  public void actionPerformed(ActionEvent arg0)
		  {
			  ChronoFrame.this.save(false, ChronoFrame.active);
		  }
	  });
    
	  this.file_save_as.addActionListener(new ActionListener()
	  {
		  public void actionPerformed(ActionEvent arg0)
		  {
			  ChronoFrame.this.save(true, ChronoFrame.active);
		  }
	  });
    
	  this.file_export.addActionListener(new ActionListener()
	  {
		  public void actionPerformed(ActionEvent arg0)
		  {
			  ChronoFrame.this.export();
		  }
	  });
    
	  this.file_quit.addActionListener(new ActionListener()
	  {
		  public void actionPerformed(ActionEvent arg0)
		  {
			  ChronoFrame.this.quit();
		  }
	  });
    
	  this.add_clock.addActionListener(new ActionListener()
	  {
		  public void actionPerformed(ActionEvent arg0)
		  {
			  DialogBox db = new DialogBox(null, "Add clock", new Clock(""));
			  db.showDialog();
			  if (db.name != null) 
			  {
				  if ((db.clockDomain != -1) && (db.ratio > 0.0F)) 
				  {
					  ((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).add(new Clock(db.name, db.freq, db.clockDomain, db.ratio));
				  } 
				  else 
				  {
					  ((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).add(new Clock(db.name, db.freq));
				  }
			  }
		  }
	  });
	
	  
	  this.add_sync_signal.addActionListener(new ActionListener()
	  {
		  public void actionPerformed(ActionEvent arg0)
		  {
			  DialogBox db = new DialogBox(null, "Add synchronous signal", new SyncSignal(""));
			  db.showDialog();
			  if (db.name != null) 
			  {
				  ((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).add(new SyncSignal(db.name, db.clockDomain));
			  }
		  }
	  });
    
	  
	  this.add_sync_bus.addActionListener(new ActionListener()
	  {
		  public void actionPerformed(ActionEvent arg0)
		  {
			  DialogBox db = new DialogBox(null, "Add synchronous bus", new SyncBus(""));
			  db.showDialog();
			  if (db.name != null)
			  {
				  ((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).add(new SyncBus(db.name, db.clockDomain));
			  }
		  }
	  });
 
  

	  this.add_async_signal.addActionListener(new ActionListener()
	  {
		  public void actionPerformed(ActionEvent arg0)
		  {
			  DialogBox db = new DialogBox(null, "Add asynchronous signal", new AsyncSignal(""));
			  db.showDialog();
			  if (db.name != null) 
			  {
				  ((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).add(new AsyncSignal(db.name, db.freq));
			  }
		  }
	  });
	 
	  
	  this.add_async_bus.addActionListener(new ActionListener()
	  {
		  public void actionPerformed(ActionEvent arg0)
		  {
			  DialogBox db = new DialogBox(null, "Add asynchronous bus", new AsyncBus(""));
			  db.showDialog();
			  if (db.name != null) 
			  {
				  ((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).add(new AsyncBus(db.name, db.freq));
			  }
		  }
	  });
	  
	  this.tools_timeLapse_add.addActionListener(new ActionListener()
	  {
		  public void actionPerformed(ActionEvent arg0)
		  {
			  if (((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).nbrTimeLapse < 5)
			  {
				  ((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).addTimeLapse();
				  ChronoFrame.this.repaint();
			  }
			  else
			  {
				  JOptionPane.showMessageDialog(null, "Can not set more than 5 timelapses", "Error", 0);
			  }
		  }
	  });
	  
	  this.tools_timeLapse_removeAll.addActionListener(new ActionListener()
	  {
		  public void actionPerformed(ActionEvent arg0)
		  {
			  ((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).removeTimeLapse();
			  ChronoFrame.this.tools_timeLapse_add.setEnabled(true);
			  ChronoFrame.this.repaint();
		  }
	  });
    
	  this.tools_back_lines_show.addActionListener(new ActionListener()
	  {
		  public void actionPerformed(ActionEvent arg0)
		  {
			  ((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).displayBackLines = (!((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).displayBackLines);
			  ((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).modifToSave = true;
			  ChronoFrame.this.repaint();
		  }
	  });
    
	  this.tools_antialiasing_on.addActionListener(new ActionListener()
	  {
		  public void actionPerformed(ActionEvent arg0)
		  {
			  ((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).antialiasing = (!((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).antialiasing);
			  ((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).modifToSave = true;
			  ChronoFrame.this.repaint();
		  }
	  });
    
	  this.tools_assertion_add.addActionListener(new ActionListener()
	  {
		  public void actionPerformed(ActionEvent arg0)
		  {
			  ((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).newAssertion();
			  ((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).modifToSave = true;
			  ChronoFrame.this.repaint();
		  }
	  });
	  
	  this.tools_assertion_delete_all.addActionListener(new ActionListener()
	  {
		  public void actionPerformed(ActionEvent arg0)
		  {
			  ((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).removeAssertions();
			  ((ChronoPanel)ChronoFrame.chrono.get(ChronoFrame.active)).modifToSave = true;
			  ChronoFrame.this.repaint();
		  }
	  });
  }
  
  /*******************************/
  
  public void quit()
  {
/*	  
	  boolean okToQuit = true;
	  for (int i = 0; i < chrono.size(); i++) 
	  {
		  if (((ChronoPanel)chrono.get(i)).modifToSave) 
		  {
			  okToQuit = false;
		  }
	  }
    
	  if (!okToQuit)
	  {
		  int option = JOptionPane.showConfirmDialog(null, "Some chronograms are not saved.\nExit Chronogram Editor ?", "Confirm exit", 1, 3);
		  if ((option != 2) && (option != -1) &&  (option == 0))
		  {
			  System.exit(0);
		  }
	  }
	  else
	  {
		  System.exit(0);
	  }
*/
	  dispose();
  }
  
  /*******************************/
  
  public void export()
  {
	  if (((ChronoPanel)chrono.get(active)).openedFile != null)
	  {
		  this.imgChooser.setCurrentDirectory(((ChronoPanel)chrono.get(active)).openedFile.getParentFile());
		  this.imgChooser.setSelectedFile(new File(((ChronoPanel)chrono.get(active)).openedFile.getName().replace("chr", "png")));
	  }
	  else
	  {
		  this.imgChooser.setCurrentDirectory(new File("."));
	  }
    
	  if (this.imgChooser.showSaveDialog(null) == 0)
	  {
		  File choosenFile = this.imgChooser.getSelectedFile();
		  if (this.imgChooser.getFileFilter().accept(choosenFile))
		  {
			  ((ChronoPanel)chrono.get(active)).releaseSelection();
        
			  BufferedImage bufImg = new BufferedImage((int)((ChronoPanel)chrono.get(active)).getSize().getWidth(), (int)((ChronoPanel)chrono.get(active)).getSize().getHeight(), 5);
			  Graphics g = bufImg.getGraphics();
			  ((ChronoPanel)chrono.get(active)).paint(g);
        
			  try
			  {
				  if (choosenFile.getName().endsWith("jpg")) 
				  {
					  ImageIO.write(bufImg, "JPG", choosenFile);
				  }
          
				  if (choosenFile.getName().endsWith("png")) 
				  {
					  ImageIO.write(bufImg, "PNG", choosenFile);
				  }
          
				  if (!choosenFile.getName().endsWith("gif")) 
				  {
					  return;
				  }
				  ImageIO.write(bufImg, "GIF", choosenFile);
			  }
			  catch (FileNotFoundException e)
			  {
				  JOptionPane.showMessageDialog(null, "File directory error", "Error", 0);
			  }
			  catch (IOException e)
			  {
				  JOptionPane.showMessageDialog(null, "Error during exportation", "Error", 0);
			  }
		  }
		  else
		  {
			  JOptionPane.showMessageDialog(null, "File extension error", "Error", 0);
		  }
	  }
  }
  
  /*******************************/
  
  public void save(boolean saveAs, int chronoIndex)
  {
	  if ((((ChronoPanel)chrono.get(chronoIndex)).saveFileOk) && (!saveAs))
	  {
		  save.save(((ChronoPanel)chrono.get(chronoIndex)).openedFile);
		  ((ChronoPanel)chrono.get(chronoIndex)).modifToSave = false;
	  }
	  else
	  {
		  if (((ChronoPanel)chrono.get(chronoIndex)).openedFile != null)
		  {
			  this.docChooser.setCurrentDirectory(((ChronoPanel)chrono.get(chronoIndex)).openedFile.getParentFile());
			  this.docChooser.setSelectedFile(new File(((ChronoPanel)chrono.get(active)).openedFile.getName()));
		  }
		  else
		  {
			  this.docChooser.setCurrentDirectory(new File("."));
		  }
		  if (this.docChooser.showSaveDialog(null) == 0)
		  {
			  File choosenFile = this.docChooser.getSelectedFile();
        
			  String[] tmp = choosenFile.getName().split("[.]");
			  String ext = "";
			  if (tmp.length == 2) 
			  {
				  ext = tmp[1];
			  }
			  if (ext == "")
			  {
				  choosenFile = new File(choosenFile.getPath() + ".chr");
				  System.out.println(choosenFile.getPath());
			  }
        
			  if (this.docChooser.getFileFilter().accept(choosenFile))
			  {
				  save.save(choosenFile);
				  ((ChronoPanel)chrono.get(chronoIndex)).modifToSave = false;
				  ((ChronoPanel)chrono.get(chronoIndex)).saveFileOk = true;
				  ((ChronoPanel)chrono.get(chronoIndex)).openedFile = choosenFile;
				  changeActiveTabTitle(choosenFile.getName());
			  }
			  else
			  {
				  JOptionPane.showMessageDialog(null, "File extension error", "Error", 0);
			  }
		  }
	  }
	  checkModifToSave();
  }
  
  /*******************************/
  
  public void newChrono()
  {
	  chrono.add(new ChronoPanel());
	  active = chrono.size() - 1;
	  tabs.addTab("New", (Component)chrono.get(active));
	  tabs.setTabComponentAt(active, new ButtonTabComponent("New", active));
	  tabs.setSelectedIndex(active);
	  ((ChronoPanel)chrono.get(active)).saveFileOk = false;
  }
  
  /*******************************/
  
  public void changeActiveTabTitle(String title)
  {
	  tabs.setTabComponentAt(active, new ButtonTabComponent(title, active));
	  ((ChronoPanel)chrono.get(active)).fileName = title;
  }
  
  /*******************************/
  
  public void checkModifToSave()
  {
	  if (((ChronoPanel)chrono.get(active)).modifToSave)
	  {
		  if (!((ChronoPanel)chrono.get(active)).fileName.endsWith("*"))
		  {
			  tabs.setTabComponentAt(active, new ButtonTabComponent(((ChronoPanel)chrono.get(active)).fileName + "*", active));
			  ((ChronoPanel)chrono.get(active)).fileName += "*";
		  }
	  }
	  else if (((ChronoPanel)chrono.get(active)).fileName.endsWith("*"))
	  {
		  ((ChronoPanel)chrono.get(active)).fileName = ((ChronoPanel)chrono.get(active)).fileName.substring(0, ((ChronoPanel)chrono.get(active)).fileName.length() - 1);
		  tabs.setTabComponentAt(active, new ButtonTabComponent(((ChronoPanel)chrono.get(active)).fileName, active));
	  }
  }
  /*******************************/
  
  public void keyPressed(KeyEvent e)
  {
    if (e.getKeyCode() == 127)
    {
      ((ChronoPanel)chrono.get(active)).removeSelected();
      
      chrono.get(active).removeSelected();
      
    
      
      
      repaint();
    }
  }
  /*******************************/
  
  public void keyReleased(KeyEvent e) {}
  
  /*******************************/
  
  public void keyTyped(KeyEvent e) {}

  /*******************************/

  public void stateChanged(ChangeEvent e)
  {
	  JTabbedPane pane = (JTabbedPane)e.getSource();
	  active = pane.getSelectedIndex();
  }
}

//  http://max99x.com/school/drawtime
// https://hacksterous.wordpress.com/waves-timing-diagram-editor/

// http://timingeditor.sourceforge.net/screenshots.html

// https://www.mhgsoft.de/TimingDraw/

// timming diagram

// https://waveme.weebly.com/