package com.fitbank.pdfmerger;

import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import javax.swing.ListModel;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import org.apache.pdfbox.exceptions.COSVisitorException;
import org.apache.pdfbox.pdmodel.PDDocument;

/**
 * Modelo de las páginas
 *
 * @author FitBank CI
 */
@Slf4j
public class PagesListModel implements ListModel {

    private final List<ListDataListener> listeners =
            new LinkedList<ListDataListener>();

    private final List<PageSource> pages = new LinkedList<PageSource>();

    public void newPDF() {
        pages.clear();
        allChanged();
    }

    public void loadPDF(File file) {
        pages.clear();
        pages.addAll(PdfPageSource.getPageSources(file));
        allChanged();
    }

    public void savePDF(OutputStream out) throws IOException {
        PDDocument doc = new PDDocument();

        for (PageSource ps : pages) {
            try {
                ps.write(doc);
            } catch (Exception e) {
                log.error("", e);
            }
        }

        try {
            doc.save(out);
        } catch (COSVisitorException e) {
            throw new IOException(e);
        }
        doc.close();
    }

    public void savePDF(File file) throws IOException {
        File tmpFile = File.createTempFile("fitbank-", ".pdf");
        FileOutputStream fos = new FileOutputStream(tmpFile);

        savePDF(fos);

        fos.close();

        tmpFile.renameTo(file);
    }

    public void saveJPG(OutputStream out) throws Exception {
        pages.get(0).writeJPG(out);
    }

    public void add(int index, PageSource pageSource) {
        addAll(index, Arrays.asList(pageSource));
    }

    public void addAll(int index, List<? extends PageSource> pageSources) {
        index = Math.max(index, 0);
        index = Math.min(index, pages.size());

        pages.addAll(index, pageSources);

        for (ListDataListener l : listeners) {
            l.intervalAdded(new ListDataEvent(this,
                    ListDataEvent.INTERVAL_ADDED, index, index + pageSources.
                    size()));
        }
    }

    public void removeAll(int[] selectedIndices) {
        Collection<PageSource> removeItems = new ArrayList<PageSource>(selectedIndices.length);
        for (int i : selectedIndices) {
            removeItems.add((PageSource) getElementAt(i));
        }
        pages.removeAll(removeItems);

        allChanged();
    }

    public int[] moveUp(int[] selectedIndices) {
        Arrays.sort(selectedIndices);

        int a = 0;
        for (int i : selectedIndices) {
            if (i <= 0) {
                break;
            }

            PageSource page = pages.get(i);
            pages.remove(i);
            pages.add(i - 1, page);
            selectedIndices[a] = selectedIndices[a] - 1;
            a++;
        }

        allChanged();

        return selectedIndices;
    }

    public int[] moveDown(int[] selectedIndices) {
        Arrays.sort(selectedIndices);

        List<Integer> reverse = new ArrayList<Integer>(selectedIndices.length);
        for (int i : selectedIndices) {
            reverse.add(0, i);
        }

        int a = 0;
        for (int i : reverse) {
            if (i + 1 >= pages.size()) {
                break;
            }

            PageSource page = pages.get(i);
            pages.remove(i);
            pages.add(i + 1, page);
            selectedIndices[a] = selectedIndices[a] + 1;
            a++;
        }

        allChanged();

        return selectedIndices;
    }

    private void allChanged() {
        for (ListDataListener l : listeners) {
            l.contentsChanged(new ListDataEvent(this,
                    ListDataEvent.CONTENTS_CHANGED, 0, Integer.MAX_VALUE));
        }
    }

    public int getSize() {
        return pages.size();
    }

    public Object getElementAt(int index) {
        return pages.get(index);
    }

    public void addListDataListener(ListDataListener l) {
        listeners.add(l);
    }

    public void removeListDataListener(ListDataListener l) {
        listeners.remove(l);
    }

}
