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

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.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.biojava.bio.structure.Atom;
import org.biojava.bio.structure.AtomImpl;
import org.biojava.bio.structure.Calc;
import org.biojava.bio.structure.Chain;
import org.biojava.bio.structure.ChainImpl;
import org.biojava.bio.structure.Element;
import org.biojava.bio.structure.Group;
import org.biojava.bio.structure.HetatomImpl;
import org.biojava.bio.structure.SVDSuperimposer;
import org.biojava.bio.structure.StructureException;
import org.biojava.bio.structure.align.StructureAlignment;
import org.biojava.bio.structure.align.StructureAlignmentFactory;
import org.biojava.bio.structure.align.ce.CeMain;
import org.biojava.bio.structure.align.ce.CeParameters;
import org.biojava.bio.structure.align.fatcat.FatCatRigid;
import org.biojava.bio.structure.align.fatcat.calc.FatCatParameters;
import org.biojava.bio.structure.align.model.AFPChain;
import org.biojava.bio.structure.io.PDBParseException;

/**
 * Klasa tworzaca plik z siecia Petriego i kilka innych. Klasa czyta dane z
 * plikow pdb w folderze roboczym i na ich podstawie generuje siec.
 *
 * Jedno miejsce to polozenie wszystkich atomow, zmiana polozenia 1 atomu to
 * nowe miejsce- nowy stan. Tranzycje - przejscia miedzy stanami. Jest miejsce
 * startowe, z tego wychodza tranzycje przestawiajace odpowiadajacym pierwszym
 * polozenim w plikach.
 *
 * @author Ania
 */
public class TworzSiec {

    //czy pierwsze czytanie - wtedy tworzy nowe miejsca dla atomow
    private boolean pierCzyt;
    //czy nowy plik aby zrobic polaczenie z miejsca/atomu
    //numery miejsc z poprzedniego czytania - czy dodawac nowe miejsca
    private int aktMiejsca;
    //numery aktualnych trnazycji ktore ostatnio ustawialy wspolrzedne
    private int aktTranz;
    //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<Integer> czTranz = new ArrayList<>();
    private BufferedWriter czasy;
    //plik zawierajacy ilosc wystapien danego atomu w danej wspolrzendej
    private BufferedWriter wystapienia;
    //plik zawierajacy ile razy dane miejsce bylo odwiedzone
    private BufferedWriter wystapieniaMiej;
    private BufferedWriter wystapieniaPliki;
    private BufferedWriter wystapieniaPlikiMiejsca;
    private List<Integer> wys = new ArrayList<>();
    //lista list dla kazdej tranzycji w jakich plikach jest
    private List pliki = new ArrayList<>();
    //lista list dla kazdego miejsca w jakich plikach jest
    private List plikiM = new ArrayList<>();

    //lista list, kazda lista zawiera po kolei ile razy miejsca o numerze rownym indeksowi
    //jest w danym pliku
    private ArrayList<ArrayList<Integer>> plikiIlosciMiejsc = new ArrayList<>();
    //lista zawierajaca nazwy plikow - w tej kolejnosci sa listy w liscie wyzej
    private ArrayList<String> nazwyPlikow = new ArrayList<>();
    //indeks ostatnio dodanego elementu do list wyzej
    private int fileIndex;
    //plik do zapisu informacji wyzej, pierwsza linia to beda indeksy miejsc, kolejne to dla kolejnych plikow
    //ilosci miejsc
    private BufferedWriter plikiIlosciMiejscPlik;

    //lista zawierajaca ile razy dane miejsce sie pojaiwalo w pliku, czyli ile razy bialko
    //mialo taka konformacje
    private ArrayList miejWyst = new ArrayList();
    //obiekt czytajacy pliki z pdb
    private CzytaniePdb czyt;
    //lista zawierajaca juz dodane miejsca w postaci stringow z ich kluczy
    private ArrayList mapaM = new ArrayList();
    //mapa zwierajaca jako klucze miejsca we i wyj 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;
    //o ile rmsd 2 klatek moze sie roznic aby uznac je za rowne 
    private double prog = 5;
    //numer miejsca startowego
    private int start = 0;

    //ile jest miejsc z nr atomow + startowe
    private int przes;

    private File newDir;

    private String fileName;
    //czy moj aligment czy bioJavy
    private int myAlignment;

    //utils
    private ConformationUtils utils;

    //czesc nazwy dodawana do glownego katalogu z wynikami
    private String dirName = "";

    /**
     * Konstuktor ktory przyjmuje jako katalog z plikami pdb roboczy katalog. Do
     * tego dokonuje inicjalizacji plikow, parametrow itd.
     */
    public TworzSiec() {
        inicjacja();
        dir = new File(System.getProperty("user.dir"));
    }

    /**
     * Konstruktor ktorego argumentem jest katalog z plikami pdb. Do tego
     * dokonuje inicjalizacji plikow, parametrow itd.
     *
     * @param kat
     * @param p
     * @param alg
     */
    public TworzSiec(String kat, double p, int alg) {
        utils = new ConformationUtils();
        dir = new File(kat);
        prog = p;
        myAlignment = alg;
        inicjacja();
    }

    public TworzSiec(String kat, double p, int alg, String name) {
        utils = new ConformationUtils();
        dir = new File(kat);
        prog = p;
        myAlignment = alg;
        dirName = name;
        inicjacja();
    }

    private void inicjacja() {
        try {
            pierCzyt = true;
            czas = 0;
            newDir = new File("" + dir.getAbsolutePath() + File.separator + "alg3" + "-" + dirName);
            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")));
            wystapieniaPliki = new BufferedWriter(new FileWriter(new File("" + newDir.getAbsolutePath() + File.separator + "wystapieniaPliki.txt")));
            wystapieniaPlikiMiejsca = new BufferedWriter(new FileWriter(new File("" + newDir.getAbsolutePath() + File.separator + "wystapieniaPlikiMiejsca.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")));
            plikiIlosciMiejscPlik = new BufferedWriter(new FileWriter(new File("" + newDir.getAbsolutePath() + File.separator + "plikiIloscMiejsc.txt")));
            siecGotowa = new BufferedWriter(new FileWriter(new File("" + newDir.getAbsolutePath() + File.separator + "siecAlg1.apnn")));
            wystapieniaMiej = new BufferedWriter(new FileWriter(new File("" + newDir.getAbsolutePath() + File.separator + "wysMiejsca.txt")));
            wystapienia.write("3");
            wystapienia.newLine();
        } catch (IOException ex) {
            Logger.getLogger(TworzSiec.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Dodaj nazwe pliku do listy oraz nowa liste do listy list. Dla
     * dotychczasowych miejsc dodaj do list zera.
     */
    private void nowyPlik(String name) {
        nazwyPlikow.add(name);
        ArrayList<Integer> lista = new ArrayList<>();
        for (int i = 0; i <= liczM; i++) {
            lista.add(i, 0);
        }
        plikiIlosciMiejsc.add(lista);
        fileIndex = plikiIlosciMiejsc.indexOf(lista);
    }

    /**
     * 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() {
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        Date date = new Date();
        System.out.println(dateFormat.format(date));
        ArrayList wsp;
        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;
        try {
            for (int i = 0; i < dl; i++) {
                System.out.println("czytam plik: " + list[i]);
                czyt = new CzytaniePdb(list[i], dir);
                fileName = list[i];
                nowyPlik(list[i]);
                czas = 0;
                pierPlik = true;
                do {
                    wsp = czyt.zwrocWszystkie();
                    czas = czyt.getKlatka();
                    if (pierCzyt) {
                        ilosc = wsp.size();
                        pierCzyt = false;
                        liczM++;
                        miejsca.write("\\place{P_" + liczM + "}{\\name{p" + liczM + "}\\capacity{1}\\init{1}}");
                        miejsca.newLine();
                        miejWyst.add(liczM, 1);
                        plikiIlosciMiejsc.get(fileIndex).add(liczM, 1);
                        List<String> pom = new ArrayList<>();
                        pom.add("start");
                        plikiM.add(pom);
                        mapaM.add(liczM, "");
                        noweMiejsce(wsp, false);
                        miejWyst.add(liczM, 1);
                        plikiIlosciMiejsc.get(fileIndex).add(liczM, 1);
                        nowaTranz(czas, start, liczM);
                        aktTranz = liczT;
                        aktMiejsca = liczM;
                        stworzPolaczenia(start, liczM);
                        przes = liczM + 1;
                        pierPlik = false;
                    } else if (pierPlik == true) {
                        pierPlik = false;
                        czytaniePierKlatki(wsp);
                    } else {
                        if ((wsp != null) && (wsp.size() > 0)) {
                            kolejneCzyt(wsp);
                        }
                    }
                    koniecP = czyt.koniecPliku();
                } while (koniecP == false);
            }
        } catch (IOException ex) {
            Logger.getLogger(TworzSiec.class.getName()).log(Level.SEVERE, null, ex);
        }
        dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        date = new Date();
        System.out.println(dateFormat.format(date));
    }

    /**
     * Metoda ktora wywolywana jest jesli czytamy pierwsza klatke nastepnego
     * pliku. Rozni sie tym od zwyklego dodawania ze laczy pierwsze miejsce z
     * atomem i miejsce start a nie z poprzednim polozeniem atomu. Zaznacza
     * rowniez ze to startowe polozenie jest aktualnym polozeniem atomu.
     *
     * @param wsp lista wspolrzednych z pliku
     */
    private void czytaniePierKlatki(ArrayList wsp) {
        String klucz;
        int nrMiej, nrTr;
        boolean tworzTranz;
        tworzTranz = false;
        nrMiej = czyZawiera(wsp);
        if (nrMiej == -1) {//nie ma miejsca, tworz nowe
            noweMiejsce(wsp, false);
            nrMiej = liczM;
            miejWyst.add(liczM, 1);
            plikiIlosciMiejsc.get(fileIndex).add(liczM, 1);
            tworzTranz = true;
        } else {//jest miejsce
            int ile = (int) miejWyst.get(nrMiej);
            ile++;
            miejWyst.set(nrMiej, ile);
            int ilePlik = plikiIlosciMiejsc.get(fileIndex).get(nrMiej);
            ilePlik++;
            plikiIlosciMiejsc.get(fileIndex).set(nrMiej, ilePlik);

            List<String> pomM = (List<String>) plikiM.get(nrMiej);
            if (!pomM.contains(fileName)) {
                pomM.add(fileName);
                plikiM.set(nrMiej, pomM);
            }

            klucz = "" + start + "," + nrMiej;
            if (mapaT.containsKey(klucz)) {//jest juz tranzycja - juz zaczynalismy z tego
                nrTr = mapaT.get(klucz);
                //dodanie do listy plikow gdzie wystepuje
                List<String> pom = (List<String>) pliki.get(nrTr);
                if (!pom.contains(fileName)) {
                    pom.add(fileName);
                    pliki.set(nrTr, pom);
                }
                int oldWys = (int) wys.get(nrTr);
                wys.set(nrTr, oldWys + 1);
                aktTranz = nrTr;
            } else {
                tworzTranz = true;
            }
        }
        aktMiejsca = nrMiej;
        if (tworzTranz == true) {//nie ma tranzycji, miejsce bylo lub utworzone
            nowaTranz(czas, start, nrMiej);
            stworzPolaczenia(start, nrMiej);
            aktTranz = liczT;
        }

    }

    /**
     * Sprawdza czy aktualne wspolrzedne pokrywaja sie z wczesniej znalezionymi
     * miejscami - modulo prog. Z kazdej pary aktualne wsp - poprzednie miejsce
     * bierze najmniejsza mozliwa odleglosc struktur jakie da sie uzyskac.
     * Nastepnie wybiera z tych najmniejszych jedna najmniejsza i sprawdza czy
     * jest ona mniejsza niz prog. Zwraca numer miejsca ktore mialo najmniejsza
     * odleglosc (rmsd)
     *
     * @param wsp aktaulne wspolrzedne
     * @return numer miejsca ktorego rmsd jest mniejsze niz prog lub -1 jesli
     * nie ma takiego
     */
    private int czyZawiera(ArrayList wsp) {

        String jeden;
        int minNr = 1;
        double minOdl = Double.MAX_VALUE;
        double rmsd;
        int n = mapaM.size();
        int m = wsp.size();
        //long start_time = System.currentTimeMillis();
        for (int i = 1; i < n; i++) { //po wszystkich miejscach do tej pory dodanych
            jeden = (String) mapaM.get(i);
            if (myAlignment == 2) {
                rmsd = bioJavaAligment(jeden, wsp);
            } else if (myAlignment == 1) {
                rmsd = rmsdAlain(jeden, wsp, true, false);
            } else {
                rmsd = rmsdAlain(jeden, wsp, false, true);
            }
            if (rmsd < minOdl) {
                minOdl = rmsd;
                minNr = i;
            }
        }
        if (myAlignment != 2) {
            minOdl *= 1.0 / m;
            minOdl = Math.sqrt(minOdl);
        }
        //long end_time = System.currentTimeMillis();
        //System.out.println((end_time - start_time));
        //System.out.println(minOdl);
        if (minOdl <= prog) {
            return minNr;
        } else {
            return -1;
        }
    }

    /**
     * Obliczenie najlepszego dopasowania - o najmniejszej odleglosci miedzy
     * aktualna konformacja i jedna z poprzednich (wywolywane dla wszystkich
     * poprzednich). Tu tylko przepisuje wspolrzedne w formacie tekstowym na
     * liste. Dalej w zaleznosci od parametrow wywoluje odpowiednia metode.
     */
    private double rmsdAlain(String poprzednia, ArrayList wsp, boolean obrot, boolean massCenter) {
        double minOdl;
        String[] tab1;
        String[] tab2;
        double[] tabMiejsce = new double[3];
        int m = wsp.size();
        tab1 = poprzednia.split(":");
        ArrayList poprzW = new ArrayList();//wspolrzedne kazdego CA z pop konformacji
        for (int j = 1; j <= m; j++) {//wpisanie tych wsp do listy
            tab2 = tab1[j].split(",");
            tabMiejsce[0] = Double.parseDouble(tab2[0]);
            tabMiejsce[1] = Double.parseDouble(tab2[1]);
            tabMiejsce[2] = Double.parseDouble(tab2[2]);
            poprzW.add(tabMiejsce[0]);
            poprzW.add(tabMiejsce[1]);
            poprzW.add(tabMiejsce[2]);
        }
        if (!massCenter) {
            minOdl = prepareData(wsp, poprzW, obrot);
        } else {
            minOdl = alignCenterMass(wsp, poprzW);
        }
        return minOdl;
    }

    /**
     * Aligment ktory dziala tak: przesuwa bialka tak aby srodki massy obu byly
     * w jednym punkie, w srodku ukladu wspolrzednych. Nastpenie probuje male
     * obroty. Tu tylko oblicza wektory przesuniecia i wywoluje nastepna metode.
     */
    private double alignCenterMass(ArrayList wsp, ArrayList poprzW) {
        double odl, minOdl = Double.MAX_VALUE;
        List<double[]> wspKopia = copyList(wsp);
        double[] moveFirst = utils.centerOfMass(wspKopia);
        double[] moveSecond = utils.centerOfMassList(poprzW);
        odl = translateAndRotate(moveSecond, poprzW, wspKopia, moveFirst, true);
        if (odl < minOdl) {
            minOdl = odl;
        }
        return minOdl;
    }

    /**
     * Metoda ktora tylko oblica wektory przesuniecia dla nalozenia kolejnych
     * atomow CA i dalej wywoluje kolejna metode.
     */
    private double prepareData(ArrayList wsp, ArrayList poprzW, boolean obrot) {
        int m = wsp.size();
        double odl, minOdl = Double.MAX_VALUE;
        double[] tabMiejsce = new double[3];
        double[] tabNowe;
        double[] tabTmp;
        double[] odlPoj = new double[3]; //odleglosc miedzy para ktora chcemy nasunac
        List<double[]> wspKopia;
        for (int j = 0; j < m; j++) {
            tabNowe = new double[4];
            tabTmp = (double[]) wsp.get(j);
            tabNowe[0] = tabTmp[0];
            tabNowe[1] = tabTmp[1];
            tabNowe[2] = tabTmp[2];
            tabNowe[3] = tabTmp[3];
            tabMiejsce[0] = (double) poprzW.get(j * 3);
            tabMiejsce[1] = (double) poprzW.get(j * 3 + 1);
            tabMiejsce[2] = (double) poprzW.get(j * 3 + 2);
            odlPoj[0] = tabMiejsce[0];
            odlPoj[1] = tabMiejsce[1];
            odlPoj[2] = tabMiejsce[2];
            wspKopia = copyList(wsp);
            odl = translateAndRotate(odlPoj, poprzW, wspKopia, tabNowe, obrot);
            if (odl < minOdl) {
                minOdl = odl;
            }
        }
        return minOdl;
    }

    public List<double[]> copyList(List<double[]> wsp) {
        List<double[]> wspKopia = new ArrayList<>();
        for (double[] tab : wsp) {
            double[] tabCopy = new double[4];
            tabCopy[0] = tab[0];
            tabCopy[1] = tab[1];
            tabCopy[2] = tab[2];
            tabCopy[3] = tab[3];
            wspKopia.add(tabCopy);
        }

        return wspKopia;
    }

    public List<Double> copyList2(List<Double> wsp) {
        List<Double> wspKopia = new ArrayList<>();
        for (Double liczba : wsp) {
            Double liczba2 = liczba;
            wspKopia.add(liczba2);
        }

        return wspKopia;
    }

    /**
     * Metoda dokonujaca wlasciwych transformacji. Najpierw bazujac na podanych
     * argumentach przesuwa struktury, dalej probuje obrotow w roznych
     * kierunkach.
     */
    private double translateAndRotate(double[] odlPoj, ArrayList poprzW, List<double[]> wsp, double[] tabPocz, boolean obrot) {
        double rmsdMin;
        int m = wsp.size();
        double tabNowe[];
        double tabMiejsce[] = new double[3];
        List<Double> wspPrzes = new ArrayList<>();
        List<double[]> wspCenter = new ArrayList<>();
        for (int k = 0; k < m; k++) {
            tabNowe = (double[]) wsp.get(k);
            tabNowe[1] = tabNowe[1] - tabPocz[1];
            tabNowe[2] = tabNowe[2] - tabPocz[2];
            tabNowe[3] = tabNowe[3] - tabPocz[3];
            wspCenter.add(tabNowe);
            tabMiejsce[0] = (double) poprzW.get(k * 3);
            tabMiejsce[1] = (double) poprzW.get(k * 3 + 1);
            tabMiejsce[2] = (double) poprzW.get(k * 3 + 2);
            wspPrzes.add(tabMiejsce[0] - odlPoj[0]);
            wspPrzes.add(tabMiejsce[1] - odlPoj[1]);
            wspPrzes.add(tabMiejsce[2] - odlPoj[2]);
        }
        double rmsd1 = utils.calcRmsd(wspPrzes, wspCenter);
        rmsdMin = rmsd1;
        if (obrot) {
            int katy = 4;
            rmsdMin = obrotRmsd(wspCenter, wspPrzes, true, false, false, rmsdMin, rmsd1, katy);
            rmsdMin = obrotRmsd(wspCenter, wspPrzes, false, true, false, rmsdMin, rmsd1, katy);
            rmsdMin = obrotRmsd(wspCenter, wspPrzes, false, false, true, rmsdMin, rmsd1, katy);
            rmsdMin = obrotRmsd(wspCenter, wspPrzes, true, true, false, rmsdMin, rmsd1, katy);
            rmsdMin = obrotRmsd(wspCenter, wspPrzes, true, false, true, rmsdMin, rmsd1, katy);
            rmsdMin = obrotRmsd(wspCenter, wspPrzes, false, true, true, rmsdMin, rmsd1, katy);
            rmsdMin = obrotRmsd(wspCenter, wspPrzes, true, true, false, rmsdMin, rmsd1, katy);
        }
        return rmsdMin;
    }

    /**
     * Metoda wykonujaca oborty - nie wykonuja ona wlasciwych kalkucacji, te
     * wykonuje metoda z ConformationUtils. Ona tylko zwieksza kolejno kat i
     * wywoluje obracanie. Nastepnie sprawdza kat w druga strone.
     */
    private double obrotRmsd(List<double[]> wsp, List<Double> wspPrzes, boolean osX, boolean osY, boolean osZ, double rmsdMin, double rmsd, int katy) {
        double rmsdPop;
        double podstawaKat = utils.getPodstawaKat();
        for (int i = 1; i <= katy; i++) {
            List<Double> wspPrzesKopia = copyList2(wspPrzes);
            wspPrzesKopia = utils.obroc(wspPrzesKopia, i * podstawaKat, osX, osY, osZ);
            rmsdPop = rmsd;
            rmsd = utils.calcRmsd(wspPrzesKopia, wsp);
            if (rmsd == 0) {
                System.out.println(rmsd);
            }
            if (rmsd < rmsdMin) {
                rmsdMin = rmsd;
            } else {
                if (rmsd > rmsdPop) {
                    break;
                }
            }
        }
        for (int i = 1; i <= katy; i++) {
            List<Double> wspPrzesKopia = copyList2(wspPrzes);
            wspPrzesKopia = utils.obroc(wspPrzesKopia, -1.0 * i * podstawaKat, osX, osY, osZ);
            rmsdPop = rmsd;
            rmsd = utils.calcRmsd(wspPrzesKopia, wsp);
            if (rmsd == 0) {
                System.out.println(rmsd);
            }
            if (rmsd < rmsdMin) {
                rmsdMin = rmsd;
            } else {
                if (rmsd > rmsdPop) {
                    break;
                }
            }
        }
        return rmsdMin;
    }

    /**
     * Metoda nie uzywana, zle dzialajaca. Ona chyba powinna dzialac podobnie
     * jak moj aligment ze srodkami mas ale daje absurdalnie duze wyniki. Pewnie
     * uzywana jest w zly sposob BioJava.
     */
    private double simpleAlignBioJava(String poprzednia, ArrayList wsp) {
        int m = wsp.size();
        String[] tab1;
        String[] tab2;
        double[] tabNowe;
        double tabNowa2[] = new double[3];
        tab1 = poprzednia.split(":");
        Atom[] popKonf = new AtomImpl[m];
        Atom[] currKonf = new AtomImpl[m];
        for (int j = 0; j < m; j++) {
            popKonf[j] = new AtomImpl();
            currKonf[j] = new AtomImpl();
            tab2 = tab1[j + 1].split(",");
            popKonf[j].setX(Double.parseDouble(tab2[0]));
            popKonf[j].setY(Double.parseDouble(tab2[1]));
            popKonf[j].setZ(Double.parseDouble(tab2[2]));
            tabNowe = (double[]) wsp.get(j);
            tabNowa2[0] = tabNowe[1];
            tabNowa2[1] = tabNowe[2];
            tabNowa2[2] = tabNowe[3];
            currKonf[j].setCoords(tabNowa2);
            /*if (j % 100 == 0) {
             System.out.println("current: " + currKonf[j].getCoords()[0] + " " + currKonf[j].getCoords()[1] + " " + currKonf[j].getCoords()[2]);
             System.out.println("pop: " + popKonf[j].getCoords()[0] + " " + popKonf[j].getCoords()[1] + " " + popKonf[j].getCoords()[2]);
             }*/
        }
        try {
            SVDSuperimposer superimposer = new SVDSuperimposer(popKonf, currKonf);
            for (Atom atom : currKonf) {

                Calc.rotate(atom, superimposer.getRotation());
                Calc.shift(atom, superimposer.getTranslation());
            }
            return SVDSuperimposer.getRMS(popKonf, currKonf);
        } catch (StructureException ex) {
            Logger.getLogger(TworzSiec.class.getName()).log(Level.SEVERE, null, ex);
        }
        return Double.MAX_VALUE;
    }

    /**
     * Aligment z BioJavy - aktualnie CE. Zmiana zakomentowanych linii uruchomi
     * aligment FATCAT.
     */
    private double bioJavaAligment(String poprzednia, ArrayList wsp) {
        try {
            String[] tab1;
            String[] tab2;
            double[] tabNowe;
            double tabNowa2[];
            int m = wsp.size();
            tab1 = poprzednia.split(":");
            Atom[] popKonf = new AtomImpl[m];
            Atom[] currKonf = new AtomImpl[m];
            Group[] groupA = new HetatomImpl[m];
            Group[] groupB = new HetatomImpl[m];
            Chain chainA = new ChainImpl();
            chainA.setChainID("A");
            Chain chainB = new ChainImpl();
            chainB.setChainID("B");
            for (int j = 0; j < m; j++) {
                popKonf[j] = new AtomImpl();
                currKonf[j] = new AtomImpl();
                popKonf[j] = setAtom(popKonf[j], j);
                currKonf[j] = setAtom(currKonf[j], j);
                tab2 = tab1[j + 1].split(",");
                popKonf[j].setX(Double.parseDouble(tab2[0]));
                popKonf[j].setY(Double.parseDouble(tab2[1]));
                popKonf[j].setZ(Double.parseDouble(tab2[2]));
                tabNowe = (double[]) wsp.get(j);
                tabNowa2 = new double[3];
                tabNowa2[0] = tabNowe[1];
                tabNowa2[1] = tabNowe[2];
                tabNowa2[2] = tabNowe[3];
                currKonf[j].setCoords(tabNowa2);

                groupA[j] = new HetatomImpl();
                groupB[j] = new HetatomImpl();
                groupA[j] = setGroup(groupA[j], j, "A");
                groupB[j] = setGroup(groupB[j], j, "B");
                groupA[j].addAtom(currKonf[j]);
                groupB[j].addAtom(popKonf[j]);
                chainA.addGroup(groupA[j]);
                chainB.addGroup(groupB[j]);
            }
            //StructureAlignment alignment = new CeMain();
            //CeParameters params = new CeParameters();
            StructureAlignment alignment = StructureAlignmentFactory.getAlgorithm(FatCatRigid.algorithmName);
            FatCatParameters params = new FatCatParameters();

            AFPChain afpChain = alignment.align(popKonf, currKonf, params);
            return afpChain.getTotalRmsdOpt();
        } catch (StructureException ex) {
            Logger.getLogger(TworzSiec.class.getName()).log(Level.SEVERE, null, ex);
        }
        return Double.MAX_VALUE;
    }

    private Group setGroup(Group group, int j, String chain) {
        try {
            group.setPDBName("ALA");
            group.setPDBFlag(false);
            group.setResidueNumber(chain, j, null);
        } catch (PDBParseException ex) {
            Logger.getLogger(TworzSiec.class.getName()).log(Level.SEVERE, null, ex);
        }
        return group;
    }

    private Atom setAtom(Atom atom, int j) {
        atom.setFullName(" CA ");
        atom.setElement(Element.Ca);
        atom.setName("CA");
        atom.setPDBserial(j);
        return atom;
    }

    private void kolejneCzyt(ArrayList wsp) {

        String klucz;
        boolean wystap;

        int miejscePlik = czyZawiera(wsp);
        if (miejscePlik == -1) {//nie ma miejsca takiego wiec na pewno nie ma tranz
            noweMiejsce(wsp, false);
            miejWyst.add(liczM, 1);
            plikiIlosciMiejsc.get(fileIndex).add(liczM, 1);
            nowaTranz(czas, aktMiejsca, liczM);
            stworzPolaczenia(aktMiejsca, liczM);
            aktMiejsca = liczM;
            aktTranz = liczT;
        } else {//jest miejsce
            int popMiej = aktMiejsca;
            aktMiejsca = miejscePlik;

            List<String> pomM = (List<String>) plikiM.get(miejscePlik);
            if (!pomM.contains(fileName)) {
                pomM.add(fileName);
                plikiM.set(miejscePlik, pomM);
            }

            int ile = (int) miejWyst.get(miejscePlik);
            ile++;
            miejWyst.set(miejscePlik, ile);
            int ilePlik = plikiIlosciMiejsc.get(fileIndex).get(miejscePlik);
            ilePlik++;
            plikiIlosciMiejsc.get(fileIndex).set(miejscePlik, ilePlik);
            if (popMiej == miejscePlik) {//atom sie nie przesunal - stare i nowe miejsca rowne

            } else {//atom sie przesunal
                klucz = "" + popMiej + "," + miejscePlik;
                wystap = mapaT.containsKey(klucz);
                if (wystap == false) {//nie ma tranzycji przesuwajacej - nowe przesuniecie
                    //nowaTranz3(czas, (int)tab[0], miejscePlik, popMiej);
                    nowaTranz(czas, popMiej, miejscePlik);
                    stworzPolaczenia(popMiej, miejscePlik);
                    aktTranz = liczT;
                } else {//juz wczesniej przesunal sie na ta pozycje
                    int nrTranz = mapaT.get(klucz);
                    //dodawanie do listy plikow w ktorych wystepuje
                    List<String> pom = (List<String>) pliki.get(nrTranz);
                    if (!pom.contains(fileName)) {
                        pom.add(fileName);
                        pliki.set(nrTranz, pom);
                    }

                    int oldCzas = (int) czTranz.get(nrTranz);
                    if (oldCzas > czas) {
                        czTranz.set(nrTranz, czas);
                    }
                    int oldWys = (int) wys.get(nrTranz);
                    wys.set(nrTranz, oldWys + 1);
                    aktTranz = nrTranz;
                }
            }
        }
    }

    /**
     * Metoda ktora tworzy nowe miejsce o wspolrzednych w tablicy. Tworzenie
     * miejsca obejmuje wpisanie do pliku z miejscami (ktory bedzie scalony jako
     * czesc pliku wynikowego), dodanie do HashMapy, wpisanie do pliku z nazwami
     * miejsc oraz zwiekszenie licznika miejsc. Moze tez ustawic zeton w
     * miejscu.
     *
     * @param tab tablica ktorej elemnty 1-3 zwieraja wspolrzedne
     * @param full czy miejsce ma byc pelne - 1 zeton, czy nie
     */
    private void noweMiejsce(ArrayList wsp, boolean full) {
        try {
            liczM++;
            double[] tab;
            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();
            List<String> pomList = new ArrayList<>();
            pomList.add(fileName);
            plikiM.add(liczM, pomList);
            String klucz = "";
            for (int i = 0; i < ilosc; i++) {
                tab = (double[]) wsp.get(i);
                klucz += " atom - " + (int) tab[0] + ":" + (tab[1]) + "," + (tab[2]) + "," + (tab[3]) + ",";
            }

            mapaM.add(liczM, klucz);
            mNazwy.write("p " + liczM + " : " + klucz);
            mNazwy.newLine();
        } catch (IOException ex) {
            Logger.getLogger(TworzSiec.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Tworzy nowa trnazycje, ktora bedzie odpowiadac za przesuniecie atomu z
     * popMiej do miejscePlik (oba to numery miejsc). Dodaje odpowiednie
     * warotsci do plikow, map i list.
     *
     * @param czas aktulany czas
     * @param miejscePlik numer miejsca do ktorego przesuwany jest atom
     * @param popMiej numer miejsca z kotrego przesuwany jest atom
     */
    private void nowaTranz(int czas, int popMiej, int miejscePlik) {
        try {
            liczT++;
            tranz.write("\\transition{T_" + liczT + "}{\\name{t" + liczT + "}}");
            tranz.newLine();
            czTranz.add(liczT, czas);
            wys.add(liczT, 1);
            List<String> pomList = new ArrayList<>();
            pomList.add(fileName);
            pliki.add(liczT, pomList);
            mapaT.put("" + popMiej + "," + miejscePlik, liczT);
            tNazwy.write("t " + liczT + " : " + "z: p" + popMiej + " , do: p" + miejscePlik);
            tNazwy.newLine();
        } catch (IOException ex) {
            Logger.getLogger(TworzSiec.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Tworzy polaczenia miedzy: nowo dodana tranzycja i miejscem z pliku (ze
     * atom przeszedl do tych wspolrzendych) miejscem bedacym poprzednia
     * lokalizacja atomu oraz nowo utworzona tranzycja
     *
     * @param miejscePlik mr miejsca do ktorego przesunal sie atom (wziete z
     * pliku)
     * @param popMiej nr poprzedniego miejsca w ktorym byl atom
     */
    private void stworzPolaczenia(int popMiej, int miejscePlik) {
        try {
            liczL++;
            luki.write("\\arc{A_" + liczL + "}{\\from{T_" + liczT + "} \\to{P_" + miejscePlik + "} \\weight{1} \\type{ordinary}}");
            luki.newLine();
            liczL++;
            luki.write("\\arc{A_" + liczL + "}{\\from{P_" + popMiej + "} \\to{T_" + liczT + "} \\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();
            tNazwy.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 = czTranz.size();
            for (int i = 0; i < n; i++) {
                czasy.write("t " + i + " czas: " + (int) czTranz.get(i));
                czasy.newLine();
            }
            czasy.write("ostatni czas ostatniej symulacji: " + czas);
            czasy.newLine();
            czasy.close();

            n = pliki.size();
            for (int i = 0; i < n; i++) {
                List<String> pom = (List<String>) pliki.get(i);
                wystapieniaPliki.write("t " + i + " plik: ");
                for (String napis : pom) {
                    wystapieniaPliki.write(napis + ", ");
                }
                wystapieniaPliki.newLine();
            }
            wystapieniaPliki.close();

            n = plikiM.size();
            for (int i = 0; i < n; i++) {
                List<String> pom = (List<String>) plikiM.get(i);
                wystapieniaPlikiMiejsca.write("p " + i + " plik: ");
                for (String napis : pom) {
                    wystapieniaPlikiMiejsca.write(napis + ", ");
                }
                wystapieniaPlikiMiejsca.newLine();
            }
            wystapieniaPlikiMiejsca.close();

            n = miejWyst.size();
            for (int i = 0; i < n; i++) {
                wystapieniaMiej.write("p " + i + " wys: " + (int) miejWyst.get(i));
                wystapieniaMiej.newLine();
            }
            wystapieniaMiej.close();

            plikiIlosciMiejscPlik.write("miejsce,");
            for (int i = 0; i <= liczM; i++) {
                plikiIlosciMiejscPlik.write(i + ",");
            }
            plikiIlosciMiejscPlik.newLine();
            int index = 0;
            for (ArrayList<Integer> lista : plikiIlosciMiejsc) {
                plikiIlosciMiejscPlik.write(nazwyPlikow.get(index) + ",");
                for (Integer ilosciPlik : lista) {
                    plikiIlosciMiejscPlik.write(ilosciPlik + ",");
                }
                plikiIlosciMiejscPlik.newLine();
                index++;
            }
            plikiIlosciMiejscPlik.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);
        }
    }

}
