/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pnandmd.Contacts;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *Klasa tworzaca plik z siecia Petriego i kilka innych. Klasa czyta dane z plikow pdb
 * w folderze roboczym i na ich podstawie generuje siec.
 * 
 * Dla kazdego atomu robi osobna siec!
 * 
 * @author Ania
 */
public class TworzSiec {
    
    //numery miejsc z aktualnego czytania - kontakty z poprzendiej klatki
    private ArrayList aktMiejsca = new ArrayList();
    //numery miejsc z poprzedniego czytania - kontakty z poprzendiej klatki
    private ArrayList popMiejsca = new ArrayList();
    //numery aktualnych trnazycji ktore ostatnio ustawialy wspolrzedne
    private ArrayList pierTranz = new ArrayList();
    //1 z 3 plikow pozniej tworzacych plik sieci - z miejscami
    private BufferedWriter miejsca;
    //2 z 3 plikow pozniej tworzacych plik sieci - z tranzycjami
    private BufferedWriter tranz;
    //3 z 3 plikow pozniej tworzacych plik sieci - z lukami
    private BufferedWriter luki;
    //plik zawierajacy nazwy miejsc - czyli wspolrzedne ktore one reprezentuja
    private BufferedWriter mNazwy;
    private BufferedWriter tNazwy;
    //lista zawierajaca czasy od kotrych mozna odpalac tranzycje
    private ArrayList czTranz = new ArrayList();
    private BufferedWriter czasy;
    //plik zawierajacy ilosc wystapien danego atomu w danej wspolrzendej
    private BufferedWriter wystapienia;
    private ArrayList wys = new ArrayList();
    //obiekt czytajacy pliki z pdb
    private CzytaniePdb czyt;
    //mapa zawierajaca jako klucze pary w kontakcie a jako elementy numery miejsc, dla spr
    //czy dane miejsce juz wystepuje
    Map<String, Integer> mapaM = new HashMap<>();
    //mapa zwierajaca jako klucze pary kontakcie ktora sa miejscami we i wy a jako elem numery tranz
    //dla spr czy dana tranzyja juz istnieje
    Map<String, Integer> mapaT = new HashMap<>();
    //czas aktulany - nr ramki
    private int czas;
    //katalog z plikami
    private File dir;
    //licznik ktore miejsce
    private int liczM = -1;
    //licznik ktora tranz
    private int liczT = -1;
    //licznik ktory luk
    private int liczL = -1;
    //plik z gotowa siecia
    private BufferedWriter siecGotowa;
    //ilosc atomow
    private int ilosc = 0;
    //pierwsze czytniae pliku
    private boolean pierPlik = false;
    
    private int[][] wej;
    private int[][] wyj;
    
    
    //pliki i lista zawierajace ilosc wystapien miejsca - tzn ile razy atom przy tworzeniu w nim by
    private BufferedWriter wystapieniaM;
    private ArrayList wysMiej = new ArrayList();
    
    
    //Klasa zwracajaca kontakty na podstawie wspolrzednych z obecnej klatki
    //potrzebuje wspolrzedncyh po konwersji i orginalnych
    Kontakty kontakty = new Kontakty();
    
    
    private File newDir;
    
    //odleglosc ponizej ktorej sa kontakty
    private int oko = 8;
   
    
    /**
     * Konstuktor ktory przyjmuje jako katalok z plikami pdb roboczy katalog.
     * Do tego dokonuje inicjalizacji plikow, parametrow itd.
     */
    public TworzSiec(){
        
        dir = new File(System.getProperty("user.dir"));
        inicjacja();
    }
    
    /**
     * Konstruktor ktorego argumentem jest katalog z plikami pdb.
     * Do tego dokonuje inicjalizacji plikow, parametrow itd.
     * @param kat katalog z plikami
     */
    public TworzSiec(String kat, int o){
        oko = o;
        dir = new File(kat);
        inicjacja();
    }
    
    private void inicjacja(){
        try {
            czas = 0;
            newDir = new File("" + dir.getAbsolutePath() + File.separator + "alg4");
            newDir.mkdir();
            miejsca = new BufferedWriter(new FileWriter(new File("" + newDir.getAbsolutePath() + File.separator + "miejsca.tmp")));
            tranz = new BufferedWriter(new FileWriter(new File("" + newDir.getAbsolutePath() + File.separator + "tranz.tmp")));
            luki = new BufferedWriter(new FileWriter(new File("" + newDir.getAbsolutePath() + File.separator + "luki.tmp")));
            mNazwy = new BufferedWriter(new FileWriter(new File("" + newDir.getAbsolutePath() + File.separator + "mNazwy.txt")));
            wystapienia = new BufferedWriter(new FileWriter(new File("" + newDir.getAbsolutePath() + File.separator + "wystapienia.txt")));
            wystapieniaM = new BufferedWriter(new FileWriter(new File("" + newDir.getAbsolutePath() + File.separator + "wystapieniaM.txt")));
            czasy = new BufferedWriter(new FileWriter(new File("" + newDir.getAbsolutePath() + File.separator + "czasy.txt")));
            tNazwy = new BufferedWriter(new FileWriter(new File("" + newDir.getAbsolutePath() + File.separator + "tNazwy.txt")));
            siecGotowa = new BufferedWriter(new FileWriter(new File("" + newDir.getAbsolutePath() + File.separator + "siecAlg1.apnn")));
            wystapienia.write("4");
            wystapienia.newLine();
        } catch (IOException ex) {
            Logger.getLogger(TworzSiec.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    /**
     * Najwazniejsza metoda. W niej czytane sa kolejne pliki pdb z katalogu,
     * nastepnie dla kazdej klatki wywolywana jest metoda ktora dodaje odpowiednie dane
     * do plikow wynikowych.
     */
    public void tworzenie(){
        ArrayList wsp;
        ArrayList org;
        FilenameFilter filtr = new FilenameFilter() {

            @Override
            public boolean accept(File dir, String name) {
                return (name.endsWith(".pdb"));
            }
        };
        String [] list = dir.list(filtr);
        int dl = list.length;
        boolean koniecP;
        for(int i = 0; i < dl; i++){
            System.out.println("czytam plik: "+list[i]);
            czyt = new CzytaniePdb(list[i], dir, (float)oko);
            czas = 0;
            pierPlik = true;
            do{
                wsp = czyt.zwrocWszystkie();
                org = czyt.zwrocOrg();
                czas = czyt.getKlatka();
                if((wsp != null) && (wsp.size() > 0)){
                    czytanieKlatki(wsp, org);
                }
                if(pierPlik == true){
                    pierPlik = false; 
                }
                koniecP = czyt.koniecPliku();
            }while(koniecP == false);
        }
        dodajStart();
    }
    
    
    /**
     * Metoda ktora dodaje miejsce startowe i laczy je jako wejsciowe ze wszystkimi tranzycjami
     * z listy pierTranz, ktora zwiera tranzycje opisujace pierwsze klatki.
     */
    private void dodajStart(){
        noweMiejsce("start", true);
        int n, nr;
        n = pierTranz.size();
        for(int i = 0; i < n; i++){
            try {
                nr = (int) pierTranz.get(i);
                liczL++;
                luki.write("\\arc{A_"+liczL+"}{\\from{P_"+liczM+"} \\to{T_"+nr+"} \\weight{1} \\type{ordinary}}");
                luki.newLine();
            } catch (IOException ex) {
                Logger.getLogger(TworzSiec.class.getName()).log(Level.SEVERE, null, ex);
            }
                    
        }
    }
    
    /**
     * Majac dane wspolrzedne z obecnej klatki sprawdza miedzy nimi kontakty,
     * ustawia aktualne miejsca jako te kontkaty - moze przy tym tworzyc nowe miejsca
     * oraz tworzy tranzycje przesuwajaca z poprzednich miejsc do obecnych.
     * @param wsp wspolrzedne z obecnej klatki, po konwersji
     * @param org wspolrzedne z obecnej klatki, orginalne
     */
    private void czytanieKlatki(ArrayList wsp, ArrayList org){
        String klucz, kluczT;
        int nrMiej, nrTr;
        int w, n;
        int w1, w2;
        kontakty.noweWsp(wsp, org, oko);
        kluczT = "";
        //dodanie miejsc wejsciowych dla tranzycji do jej klucza jesli takie sa
        if(pierPlik){
            popMiejsca.clear();
        }
        else{
            n = popMiejsca.size();
            for(int i = 0; i < n; i++){
                w1 = (int) popMiejsca.get(i);
                kluczT += "" + w1 + ",";
            }
        }
        ArrayList wyn = (ArrayList) kontakty.zwrocKonkaty(wsp);
        n = wyn.size();
        kluczT += ":";
        //uzyskanie miejsc, sprawdzenie czy takie byly, jesli nie to dodanie
        for(int i = 0; i < n; i += 2){
            w1 = (int) wyn.get(i);
            w2 = (int) wyn.get(i + 1);
            klucz = ""+w1+"i"+w2;
            if(mapaM.containsKey(klucz)){//jest juz takie miejsce
                nrMiej = mapaM.get(klucz);
                w = (int) wysMiej.get(nrMiej);
                wysMiej.set(nrMiej, w + 1);
            }
            else{
                noweMiejsce(klucz, false);
                nrMiej = liczM;
            }
            aktMiejsca.add(nrMiej); //dodaj kontakt do obecnych w tej klatce   
        }
        Collections.sort(aktMiejsca);
        Collections.sort(popMiejsca);
        n = aktMiejsca.size();
        for(int i = 0; i < n; i++){
            nrMiej = (int) aktMiejsca.get(i);
            kluczT += "" + nrMiej + ",";
        }
        //sprawdzenie czy jest tranzycja taka, jesli nie to dodanie
        if(mapaT.containsKey(kluczT)){//jest juz tranzycja, dodaj wystapienie i ew zmien czas
            nrTr = mapaT.get(kluczT);
            w = (int) wys.get(nrTr);
            wys.set(nrTr, w + 1);
            int oldCzas = (int) czTranz.get(nrTr);
            if(oldCzas > czas){
                czTranz.set(nrTr, czas);
            }
        }
        else{//nie ma tranzycji, stworz
            nowaTranz(kluczT);
            stworzPolaczenia(popMiejsca, aktMiejsca, liczT);
            if(pierPlik){
                pierTranz.add(liczT);
            }
        }
        popMiejsca = new ArrayList(aktMiejsca);
        aktMiejsca.clear();
    }
    
    
    
    /**
     * Metoda ktora dodaje nowe miejsce do siebi
     * @param klucz klucz ktory zawiera atomy w kontakcie i opisuje miejsce w pliku i mapie
     * @param full czy miejsce ma byc pelne czy nie
     */
    private void noweMiejsce(String klucz, boolean full){
        try {
            liczM++;
            if(full == false){
                miejsca.write("\\place{P_"+liczM+"}{\\name{p"+liczM+"}\\capacity{1}\\init{0}}");
            }
            else{
                miejsca.write("\\place{P_"+liczM+"}{\\name{p"+liczM+"}\\capacity{1}\\init{1}}");
            }
            miejsca.newLine();
            mapaM.put(klucz, liczM);
            mNazwy.write("p "+liczM + " : " +klucz);
            wysMiej.add(liczM, 1);
            mNazwy.newLine();
         } catch (IOException ex) {
                Logger.getLogger(TworzSiec.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    /**
     * Metoda ktora tworzy nowa tranzycje. dopisuje ja do pliku, ustawia czas na obecny i wystapienia na 1.
     * @param klucz klucz ktory zawiera miejsca wej i wyj tranzycji, opisuje on tranzycje w pliku

     */
    private void nowaTranz(String klucz){
        try {
            liczT++;
            tranz.write("\\transition{T_"+liczT+"}{\\name{t"+liczT+"}}");
            tranz.newLine();
            czTranz.add(liczT, czas);
            wys.add(liczT, 1);
            mapaT.put(klucz, liczT);
            tNazwy.write("t "+liczT+ " : "+klucz);
            tNazwy.newLine();
        } catch (IOException ex) {
                Logger.getLogger(TworzSiec.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    /**
     * Tworzy polaczenia miedzy:
     * miejscami ktorych numery sa w wej - jako miejsca wejsciowe dla tranzycji
     * miejscami ktorych numery sa w wyj - jako miejsca wyjsciowe dla tranzycji
     * 
     * 
     * @param wej lista miejsca wejsciowych
     * @param wyj lista miejsc wyjsciowych
     * @param nrTranz numer tranzycji wokol ktorej sa robione polaczeni
     */
    private void stworzPolaczenia(ArrayList wej, ArrayList wyj, int nrTranz) {
    try {
        int n;
        n = wej.size();
        int nr;
        for(int i = 0; i < n; i++){
            nr = (int) wej.get(i);
            liczL++;
            luki.write("\\arc{A_"+liczL+"}{\\from{P_"+nr+"} \\to{T_"+nrTranz+"} \\weight{1} \\type{ordinary}}");
            luki.newLine();
        }
        n = wyj.size();
        for(int i = 0; i < n; i++){
            nr = (int) wyj.get(i);
            liczL++;
            luki.write("\\arc{A_"+liczL+"}{\\from{T_"+nrTranz+"} \\to{P_"+nr+"} \\weight{1} \\type{ordinary}}");
            luki.newLine();
        }
        } catch (IOException ex) {
                Logger.getLogger(TworzSiec.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    private void przepisz(String name){
        try{
            String line;
            try (BufferedReader br = new BufferedReader(new FileReader(name))) {
                while((line = br.readLine()) != null){
                    siecGotowa.write(line);
                    siecGotowa.newLine();
                }
                siecGotowa.newLine();
            }
            } catch (IOException ex) {
                Logger.getLogger(TworzSiec.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public void koniec(){
        try {
            miejsca.close();
            tranz.close();
            luki.close();
            mNazwy.close();
            siecGotowa.write("\\beginnet{unnamed3}");
            siecGotowa.newLine();
            siecGotowa.newLine();
            siecGotowa.newLine();
            przepisz("" + newDir.getAbsolutePath() + File.separator + "miejsca.tmp");
            przepisz("" + newDir.getAbsolutePath() + File.separator + "tranz.tmp");
            przepisz("" + newDir.getAbsolutePath() + File.separator + "luki.tmp");
            siecGotowa.newLine();
            siecGotowa.write("\\endnet");
            siecGotowa.newLine();
            siecGotowa.close();
            
            int n = wys.size();
            wystapienia.write("tranz: "+liczT+" miejsca: "+liczM);
            wystapienia.newLine();
            for(int i = 0; i < n; i++){
                wystapienia.write("t "+i+" wys: "+(int) wys.get(i) );
                wystapienia.newLine();
            }
            wystapienia.close();
            
            n = wysMiej.size();
            wystapieniaM.write("tranz: "+liczT+" miejsca: "+liczM);
            wystapieniaM.newLine();
            for(int i = 0; i < n; i++){
                wystapieniaM.write("m "+i+" wys: "+(int) wysMiej.get(i) );
                wystapieniaM.newLine();
            }
            wystapieniaM.close();
            
            n = czTranz.size();
            for(int i = 0; i < n; i++){
                czasy.write("t "+i+" czas: "+(int) czTranz.get(i) );
                czasy.newLine();
            }
            czasy.close();
            
            File plik = new File("" + newDir.getAbsolutePath() + File.separator + "miejsca.tmp");
            plik.delete();
            plik = new File("" + newDir.getAbsolutePath() + File.separator + "tranz.tmp");
            plik.delete();
            plik = new File("" + newDir.getAbsolutePath() + File.separator + "luki.tmp");
            plik.delete();
            
            tNazwy.close();
            mNazwy.close();
                        
            
        } catch (IOException ex) {
                Logger.getLogger(TworzSiec.class.getName()).log(Level.SEVERE, null, ex);
        }
   }
        
    
    
}
