package com.example.staxread;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

public class BooksReader {

    public List<Book> readFromXML(InputStream is) throws XMLStreamException {
        XMLInputFactory inputFactory = XMLInputFactory.newInstance();
        XMLStreamReader reader = null;
        try {
            reader = inputFactory.createXMLStreamReader(is);
            return readDocument(reader);
        } finally {
            if (reader != null) {
                reader.close();
            }
        }
    }

    private List<Book> readDocument(XMLStreamReader reader) throws XMLStreamException {
        while (reader.hasNext()) {
            int eventType = reader.next();
            switch (eventType) {
                case XMLStreamReader.START_ELEMENT:
                    String elementName = reader.getLocalName();
                    if (elementName.equals("books"))
                        return readBooks(reader);
                    break;
                case XMLStreamReader.END_ELEMENT:
                    break;
            }
        }
        throw new XMLStreamException("Premature end of file");
    }

    private List<Book> readBooks(XMLStreamReader reader) throws XMLStreamException {
        List<Book> books = new ArrayList<>();
        
        while (reader.hasNext()) {
            int eventType = reader.next();
            switch (eventType) {
                case XMLStreamReader.START_ELEMENT:
                    String elementName = reader.getLocalName();
                    if (elementName.equals("book"))
                        books.add(readBook(reader));
                    break;
                case XMLStreamReader.END_ELEMENT:
                    return books;
            }
        }
        throw new XMLStreamException("Premature end of file");
    }

    private Book readBook(XMLStreamReader reader) throws XMLStreamException {
        Book book = new Book("", "", null, "", 0);
        book.setLanguage(reader.getAttributeValue(null, "language"));
        
        while (reader.hasNext()) {
            int eventType = reader.next();
            switch (eventType) {
                case XMLStreamReader.START_ELEMENT:
                    String elementName = reader.getLocalName();
                    if (elementName.equals("authors"))
                        book.setAuthors(readAuthors(reader));
                    else if (elementName.equals("title"))
                        book.setTitle(readCharacters(reader));
                    else if (elementName.equals("category"))
                        book.setCategory(readCategory(reader));
                    else if (elementName.equals("year"))
                        book.setYear(readInt(reader));
                    break;
                case XMLStreamReader.END_ELEMENT:
                    return book;
            }
        }
        throw new XMLStreamException("Premature end of file");
    }

    private List<String> readAuthors(XMLStreamReader reader) throws XMLStreamException {
        List<String> authors = new ArrayList<>();
        while (reader.hasNext()) {
            int eventType = reader.next();
            switch (eventType) {
                case XMLStreamReader.START_ELEMENT:
                    String elementName = reader.getLocalName();
                    if (elementName.equals("author"))
                        authors.add(readCharacters(reader));
                    break;
                case XMLStreamReader.END_ELEMENT:
                    return authors;
            }
        }
        throw new XMLStreamException("Premature end of file");

    }
    
    private String readCharacters(XMLStreamReader reader) throws XMLStreamException {
        StringBuilder result = new StringBuilder();
        while (reader.hasNext()) {
            int eventType = reader.next();
            switch (eventType) {
                case XMLStreamReader.CHARACTERS:
                case XMLStreamReader.CDATA:
                    result.append(reader.getText());
                    break;
                case XMLStreamReader.END_ELEMENT:
                    return result.toString();
            }
        }
        throw new XMLStreamException("Premature end of file");
    }
    
    private Category readCategory(XMLStreamReader reader) throws XMLStreamException {
        String characters = readCharacters(reader);
        try {
            return Category.valueOf(characters);
        } catch(IllegalArgumentException e) {
            throw new XMLStreamException("Invalid category " + characters);
        }
    }
    
    private int readInt(XMLStreamReader reader) throws XMLStreamException {
        String characters = readCharacters(reader);
        try {
            return Integer.valueOf(characters);
        } catch (NumberFormatException e) {
            throw new XMLStreamException("Invalid integer " + characters);
        }
    }
}