function getCurrentTab() {
    console.debug("Querying active tab.");
    var queryInfo = {
      active: true,
      currentWindow: true
    };

    return browser.tabs.query(queryInfo)
    .then(function(tabs) {
        if (tabs.length == 1){
            let url = tabs[0].url;
            console.info(`Active URL: ${url}`);
            return url;
        } else {
            console.error(`Expected 1 active tab, received: ${tabs}`);
            throw 'Failed to get active tab.';
        }
    }, onError);
}

function extractBookId(url){
    console.debug(`Extracting book id from ${url}`);
    // match a url like:
    // https://www.safaribooksonline.com/library/view/startup-opportunities-2nd/9781119378181/
    // https://www.safaribooksonline.com/library/view/startup-growth-engines/77961SEM00001/
    let match = url.match(/\/library\/view\/[^\/]+\/(\w+)\//);
    let bookId = match && match[1];

    if (bookId)  {
        console.debug(`Extracted book id: ${bookId}`);
        return bookId;
    }else{
        console.error('Could not extract book id from url, only '
                    +'domain "www.safaribooksonline.com“ is supported.');
        throw 'Failed to extract book id.';
    }
}

class Book{

    constructor(book_id, epub) {
        this.book_id = book_id
        this.raw_book = {}
        this.chapter_list = []
        this.chapter_info = {}
        this.book_files = {}
        this.book_info = null
        this.book_toc = null
        this.book_flattoc = null
    }

    downloadResource(url){
        console.info(`Downloading ${url}`)
        return fetch(url, {
            credentials: 'include'
        }).then((res) => {
            // console.log(`Downloaded.`)
            return res;
        }, onError)
    }

    downloadJson(url){
        return this.downloadResource(url)
            .then((res) => {
                return res.json();
            }, onError);
    }

    downloadBookInfo(){
        console.info(`Downloading book info for ${this.book_id}`);
        let url = `https://www.safaribooksonline.com/api/v1/book/${this.book_id}/`;
        return this.downloadJson(url)
            .then((book_info) => {
                this.book_info = book_info;
            }, onError);
    }

    downloadChapterList(){
        function helper(book, url){
            console.info(`Downloading chapter list ${url}`);
            return book.downloadJson(url)
                .then((chapter_list) => {
                    book.chapter_list
                        = book.chapter_list.concat(chapter_list.results);

                    if (chapter_list.next != null){
                        return helper(book, chapter_list.next);
                    }
                }, onError);
        }

        return helper(this, this.book_info.chapter_list)
            .then(() => {
                console.info(`Chapter List Downloaded.`);
            }, onError);
    }

    downloadMetaContent(){
        let downloads = []

        this.insertBookFile(
            this.book_info.cover,
            "cover.img")

        downloads.push(
            this.downloadJson(this.book_info.toc)
            .then((json) => {
                this.book_toc=json
            }))
        downloads.push(
            this.downloadJson(this.book_info.flat_toc)
            .then((json) => {
                this.book_flattoc=json
            }))
        downloads.push(
            Promise.map(this.book_info.chapters, (chapter) => {
                renderProgress(`${chapter}`)
                return this.downloadJson(chapter)
                        .then((json) => {
                            this.chapter_info[chapter] = json
                            return this.extractChapterAssets(json)
                        })
            },{concurrency: 10}))

        return Promise.all(downloads)
    }


    extractChapterAssets(json){
        if (!json.asset_base_url || !json.content){
            throw "Missing data."
        }
        // Html
        this.insertBookFile(
            json.content,
            json.full_path)
        // List of images.
        for (let idx in json.images){
            this.insertBookFile(
                json.asset_base_url + json.images[idx],
                json.images[idx])
        }
        // List of stylesheets.
        for (let idx in json.stylesheets){
            this.insertBookFile(
                json.stylesheets[idx].original_url,
                json.stylesheets[idx].full_path)
        }
    }

    downloadContent(){
        return Promise.map(Object.keys(this.book_files), (url) => {
            renderProgress(`${this.book_files[url].filename}`)
            return this.downloadResource(url)
                    .then((res) => {
                        if (res.ok){
                            this.book_files[url].headers = res.headers
                            this.book_files[url].mime = res.headers.get('Content-Type')
                            this.book_files[url].body = res.blob()
                        }
            })
        },{concurrency: 10})
    }

    insertBookFile(url, filename){
        this.book_files[url] = {
            url: url,
            filename: filename,
            headers: null,
            mime: null,
            body: null
        }
    }
}

class SidebarPage{

    constructor() {
        $('#loading').show();
        $('#error-message').hide();
        $('#book-info').hide();
        $("#book-file-list").empty();
    }

    renderInfo(book){
        $("#book-name").text(book.book_info.title);
        $("#book-cover").attr("src", book.book_info.cover);
        $('#book-info').show();
    }

    renderDone(){
        $('#loading').hide();
    }

    renderChapterList(book){
        // Add chapters to UI
        for (let chapter_idx in book.chapter_list) {
            let chapter = book.chapter_list[chapter_idx];
            var chapter_dom = $("<li></li>")
                        .addClass("list-group-item")
                        .html(chapter.title)
                        .attr("chapterIndex", chapter_idx);
            $("#book-chapter-list").append(chapter_dom);
        }
        $('#loading').hide();
    }
}

function onError(error) {
    console.error(`Error: ${error}`);
    $('#error-message').text(`Error: ${error}`);
    $('#error-message').show();
    $('#loading').hide();
    $('#book-info').hide();
}

function renderProgress(txt){
    // Add chapters to UI
    var progress_dom = $("<li></li>")
                .addClass("list-group-item")
                .html(txt)
    $("#book-file-list").prepend(progress_dom);
}



function createEpub(book, epub){
    epub.addFile("book.json", JSON.stringify(book, null, '\t'))
    for (let url in book.book_files){
        file = book.book_files[url]
        epub.addFile(file.filename, file.body)
    }
}

function onDownloadBookClicked(){
    console.info("Begin book download.");
    page = new SidebarPage()

    getCurrentTab()
    .then(extractBookId, onError)
    .then((book_id) => {
        epub = new EpubWriter();
        book = new Book(book_id);
        book.downloadBookInfo()
        .then(() => { page.renderInfo(book); })
        // .then(() => { return book.downloadChapterList(); })
        // .then(() => { return page.renderChapterList(book); })
        .then(() => { return book.downloadMetaContent(); })
        .then(() => { return book.downloadContent(); })
        .then(() => { return createEpub(book, epub); })
        .then(() => { return epub.generateAsync(); })
        .then((file) => {
            let title = book.book_info.title
            let filename = "books/"
                + title.replace(/[^a-z0-9]/gi, '_').toLowerCase()
                + ".zip"
            console.log(`Zip file name ${filename}`)
            renderProgress(`Saved to ${filename}`)
            let url = window.URL.createObjectURL(file)
            return browser.downloads.download({ "filename" : filename, url : url})
        })
        .then(() =>{
            page.renderDone();
        })

    }, onError);
}

document.addEventListener('DOMContentLoaded', function() {
    console.log("Start safari book hunter.");
    $('#loading').hide();
    $('#error-message').hide();
    $('#book-info').hide();

    $('#download-book-button').click(() => {
        onDownloadBookClicked();
    });



    $('#deselect-all-button').show()
    $('#download-button').show()
    $('#download-section').hide();
    // let bookInfo = new BookInfo();
})
