package theGhastModding.GoSBuilder.main;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;

@SuppressWarnings("serial")
public class BuilderLogGui extends JPanel {
	
	public static JFrame frame;
	
	public static final String NAME = "Hypernova builder";
	
	private BufferedImage background;
	private JButton btnAbout;
	private List<JLabel> console = new ArrayList<JLabel>();
	public JProgressBar progressBar;
	private JButton btnYay;
	private JCheckBox checkbox;
	
	public static void main(String[] args){
		frame = new JFrame(NAME);
		frame.setResizable(false);
		BuilderLogGui contentPane = new BuilderLogGui();
		frame.setContentPane(contentPane);
		Toolkit kit = Toolkit.getDefaultToolkit();
		Dimension d = kit.getScreenSize();
		frame.setLocation((int)(d.getWidth() / 2 - 488), (int)(d.getHeight() / 2 - 240));
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.pack();
		frame.setVisible(true);
	}
	
	public BuilderLogGui(){
		super();
		setLayout(null);
		setPreferredSize(new Dimension(977,480));
		try {
			background = ImageIO.read(this.getClass().getResourceAsStream("/icon.png"));
		}catch(Exception e){
			JOptionPane.showMessageDialog(frame, "Error loading background", "Error", JOptionPane.ERROR_MESSAGE);
			e.printStackTrace();
			System.exit(1);
		}
		frame.setIconImage(background);
		JLabel backgroundLabel = new JLabel(new ImageIcon(resizeImage(background, 977, 480)));
		backgroundLabel.setBounds(0, 0, 977, 480);
		
		
		btnYay = new JButton("");
		JFileChooser midiChooser = new JFileChooser();
        FileFilter filter = new FileNameExtensionFilter("MIDI files", 
                "mid", "midi");  
        midiChooser.setFileFilter(filter);
		JFileChooser zipChooser = new JFileChooser();
        filter = new FileNameExtensionFilter("Zip files", 
                "zip");  
        zipChooser.setFileFilter(filter);
		btnYay.setBounds(12, 12, 174, 23);
		btnYay.setIcon(new ImageIcon(texturedButtonTexture(12, 12, 174, 23, "Build MIDI")));
		btnYay.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				if(!checkbox.isSelected()){
					int option = midiChooser.showOpenDialog(frame);
					if(option == JFileChooser.APPROVE_OPTION){
						File f = midiChooser.getSelectedFile();
						if(f.exists()){
			        		int option2 = JOptionPane.showConfirmDialog(frame, "The selected file allready exists. Do you want to overwrite that file?", "Warning", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
			        		if(option2 != 0){
			        			return;
			        		}
						}
						if(!(f.getName().endsWith(".mid") || f.getName().endsWith(".midi"))){
							f = new File(f.getPath() + ".mid");
						}
						btnYay.setEnabled(false);
						zipChooser.setCurrentDirectory(midiChooser.getCurrentDirectory());
						buildMidi(f.getPath());
					}
				}else{
					int option = zipChooser.showOpenDialog(frame);
					if(option == JFileChooser.APPROVE_OPTION){
						File f = zipChooser.getSelectedFile();
						if(f.exists()){
			        		int option2 = JOptionPane.showConfirmDialog(frame, "The selected file allready exists. Do you want to overwrite that file?", "Warning", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
			        		if(option2 != 0){
			        			return;
			        		}else{
			        			f.delete();
			        		}
						}
						if(!(f.getName().endsWith(".zip"))){
							f = new File(f.getPath() + ".zip");
						}
						btnYay.setEnabled(false);
						midiChooser.setCurrentDirectory(zipChooser.getCurrentDirectory());
						buildMidi(f.getPath());
					}
				}
			}
		});
		add(btnYay);
		
		btnAbout = new JButton("");
		btnAbout.setBounds(867, 12, 98, 23);
		btnAbout.setIcon(new ImageIcon(texturedButtonTexture(867, 12, 98, 23, "About")));
		AboutDialog about = new AboutDialog();
		btnAbout.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				about.setVisible(true);
			}
		});
		add(btnAbout);
		
		JLabel lblConsole = new JLabel("Console:");
		lblConsole.setBounds(12, 47, 55, 16);
		lblConsole.setForeground(Color.WHITE);
		add(lblConsole);
		
		JLabel label = new JLabel("");
		label.setForeground(Color.WHITE);
		console.add(label);
		label.setBounds(12, 75, 953, 16);
		add(label);
		
		JLabel label_1 = new JLabel("");
		label_1.setForeground(Color.WHITE);
		console.add(label_1);
		label_1.setBounds(12, 103, 953, 16);
		add(label_1);
		
		JLabel label_2 = new JLabel("");
		label_2.setForeground(Color.WHITE);
		console.add(label_2);
		label_2.setBounds(12, 131, 953, 16);
		add(label_2);
		
		JLabel label_3 = new JLabel("");
		label_3.setForeground(Color.WHITE);
		console.add(label_3);
		label_3.setBounds(12, 161, 953, 16);
		add(label_3);
		
		JLabel label_4 = new JLabel("");
		label_4.setForeground(Color.WHITE);
		console.add(label_4);
		label_4.setBounds(12, 189, 953, 16);
		add(label_4);
		
		JLabel label_5 = new JLabel("");
		label_5.setForeground(Color.WHITE);
		console.add(label_5);
		label_5.setBounds(12, 217, 953, 16);
		add(label_5);
		
		JLabel label_6 = new JLabel("");
		label_6.setForeground(Color.WHITE);
		console.add(label_6);
		label_6.setBounds(12, 245, 953, 16);
		add(label_6);
		
		JLabel label_7 = new JLabel("");
		label_7.setForeground(Color.WHITE);
		console.add(label_7);
		label_7.setBounds(12, 280, 953, 16);
		add(label_7);
		
		JLabel label_8 = new JLabel("");
		label_8.setForeground(Color.WHITE);
		console.add(label_8);
		label_8.setBounds(12, 308, 953, 16);
		add(label_8);
		
		JLabel label_9 = new JLabel("");
		label_9.setForeground(Color.WHITE);
		console.add(label_9);
		label_9.setBounds(12, 336, 953, 16);
		add(label_9);
		
		progressBar = new JProgressBar();
		progressBar.setBounds(12, 454, 148, 14);
		progressBar.setBackground(Color.BLACK);
		progressBar.setForeground(new Color(0,255,0));
		progressBar.setMinimum(0);
		progressBar.setMaximum(100);
		progressBar.setValue(0);
		add(progressBar);
		
		JButton btnExit = new JButton("");
		btnExit.setBounds(867, 445, 98, 23);
		btnExit.setIcon(new ImageIcon(texturedButtonTexture(867, 445, 98, 23, "Exit")));
		btnExit.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				System.exit(0);
			}
		});
		add(btnExit);
		
		JLabel lblAmountOfTracks = new JLabel("Amount of tracks to build:");
		lblAmountOfTracks.setBounds(204, 19, 159, 16);
		lblAmountOfTracks.setForeground(Color.WHITE);
		add(lblAmountOfTracks);
		
		textField = new JTextField();
		textField.setText("65536");
		textField.setForeground(Color.WHITE);
		textField.setBackground(new Color(33,35,38));
		textField.setBounds(373, 17, 114, 20);
		textField.setLayout(null);
		add(textField);
		textField.setColumns(10);
		
		checkbox = new JCheckBox("Build in zip file");
		checkbox.setForeground(Color.WHITE);
		checkbox.setBackground(new Color(33,35,38));
		checkbox.setBounds(373 + 114 + 12, 17, 110, 16);
		checkbox.setEnabled(false);
		checkbox.setVisible(false);
		add(checkbox);
		
		add(backgroundLabel);
		
		printToConsole(NAME + " started");
	}
	
	private ByteBuffer buffer;
	private JTextField textField;
	
	public int bytesToInt(byte[] bytes) {
	    buffer = ByteBuffer.allocate(Integer.BYTES);
	    buffer.put(bytes);
	    buffer.flip();
	    return buffer.getInt();
	}
	
	public byte[] intToBytes(int i) {
		  byte[] result = new byte[4];
	
		  result[0] = (byte) (i >> 24);
		  result[1] = (byte) (i >> 16);
		  result[2] = (byte) (i >> 8);
		  result[3] = (byte) (i /*>> 0*/);
	
		  return result;
	}
	
	public void buildMidi(String location){
		MidiBuilder builder = new MidiBuilder(this, location);
		builder.beginBuild();
	}
	
	private class MidiBuilder implements Runnable {
		
		private Thread builderThread;
		private BuilderLogGui console;
		private String exportLocation;
		
		public MidiBuilder(BuilderLogGui gui, String exportLocation){
			builderThread = new Thread(this);
			this.console = gui;
			this.exportLocation = exportLocation;
		}
		
		public void beginBuild(){
			builderThread.start();
		}
		
		@Override
		public void run() {
			boolean b = true;
			for(Character c:textField.getText().toCharArray()){
				if(c != '0' && c != '1' && c != '2' && c != '3' && c != '4' && c != '5' && c != '6' && c != '7' && c != '8' && c != '9'){
					b = false;
				}
			}
			if(!b){
				JOptionPane.showMessageDialog(console, "The text entered in the number of tracks setting is not a number", "Error", JOptionPane.ERROR_MESSAGE);
				btnYay.setEnabled(true);
				return;
			}
			int numTracks = Integer.parseInt(textField.getText());
			if(numTracks > 65536){
				JOptionPane.showMessageDialog(console, "The entered number of tracks is too big. The maximum is 65536!", "Error", JOptionPane.ERROR_MESSAGE);
				btnYay.setEnabled(true);
				return;
			}
			if(numTracks <= 1){
				JOptionPane.showMessageDialog(console, "You need to have atleast 2 tracks!", "Error", JOptionPane.ERROR_MESSAGE);
				btnYay.setEnabled(true);
				return;
			}
			console.printToConsole("Build started");
			try {
				FileOutputStream out = null;
				out = new FileOutputStream(new File(exportLocation));
				out.write("MThd".getBytes());
				out.flush();
				out.write(0);
				out.write(0);
				out.write(0);
				out.write(6);
				out.flush();
				out.write(0);
				out.write(1);
				byte[] bb = intToBytes(numTracks);
				out.write(bb[2]);
				out.write(bb[3]);
				out.write((byte)0x01);
				out.write((byte)0xe0);
				out.flush();
				InputStream str = this.getClass().getResourceAsStream("/first_track.track");
				while(str.available() > 0){
					byte i = (byte)str.read();
					out.write(i);
				}
				out.flush();
				str.close();
				//65535
				for(int i = 0; i < numTracks - 1; i++){
					console.printToConsole("Writing track " + (i + 1) + " of " + numTracks);
					console.progressBar.setValue((int)(((double)(i + 1) / (double)numTracks) * 100D));
					writeTrack(out,i);
				}
				out.close();
				console.printToConsole("Build finished! Have fun with the MIDI.");
				btnYay.setEnabled(true);
			} catch(Exception e){
				JOptionPane.showMessageDialog(console, "Error exporting MIDI", "Error", JOptionPane.ERROR_MESSAGE);
				e.printStackTrace();
				btnYay.setEnabled(true);
				System.exit(1);
			}
			console.progressBar.setValue(0);
		}
		
		/*private void writeTrack(OutputStream str, int t) throws Exception {
			str.write("MTrk".getBytes());
			str.write(0x00);
			str.write(0x00);
			str.write(0x00);
			str.write(0x0D);
			str.write(0);
			str.write((byte)(0x90));
			str.write((byte)0x60);
			str.write((byte)100);
			str.write((byte)135);
			str.write((byte)104);
			str.write((byte)0x80);
			str.write((byte)0x60);
			str.write(0);
			str.write(0);
			str.write((byte)0xff);
			str.write((byte)0x2F);
			str.write((byte)0x00);
		}*/
		
		/*
		 *Note on is:
		 *0
		 *0x90
		 *60
		 *100
		 * 
		 */
		
		private void writeTrack(OutputStream str, int t) throws Exception {
			str.write("MTrk".getBytes());
			str.write((byte)-29);
			str.write((byte)-114);
			str.write((byte)56);
			str.write((byte)-43);
			long time = System.currentTimeMillis();
			byte[] noteOnBytes = new byte[]{(byte)0, (byte)0x90, (byte)60, (byte)100};
			byte[] noteOffBytes = new byte[]{(byte)0, (byte)0x80, (byte)60, (byte)0};
			for(long i = 0; i < 477218586; i++){
				if(System.currentTimeMillis() - time >= 1000){
					time = System.currentTimeMillis();
					console.printToConsole(Long.toString(i + 1) + " NoteOn events of 477218586 NoteOns for track " + Integer.toString(t + 1) + " written!");
				}
				str.write(noteOnBytes);
				str.flush();
				//4294967292
				//3817748693
			}
			str.write((byte)135);
			str.write((byte)104);
			str.write((byte)0x80);
			str.write((byte)60);
			str.write((byte)0);
			for(long i = 0; i < 477218585; i++){
				if(System.currentTimeMillis() - time >= 1000){
					time = System.currentTimeMillis();
					console.printToConsole(Long.toString(i + 2) + " NoteOff events of 477218586 NoteOffs for track " + Integer.toString(t + 1) + " written!");
				}
				str.write(noteOffBytes);
				str.flush();
			}
			str.write(0);
			str.write((byte)0xff);
			str.write((byte)0x2F);
			str.write((byte)0x00);
			str.flush();
		}
		
	}
	
	public void printToConsole(String text){
		System.out.println(text);
		for(JLabel label:console){
			if(label.getText().isEmpty()){
				label.setText(text);
				return;
			}
		}
		JLabel before = null;
		for(JLabel now:console){
			if(before != null){
				before.setText(now.getText());
			}
			before = now;
		}
		console.get(console.size() - 1).setText(text);
	}
	
	private BufferedImage texturedButtonTexture(int x, int y, int width, int height, String text){
		BufferedImage bf = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		Graphics2D g2 = (Graphics2D) bf.getGraphics();
		g2.drawImage(resizeImage(background, 977, 480).getSubimage(x, y, width, height), 0, 0, width, height, null);
		g2.setColor(Color.WHITE);
		g2.drawString(text, 2, height / 2 + 5);
		return bf;
	}

	
	public static BufferedImage resizeImage(BufferedImage original, int width, int height){
		BufferedImage resized = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		Graphics2D g = resized.createGraphics();
		g.drawImage(original, 0, 0, width, height, null);
		g.dispose();
		return resized;
	}
}
