/*
Zde jsou uvedeny funkce pro vystupni data
 */
package octantKlient;

import com.javadocmd.simplelatlng.LatLng;
import com.javadocmd.simplelatlng.LatLngTool;
import com.javadocmd.simplelatlng.util.LengthUnit;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 *
 * @author FilipDvorak
 */
public class VystupniData {

    public static JSONArray spocitejPolohyCilu(ArrayList<OrientacniBod> poleOrientacnichBodu, JSONArray jsonPoleCilu) {

        JSONObject zaznam = null;
        JSONArray jsonPoleCiluNove = new JSONArray();

        Log.zapisDoLogu("Pocitam polohy cile od jednotlivych oreientacnich bodu");

        //prohledava JSON pole s informacemi o cilech -> pocet cilu = pocet opakovani cyklu for
        for (int i = 0; i < jsonPoleCilu.length(); i++) {
            //START - zacatek mereni casu trvani metody
            long zacatekVypoctu = (new Date()).getTime();
            try {
                //zaznam - jedna polozka v poli jsonPoleCilu
                zaznam = jsonPoleCilu.getJSONObject(i);
            } catch (JSONException ex) {
                Logger.getLogger(VystupniData.class.getName()).log(Level.SEVERE, null, ex);
                Log.zapisDoLogu(ex);
            }

            //metoda vytvori pole s ip adresami orientacnich bodu - nacte je z pole poleOrientacnichBodu
            ArrayList<String> ipAdresyOrientacnichBodu = VstupniData.vytvorPoleIpAdresOrientacnichBodu(poleOrientacnichBodu);

            
            HashMap<String, String> hashIpAdresaVzdalenost = null;
            HashMap<String, String> hashIpAdresaVzdalenostNova = new HashMap<String, String>();
            HashMap<String, String> hashIpAdresaVzdalenostSpatne = new HashMap<String, String>();
            
            //nasledujici fce se pripoji na postupne na vsechny uzly v siti PlanetLab, preda jim IP adresu cile a jako odpoved 
            //dostane od kazdeho z nich dve vzdalenosti cili  
            //HashMap - seznam hodnot podle klice - IP adresa a vzdalenost je hodnota - horni a spodni
            
            try {
                //System.out.println("Cilova IP (zaznam) je :" + zaznam.getString("ip"));
                Log.zapisDoLogu("IP adresa (zaznam) cile je :" + zaznam.getString("ip"));
                hashIpAdresaVzdalenost = Ping.pozadavekNaOrienacniBod(ipAdresyOrientacnichBodu, zaznam.getString("ip"));
                //System.out.println("SSH spojeni dokoncena");
            } catch (Exception ex) {
                Log.zapisDoLogu("Nepodarilo se ziskat zaznam IP z pole JSON");
                Log.zapisDoLogu(ex);
            }

          //funkce vymaze spatne hodnoty vzdalenosti spolu s jejich IP adresamy z hash mapy
            for (String aktualniAdresa : hashIpAdresaVzdalenost.keySet()) {
                String vzdalenost = hashIpAdresaVzdalenost.get(aktualniAdresa);
                if (!(vzdalenost == null || vzdalenost.equals("NaN"))) {
                    hashIpAdresaVzdalenostNova.put(aktualniAdresa, vzdalenost);

                }else{
                    //pro zaznam nedostupnych uzlu
                    hashIpAdresaVzdalenostSpatne.put(aktualniAdresa, vzdalenost);
                }
            }

            
            
            //vymazani souboru
            Soubory.smazSoubor();
            
             Log.zapisDoLogu("Pocet nedostupnych orientacnich bodu je:" + hashIpAdresaVzdalenostSpatne.size());
            //zapsani do pole spatnych orientacnich uzlu
            for (String aktualniAdresaSpatnehoUzlu : hashIpAdresaVzdalenostSpatne.keySet()) {
                Log.zapisDoLogu(aktualniAdresaSpatnehoUzlu);
                Soubory.zapisDoSouboruSpatneUzly(aktualniAdresaSpatnehoUzlu, hashIpAdresaVzdalenostSpatne.get(aktualniAdresaSpatnehoUzlu));
            }

             Log.zapisDoLogu("Nedostune uzly zapsany");
            
             //vytvori pole kde budou nasledne ulozeny OB (stredy kruznic) s polomerem (vzdalenost od OB k cili)
            ArrayList<Kruznice> kruzniceSeStredyVorientacnichBodech = new ArrayList<Kruznice>();

             //pole do ktereho budou ukladany nedostupne orientacniBody (planetlabu) -- neni Java, udavaji spatne hodnoty apod.
            ArrayList<OrientacniBod> nedostupneOrientacniBody = new ArrayList<OrientacniBod>();
           
            
            //pro kazdy orientacni bod najde a priradi vzdalenost
            for (OrientacniBod aktualniOrientacniBod : poleOrientacnichBodu) {
                //ve vzdalenosti jsou ulozeny pozitivni/negativni polomery kruznic     
                String vzdalenosti = hashIpAdresaVzdalenostNova.get(aktualniOrientacniBod.getIp().getIpAdresa());
                //System.out.println(aktualniOrientacniBod.getIp().getIpAdresa() + " " + vzdalenosti);
                //Vypis hodnot do logovaciho souboru
                Log.zapisDoLogu("Orientacni bod " + aktualniOrientacniBod.getIp().getIpAdresa() + " " + "hodnoty vzdalenosti" +vzdalenosti);
                
                if(vzdalenosti == null){
                    continue;
                }
                
               
                
                //rozdeli promennou vzdalenosti na pozitivni a negativni
                String[] hodnota = vzdalenosti.split(" ");
                Double vzdalenostNegativni = 0.0;
                Double vzdalenostPozitivni = 0.0;
                //podminka pro overeni hodnot - pokud se metoda nepripoji na orientacni uzel je oznamena chyba
                //chybne vzdalenosti jsou vyrazeny
                if (!vzdalenosti.equals("NaN") && !vzdalenosti.equals("bezSSH") && !vzdalenosti.equals("bezklice") && !vzdalenosti.equals("neniJAVA")) {
                    vzdalenostNegativni = Double.valueOf(hodnota[0]);
                    vzdalenostPozitivni = Double.valueOf(hodnota[1]);
                } //pokud chyba nastala tak ji vypise spolus IP adresou orientacniho uzlu do pole a ulozi do souboru
                else {
                    nedostupneOrientacniBody.add(aktualniOrientacniBod);
                    Log.zapisDoLogu("Zapisuji do souboru spatne uzly");
                    Soubory.zapisDoSouboruSpatneUzly(aktualniOrientacniBod.getIp().getIpAdresa().toString(), vzdalenosti);
                }
   

                //podminka pro overeni nenuloveho polomeru kruznice - vzdalenosti
                if (vzdalenostNegativni > 0 && vzdalenostPozitivni > 0) {
                    Kruznice aktualniKruznice = new Kruznice(aktualniOrientacniBod, vzdalenostPozitivni, vzdalenostNegativni);
                    kruzniceSeStredyVorientacnichBodech.add(aktualniKruznice);

                //pokud je nulova tak se zapise do pole nedostupnych orientacnich bodu a ulozi do souboru
                } else {
                    nedostupneOrientacniBody.add(aktualniOrientacniBod);
                    Soubory.zapisDoSouboruSpatneUzly(aktualniOrientacniBod.getIp().getIpAdresa().toString(), vzdalenosti);
                }
            }

            if (!nedostupneOrientacniBody.isEmpty()) {
                String nedostupneOrienatacniBodyString = " Pocet nedostupnych OB :" + nedostupneOrientacniBody.size();
                Log.zapisDoLogu(nedostupneOrienatacniBodyString);
            }
            
            //umela dat - slouzi pro TEST na lokalnim PC
            ArrayList<Kruznice> pokusss = new ArrayList<Kruznice>();
            pokusss.add(new Kruznice(new OrientacniBod(new Souradnice(500.0, 500.0), null), 500.0, 400.0));
            pokusss.add(new Kruznice(new OrientacniBod(new Souradnice(1350.0, 450.0), null), 500.0, 400.0));
            pokusss.add(new Kruznice(new OrientacniBod(new Souradnice(900.0, 900.0), null), 700.0, 200.0));
            Vykresleni.vykresliKruznice(pokusss);
            
            //vykresleni kruznic
            Vykresleni.vykresliKruznice(kruzniceSeStredyVorientacnichBodech);
            
            Log.zapisDoLogu("Hledam stredni polomery kruznic");
            //funkce pro vypocet kruznic, ktere rozdeli mezikruzi a vytvori nove kruznice s polomerem vnitrni plus 1/3 mezikruzi
            //funkce dostane jako parametr realna data - pro test na SERVERu
            //ArrayList<Kruznice> kruzniceSeStrednimPolomerem = Vypocet.najdiStredniPolomeryKruznic(kruzniceSeStredyVorientacnichBodech);
            //funkce dostane jako parametr umela data - pro test na lokalnim PC
            ArrayList<Kruznice> kruzniceSeStrednimPolomerem = Vypocet.najdiStredniPolomeryKruznic(pokusss);
            
            //vykresleni kruznic se strednim polomerem
            Vykresleni.vykresliKruzniceStredni(kruzniceSeStrednimPolomerem);


            
            Log.zapisDoLogu("Pocet novych kruznic je :" + kruzniceSeStrednimPolomerem.size());
            //System.out.println("Pocet kruznic se strednimpolomerem je :" + kruzniceSeStrednimPolomerem.size());
            
            Log.zapisDoLogu("Hledam pruseciky novych kruznic");
            //funkce jenz hleda pruseciky kruznic se strednim polomerem
            ArrayList<Prusecik> prusecikyDvouKruznic = Vypocet.najdiPrusecikyKruznicSeStrednimPolomerem(kruzniceSeStrednimPolomerem);
            
            Log.zapisDoLogu("Pocet pruseciku novych kruznice je : " + prusecikyDvouKruznic.size());

            //overi zda se nasel alspon jeden prusecik
            if (prusecikyDvouKruznic == null) {
                return null;
            }

            //fce ktera vrati pruseciky, ktere se nalezaji ve vsech mezikruzich kruznic umistenych v poli
            //pro test na SERVERu
            // ArrayList<Prusecik> prusecikyVeVsechkruznicich = Vypocet.najdiPrusecikyLeziciVeVSechKruznicich(prusecikyDvouKruznic, kruzniceSeStredyVorientacnichBodech);
            //umela data pro TEST na lokalnim PC
             ArrayList<Prusecik> prusecikyVeVsechkruznicich = Vypocet.najdiPrusecikyLeziciVeVSechKruznicich(prusecikyDvouKruznic, pokusss);

            //pokud bude prusecik ve vech kruznicih jen jeden tak se vypisi jeho souradnice 
            if (prusecikyVeVsechkruznicich.size() == 1) {
                try {
                    //vkladani do zaznamu - do JSON            ---//111.33 a 64.152 slouzi na prepocet zpet na stupne

                    zaznam.put("latitude", prusecikyVeVsechkruznicich.get(0).getSouradnice().getSouradniceY() / 111.33);
                    zaznam.put("longitude", prusecikyVeVsechkruznicich.get(0).getSouradnice().getSouradniceX() / 64.152);
                } catch (JSONException ex) {
                    Log.zapisDoLogu("Nepodarilo se zapsat souradnice ciloveho pruseciku");
                }
            } else if (prusecikyVeVsechkruznicich.size() == 2) {
                Prusecik cilovyPrusecik = Vypocet.vypoctiCilovyPrusekZeDvouPruseciku(prusecikyVeVsechkruznicich);

                try {

                    zaznam.put("latitude", cilovyPrusecik.getSouradnice().getSouradniceY() / 111.33);
                    zaznam.put("longitude", cilovyPrusecik.getSouradnice().getSouradniceX() / 64.152);
                } catch (JSONException ex) {
                    Log.zapisDoLogu("Nepodarilo se zapsat souradnice ciloveho pruseciku");
                }
            } else if (prusecikyVeVsechkruznicich.size() > 2) {
                //fce ktera vypocita odhad umisteni cile
                Prusecik cilovyPrusecik = Vypocet.vypoctiCilovyPrusecik(prusecikyVeVsechkruznicich);
                
                // System.out.println("cilovy prusecik " + cilovyPrusecik.getSouradnice().getSouradniceX() + " " + cilovyPrusecik.getSouradnice().getSouradniceY());
                //Vykresli pruseciky ve vsech kruznicich a zaroven i ten hledany
                Vykresleni.vykresliPrusecikyAVyslednyPrusecik(prusecikyVeVsechkruznicich, cilovyPrusecik);

                
                try {
                    zaznam.put("latitude", cilovyPrusecik.getSouradnice().getSouradniceY() / 111.33);
                    zaznam.put("longitude", cilovyPrusecik.getSouradnice().getSouradniceX() / 64.152);
                    zaznam.put("comment", cilovyPrusecik.getObsahOblasti());
                } catch (JSONException ex) {
                    Log.zapisDoLogu("Nepodarilo se zapsat souradnice ciloveho pruseciku");
                }
            }


            //overi zda existuje cilovy prusecik
            if (prusecikyVeVsechkruznicich.size() >= 1) {
                jsonPoleCiluNove.put(zaznam);
                try {
                    Log.zapisDoLogu("Cilovy prusecik ma souradnice :" + zaznam.get("longitude") + " " + zaznam.get("latitude"));
                } catch (JSONException ex) {
                    Log.zapisDoLogu(ex);
                }
            }


            //jak dlouho trval vypocet
            long konecVypoctu = (new Date()).getTime();
            try {
                zaznam.put("duration", (konecVypoctu - zacatekVypoctu) / 1000);
                Log.zapisDoLogu("Vysledna poloha je : " + jsonPoleCiluNove.toString());
            } catch (JSONException ex) {
                Logger.getLogger(VystupniData.class.getName()).log(Level.SEVERE, null, ex);
                Log.zapisDoLogu(ex);
            }
        }

      
        return jsonPoleCiluNove;
    }

    public static ArrayList<Double> spocitejChybnouVzdalenost(JSONArray jsonPoleCilu) {

        ArrayList<Double> pocetKm = new ArrayList<Double>(null);
        JSONArray jsonPlanetLabsUzly = new JSONArray();
        JSONObject zaznam;
        try {


            ArrayList<CilovyPrusecik> bodCile = new ArrayList<CilovyPrusecik>();
            ArrayList<CilovyPrusecik> znameSouradnice = new ArrayList<CilovyPrusecik>();

            //nacteni IP adresy ze souboru se znamimy souradnicemi
            //pro nacteni orientacnich bodu ze souboru 
            BufferedReader br1 = new BufferedReader(new FileReader("/var/www/html/bachsoft/public/methods/xdvora02/znamebody.txt"));
            StringBuilder sb1 = new StringBuilder();
            String line1 = null;
            while ((line1 = br1.readLine()) != null) {
                sb1.append(line1);
            }
            try {
                jsonPlanetLabsUzly = new JSONArray(sb1.toString());
            } catch (JSONException ex) {
                Logger.getLogger(VystupniData.class.getName()).log(Level.SEVERE, null, ex);
            }

            //projizdi JSON pole s informacemi o OB
            for (int i = 0; i < jsonPlanetLabsUzly.length(); i++) {
                try {
                    zaznam = jsonPlanetLabsUzly.getJSONObject(i);

                    if (zaznam == null) {
                        continue;
                    }

                    znameSouradnice.add(new CilovyPrusecik((new Souradnice(zaznam.getDouble("longitude"), zaznam.getDouble("latitude"))), new IpAdresa(zaznam.getString("ip"))));


                } catch (JSONException ex) {
                    Logger.getLogger(VystupniData.class.getName()).log(Level.SEVERE, null, ex);
                }

                //podminka kdyz bude zaznam nulovy

            }

            //projizdi JSON pole s informacemi o cilech - nutno zjistit souradnice
            for (int i = 0; i < jsonPoleCilu.length(); i++) {
                try {
                    zaznam = jsonPoleCilu.getJSONObject(i);

                    //podminka kdyz bude zaznam nulovy
                    if (zaznam == null) {
                        continue;
                    }

                    bodCile.add(new CilovyPrusecik((new Souradnice(zaznam.getDouble("longitude"), zaznam.getDouble("latitude"))), new IpAdresa(zaznam.getString("ip"))));

                    for (int j = 0; j < jsonPlanetLabsUzly.length(); j++) {
                        if (znameSouradnice.get(j).getIpAdresa().getIpAdresa().equals(bodCile.get(i).getIpAdresa().getIpAdresa())) {

                            //longitude:X a latitude:Y
                            //geo vypocet vzdalenosti dvou bodu
                            LatLng bodA = new LatLng(znameSouradnice.get(j).getSouradniceA().getSouradniceY(), znameSouradnice.get(j).getSouradniceA().getSouradniceX());
                            LatLng bodB = new LatLng(bodCile.get(i).getSouradniceA().getSouradniceY(), bodCile.get(i).getSouradniceA().getSouradniceX());

                            Double vzd = LatLngTool.distance(bodA, bodB, LengthUnit.KILOMETER);
                            pocetKm.add(vzd);
                        }
                    }



                } catch (JSONException ex) {
                    Logger.getLogger(VystupniData.class.getName()).log(Level.SEVERE, null, ex);
                }


            }

        } catch (IOException ex) {
            Logger.getLogger(VystupniData.class.getName()).log(Level.SEVERE, null, ex);
        }
        return pocetKm;
    }
}