package pl.pejotr.PolskiMorsea;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.util.Scanner;
import java.util.List;
import java.util.concurrent.ExecutionException;


/**
 * @author pejotr
 *
 */
public class PolskiAlfabetMorsea extends JFrame{
	private static final long serialVersionUID = 1L;
	
	public static void main(String[] args) {
		PolskiAlfabetMorsea paf = new PolskiAlfabetMorsea();
		paf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		paf.setVisible(true);
	}
	
	public PolskiAlfabetMorsea() {
		super("Polski Alfabet Morsea");
		wybierzPlik_ = new JFileChooser();
		wybierzPlik_.setCurrentDirectory(new File("."));
		
		setSize(DEFAULT_WIDTH, DEFAULT_HIGHT);
		setResizable(false);
		
		statusKontener_ = new JPanel();
		statusKontener_.setPreferredSize(new Dimension(DEFAULT_WIDTH, 30));
		statusKontener_.setLayout(new FlowLayout());
		statusKontener_.setBorder(BorderFactory.createBevelBorder(1));
		statusEtykieta_ = new JLabel("Bezczynny.");
		iloscOdczytanychZnakow_ = new JLabel();
		statusKontener_.add(statusEtykieta_);
		statusKontener_.add(iloscOdczytanychZnakow_);
		add(statusKontener_, BorderLayout.SOUTH);
		
		
		trescKontener_ = new JPanel(new BorderLayout(1,1));
		poleTekstowe_ = new JTextArea(20, 20);
		poleTekstowe_.setEnabled(false);
		JScrollPane paskiPrzewijania = new JScrollPane(poleTekstowe_);
		trescKontener_.add(paskiPrzewijania);
		add(trescKontener_, BorderLayout.NORTH);
		
		menuPasek_ = new JMenuBar();
		setJMenuBar(menuPasek_);
		JMenu plik = new JMenu("Plik");
		menuPasek_.add(plik);
		menuOtworz_ = new JMenuItem("Otwórz");
		plik.add(menuOtworz_);
		menuOtworz_.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				int rezultat = wybierzPlik_.showOpenDialog(null);
				if(rezultat == JFileChooser.APPROVE_OPTION) {
					czytajPlik(wybierzPlik_.getSelectedFile());
				}
			}
			
		}
		);
	}
	
	//: Uruchomienie pracownika (SwingWorkera) odczytującego plik
	public void czytajPlik(File file) {
		statusEtykieta_.setText("Odczytywanie pliku " + file.getName()  + " w toku...");
		(pracownik_ = new MorsePracownik(file)).execute();
	}
	
	//: Uruchomienie pracownika (SwingWorkera) kodujacego znaki
	public void kodujHuffman() {
		
		int[] stat;
		try {
			stat = pracownik_.get();
			prac_ = new HuffmanPracownik(stat);
			prac_.execute();
		} catch (InterruptedException e) {
			System.out.println("InterruptedException");
			e.printStackTrace();
		} catch (ExecutionException e) {
			System.out.println("ExecutionException");
			e.printStackTrace();
		}
			
	}
	
	//: Wypisanie wyników kodowania
	public void wypisz() {
		try {
			HuffNode root = prac_.get();
			postOrder(root, "");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
	}
	
	//: Przejscie drzewa w porzadku post-order z wypisanie tylko liści
	public void postOrder(HuffNode n, String s) {
		if(n.lewy_ != null || n.prawy_ != null) {
			if(n.lewy_ != null) {
				postOrder(n.lewy_, s + '*');
			}
		
			if(n.prawy_ != null) {
				postOrder(n.prawy_, s + '-');
			}
		}
		else {
			poleTekstowe_.append("Literka: " + n.literka_ + " prawdopodobieństwo: " + n.prawdopod_ + " kod: " + s + "\n");
		}
	}
	
	
	/**
	 * 
	 * @author pejotr
	 *
	 */
	class MorsePracownik extends SwingWorker<int[], Integer> {
		
		public MorsePracownik(File plik) {
			_nazwaPliku = plik.getAbsolutePath();
		}

		@Override
		public int[] doInBackground()  {
			int znak;
			int licznik = 0;
			String wyraz;
			Scanner s = null;
			try {
				s = new Scanner(new BufferedReader(new FileReader(_nazwaPliku)));
				while(!Thread.currentThread().isInterrupted() && s.hasNext()) {
					wyraz = s.next();
					wyraz = wyraz.toLowerCase();
					for(int i =0; i < wyraz.length(); i++) {
						znak = (int)wyraz.charAt(i)-97;
						if(znak < 0 || znak > 25)
							continue;
						licznik++;
						statystyka_[(int)wyraz.charAt(i)-97]++;
					}
					publish(new Integer(licznik));
				}
			} catch (FileNotFoundException e) {
				System.out.println("Nie odnaleziono pliku");
			} finally {
				if(s != null) {
					s.close();
				}
			}
			return statystyka_;
		}
		
		@Override
		protected void process(List<Integer> ilosc){ 
			Integer ostatniaIlosc = ilosc.get(ilosc.size() - 1);
			iloscOdczytanychZnakow_.setText("Odczytana ilosc znakow: " + ostatniaIlosc);
		}
		
		@Override
		public void done() {
			statusEtykieta_.setText("Odczyt zakonczony.");
			kodujHuffman();
		}
		
		private int[] statystyka_ = new int[26];
		String _nazwaPliku;
	}
	
	class HuffmanPracownik extends SwingWorker<HuffNode, Void> {
		
		private HuffNode[] drzewa_;
		private int wolnePole_;
		private int size_;
		
		public HuffmanPracownik(int[] stat) {
			size_ = stat.length;
			wolnePole_ = size_;
			drzewa_ = new HuffNode[2*size_ - 1];
			int liczbaZnakow = 0;
			for(int i=0; i < size_; i++) {
				liczbaZnakow += stat[i];
			}
			for(int i=0; i < size_; i++) {
				drzewa_[i] = new HuffNode((double)stat[i]/liczbaZnakow, (char)(i+97));
			}
		}
		
		@Override
		public HuffNode doInBackground() {
			HuffNode temp1, temp2;
			while(wolnePole_ != drzewa_.length) {
				temp1 = null;
				temp2 = null;
				
				for(int i = 0; i < wolnePole_; i++) {	
					// wezly majace rodzica nie sa brane pod uwage
					if(drzewa_[i].rodzic_ != null) {
						continue;
					}
					
					// poszukiwanie dwoch najmniejszych prawdopodobienstw
					if(temp1 == null) {
						temp1 = drzewa_[i];
					} else if (temp2 == null) {
						temp2 = drzewa_[i];
					} else if(temp1.prawdopod_ > drzewa_[i].prawdopod_) {
						temp2 = temp1;
						temp1 = drzewa_[i];
						
					// usunięcie błędu w wikipedii
					} else if(temp2.prawdopod_ > drzewa_[i].prawdopod_) {
						temp2 = drzewa_[i];
					}
				}
				
				drzewa_[wolnePole_] = new HuffNode(temp1.prawdopod_ + temp2.prawdopod_, temp1, temp2, null);
				temp1.rodzic_ = temp2.rodzic_ = drzewa_[wolnePole_];
				wolnePole_++;
				
			}
			
			return drzewa_[drzewa_.length-1];
		}
		
		@Override
		public void done() {
			wypisz();
		}
	}
	
	//! Węzeł drzewa Huffamana
	class HuffNode {
		double prawdopod_;
		HuffNode lewy_, prawy_, rodzic_;
		char literka_;
		
		HuffNode(double p, char l) {
			this.prawdopod_ = p;
			this.lewy_ = this.prawy_ = this.rodzic_ = null;
			literka_ = l;
		}
		
		HuffNode(double pr, HuffNode l, HuffNode p, HuffNode r) {
			this.prawdopod_ = pr;
			this.lewy_ = l;
			this.prawy_ = p;
			this.rodzic_ = r;
		}
			
	}

	
	private JMenuBar menuPasek_;
	private JMenuItem menuOtworz_;
	private JFileChooser wybierzPlik_;
	private JTextArea poleTekstowe_;
	
	private JPanel statusKontener_;
	private JPanel trescKontener_;
	private JLabel statusEtykieta_;
	private JLabel iloscOdczytanychZnakow_;
	
	
	private MorsePracownik pracownik_;
	private HuffmanPracownik prac_;
	
	
	public static final int DEFAULT_WIDTH = 500;
	public static final int DEFAULT_HIGHT = 400;
	
}

