package com.example.demo.waluty;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URL;
import java.time.LocalDate;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/* Adnotacje @Component, @Service, @Repository, a także @Controller i @RestController
 * powodują, że:
 * 1) podczas startu aplikacji Spring tworzy obiekt tej klasy (pojedynczą sztukę, czyli "singleton")
 * 2) ten obiekt będzie dostępny dla innych jako "komponent" ("bean") i będzie go wstrzykiwać
 * 
 * @Primary w tym miejscu oznacza, że w razie występowania innych komponentów implementujących ten sam interfejs,
 * Spring będzie preferował tę implementację.
 * 
 */
@Service
@Primary
public class PobieranieXML implements PobieranieWalut {
    private static final String ADRES = "http://api.nbp.pl/api/exchangerates/tables";

    /** Pobiera tabelę z bieżącymi kursami walut.
     * Zwraca null w przypadku błędów.
     */
    public TabelaWalut pobierzBiezaceKursy() {
        Document doc = wczytajXmlZAdresu(ADRES + "/a?format=xml");
        return tabelaZXml(doc);
    }
    
    public TabelaWalut pobierzArchiwalneKursy(String data) {
        Document doc = wczytajXmlZAdresu(ADRES + "/A/" + data + "?format=xml");
        return tabelaZXml(doc);
    }

    private Document wczytajXmlZAdresu(String adres) {
        // Document Object Model
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();

            URL url = new URL(adres);
            try (InputStream in = url.openStream()) {
                return db.parse(in);
            }
        } catch (ParserConfigurationException | SAXException | IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    private TabelaWalut tabelaZXml(Document doc) {
        if (doc == null)
            return null;
        try {
            XPathFactory xpf = XPathFactory.newInstance();
            XPath xpath = xpf.newXPath();

            // String numer = xpath.evaluate(
            // "/ArrayOfExchangeRatesTable/ExchangeRatesTable/No", doc);

            String nazwaTabeli = xpath.evaluate("//Table", doc);
            String numerTabeli = xpath.evaluate("//No", doc);
            LocalDate data = LocalDate.parse(xpath.evaluate("//EffectiveDate", doc));

            TabelaWalut tabela = new TabelaWalut(nazwaTabeli, numerTabeli, data);

            NodeList rates = (NodeList) xpath.evaluate("//Rate", doc, XPathConstants.NODESET);
            final int n = rates.getLength();
            for (int i = 0; i < n; i++) {
                Node rate = rates.item(i);
                String kod = xpath.evaluate("Code", rate);
                String nazwa = xpath.evaluate("Currency", rate);
                BigDecimal kurs = new BigDecimal(xpath.evaluate("Mid", rate));
                Waluta waluta = new Waluta(kod, nazwa, kurs);
                tabela.dodajWalute(waluta);
            }

            return tabela;
        } catch (XPathExpressionException e) {
            e.printStackTrace();
            return null;
        }
    }
}
