package be.msyben.vinylManager.tools.importexport;

import be.msyben.vinylManager.entities.Album;
import be.msyben.vinylManager.entities.Collection;
import be.msyben.vinylManager.entities.Disque;
import be.msyben.vinylManager.entities.Titre;
import be.msyben.vinylManager.exceptions.VinylManagerException;
import be.msyben.vinylManager.properties.Canaux;
import be.msyben.vinylManager.properties.Preferences;
import be.msyben.vinylManager.properties.Support;
import be.msyben.vinylManager.properties.Taille;
import be.msyben.vinylManager.properties.Vitesse;
import com.opencsv.CSVWriterBuilder;
import com.opencsv.ICSVWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

/**
 * Export au format Excel ou CSV
 * @author Mathieu Syben
 */
public final class TableFormatExport {
    
    private TableFormatExport() {}
    
    /**
     * Exporte la collection dans un fichier tabulaire au format définit
     * @param f Le fichier de sortie
     * @param c La collection
     * @param fields Les champs à exporter
     * @param csv True si export CSV, Excel sinon
     * @throws VinylManagerException En cas d'erreur
     */
    public static void exportTable(File f, Collection c, List<TableFormatExportField> fields, boolean csv) throws VinylManagerException {
        List<List<String>> datas = getDatas(c, fields);
        if (csv) {
            writeCsv(f, fields, datas);
        } else {
            writeXlsx(f, fields, datas);
        }
    }
    
    private static void writeXlsx(File f, List<TableFormatExportField> fields, List<List<String>> datas) throws VinylManagerException {
        try (XSSFWorkbook wb = new XSSFWorkbook();
                OutputStream os = new FileOutputStream(f)) {
            Sheet s = wb.createSheet("Export");
            String[] header = getHeaders(fields);
            
            XSSFCellStyle headerStyle = wb.createCellStyle();
            headerStyle.setFillForegroundColor(getColor("#CBB5FF"));
            headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
            
            for (int i = 0; i < header.length; i++) {
                writeCell(s,0,i,header[i]);
                getCell(s, 0, i).setCellStyle(headerStyle);
            }
            
            for (int i = 0; i < datas.size(); i++) {
                for (int j = 0; j < datas.get(i).size(); j++) {
                    writeCell(s, i+1, j, datas.get(i).get(j));
                }
            }
            
            for (int i = 0; i <= header.length; i++) {
                s.autoSizeColumn(i);
            }
            
            wb.write(os);
        } catch (IOException e) {
            throw new VinylManagerException(e);
        }
    }
    
    private static XSSFColor getColor(String s) {		
            return new XSSFColor(java.awt.Color.decode(s), null);
    }
    private static void writeCell(Sheet sheet, int row, int col, String value) { 
        if (value == null) {
            return;
        }
        Cell c = getCell(sheet, row, col);
        c.setCellValue(value);
    }
    private static Cell getCell(Sheet sheet, int row, int col) {
        Row r = sheet.getRow(row);
        if (r == null) {
            //log.info("Création row " + row);
            r = sheet.createRow(row);
        }
        Cell c = r.getCell(col);
        if (c == null) {
            //log.info("Création col " + col);
            c = r.createCell(col);
        }
        return c;
    }
    
    private static void writeCsv(File f, List<TableFormatExportField> fields, List<List<String>> datas) throws VinylManagerException {
        try (ICSVWriter writer = new CSVWriterBuilder(new FileWriter(f))
            .withSeparator(';')
            .build()) {
            
            writer.writeNext(getHeaders(fields));
            for (List<String> line : datas) {
                writer.writeNext(line.toArray(String[]::new));
            }
        } catch (IOException e) {
            throw new VinylManagerException(e);
        }
    }
    
    private static String[] getHeaders(List<TableFormatExportField> fields) {
        String[] header = new String[fields.size()];
        for (int i = 0; i < fields.size(); i++) {
            header[i] = fields.get(i).toString();
        }
        return header;
    }
    
    private static List<List<String>> getDatas(Collection c, List<TableFormatExportField> fields) {
        boolean hasDisque = hasDisque(fields);
        boolean hasPiste = hasTitre(fields);
        
        List<List<String>> ret = new ArrayList<>();
        for (Album a : c.getAlbums()) {
            if (hasDisque || hasPiste) {
                int disqueNum = 0;
                for (Disque d : a.getDisques()) {
                    disqueNum++;
                    if (hasPiste) {
                        for (Titre t : d.getFaceA()) {
                            List<String> current = new ArrayList<>();
                            for (TableFormatExportField field : fields) {
                                current.add(getValue(field, a, d, disqueNum, true, t));
                            }
                            ret.add(current);
                        }
                        for (Titre t : d.getFaceB()) {
                            List<String> current = new ArrayList<>();
                            for (TableFormatExportField field : fields) {
                                current.add(getValue(field, a, d, disqueNum, false, t));
                            }
                            ret.add(current);
                        }
                    } else {
                        List<String> current = new ArrayList<>();
                        for (TableFormatExportField field : fields) {
                            current.add(getValue(field, a, d, disqueNum, true, null));
                        }
                        ret.add(current);
                    }
                }
            } else {
                List<String> current = new ArrayList<>();
                for (TableFormatExportField field : fields) {
                    current.add(getValue(field, a, null, 0, true, null));
                }
                ret.add(current);
            }
        }
        
        return ret;
    }
    
    private static String getValue(TableFormatExportField field, Album a, Disque d, int disqueNum, boolean faceA, Titre t) {
        
        switch(field) {
            
        
            case artiste:
            return a.getArtiste();
            case titre:
            return a.getTitre();
        case compositeur:
            return a.getCompositeur();
        case reference:   
            return a.getReference();
        case editeur: 
            return a.getEditeur();
        case annee:  
            return a.getAnnee();
        case anneeParution: 
            return a.getAnneeParution();
        case pays: 
            return a.getPays();
        case chef:  
            return a.getChef();
        case orchestre:
            return a.getOrchestre();
        case musiciens:  
            return a.getMusiciens();
        case etatAlbum: 
            return a.getEtat();
        case commentaire: 
            return a.getCommentaire();
        case etoiles: 
            return a.getCote()+"" + (a.getCoteDemi()?".5":"");
       case genre: 
            return a.getGenre();
        case valeur:  
            return a.getValeur();
        case support:
            return Support.getSupport(a.getSupport()).getName();
        case parolier:  
            return a.getParolier();
        case location:
            return a.getLocation();
        case possede:  
            return a.getPossede()?"X":"";
        case dateAchat: 
            if (a.getDateAchat() == null) {
                return "";
            }
            DateFormat df = new SimpleDateFormat(Preferences.getInstance().get(Preferences.DATE_FORMAT, "dd/MM/yyyy"));
            return df.format(a.getDateAchat());
        case compilation: 
            return a.getCompliation()?"X":"";
        case numDisque: 
            return disqueNum+"";
        case etatDisque:
            return d.getEtat();
        case taille:  
            return Taille.getTaille(d.getTaille()).getName();
        case vitesse: 
            return Vitesse.getVitesse(d.getVitesse()).getName();
        case couleur: 
            return d.getCouleur();
        case canaux:
            return Canaux.getCanaux(d.getCanaux()).getName();
        case face:
            return faceA?"A":"B";
        case numPiste:
            return t.getNum()+"";
        case piste:   
            return t.getNom();
        case duree: 
            return t.getDuree()+"";
        case pisteArtiste: 
            return t.getArtiste();
        case pisteAnnee: 
            return t.getAnnee();
        default:
            return "/";
        }        
    }
    
    
    private static boolean hasDisque(List<TableFormatExportField> fields) {
        for (TableFormatExportField f : fields) {
            if (f.isHasDisque()) {
                return true;
            }
        }
        return false;
    }
    
    
    
    
    private static boolean hasTitre(List<TableFormatExportField> fields) {
        for (TableFormatExportField f : fields) {
            if (f.isHasTitre()) {
                return true;
            }
        }
        return false;
    }
    
}
