package com.fitbank.arreglos;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.swing.JOptionPane;
import javax.swing.UIManager;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.Transformer;
import org.apache.commons.csv.CSVUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

import com.FitBank.xml.Formas.Formulario;
import com.FitBank.xml.Parser.ParserFormulario;

import com.fitbank.serializador.xml.SerializadorXml;
import com.fitbank.util.Debug;
import com.fitbank.util.ListaArchivos;
import com.fitbank.util.PilaSincronizada;
import com.fitbank.util.Servicios;
import com.fitbank.util.SwingUtils;
import com.fitbank.util.ThreadPrintStream;
import com.fitbank.web.providers.HardDiskWebPageProvider;
import com.fitbank.webpages.WebPage;
import com.fitbank.webpages.WebPageXml;
import com.fitbank.webpages.util.ValidationMessage;
import com.fitbank.webpages.util.ValidationUtils;
import com.fitbank.webpages.util.Validator;
import com.fitbank.webpages.util.validators.SpellValidator;
import javax.swing.JFileChooser;

/**
 *
 * @author FitBank CI
 */
public class Main extends javax.swing.JFrame {

    private static final boolean GUARDAR_SIEMPRE = false;

    private static final int NUMERO_THREADS = 1;

    private static PilaSincronizada<File> archivos;

    private static int total = 0;

    private static int done = 0;

    private Map<String, Collection<ValidationMessage>> messages =
            Collections.synchronizedMap(
            new HashMap<String, Collection<ValidationMessage>>());

    public Main() {
        initComponents();
        setLocationRelativeTo(null);
        esconderPanelFrases();
        SwingUtils.load(this.getClass(), generateHTMLReport, fix, savePhrases,
                readPhrases, targetPhrases, replacementPhrases);
    }

    protected void addMessageThrowable(Validator validator, WebPage webPage,
            final Throwable t) {
        ValidationMessage message = new ValidationMessage(validator,
                "ERROR", webPage, webPage, ValidationMessage.Severity.ERROR) {

            @Override
            public String toString() {
                StringWriter sw = new StringWriter();
                t.printStackTrace(new PrintWriter(sw));
                return sw.toString();
            }

        };
        messages.get(webPage.getURI()).add(message);
    }

    private <T> Collection<T> get(final Class<? extends T> clase) {
        List<Object> list = Arrays.asList(arreglos.getSelectedValues());

        return (Collection<T>) CollectionUtils.select(CollectionUtils.collect(
                list, new Transformer() {

            public Object transform(Object input) {
                return ((ArreglosListModel.Item) input).getValue();
            }

        }), new Predicate() {

            public boolean evaluate(Object object) {
                return clase.isInstance(object);
            }

        });
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        javax.swing.JButton arreglar = new javax.swing.JButton();
        progress = new javax.swing.JProgressBar();
        javax.swing.JPanel jPanel1 = new javax.swing.JPanel();
        javax.swing.JScrollPane arreglosScrollPane = new javax.swing.JScrollPane();
        arreglos = new javax.swing.JList();
        generateHTMLReport = new javax.swing.JCheckBox();
        fix = new javax.swing.JCheckBox();
        panelFrases = new javax.swing.JPanel();
        savePhrases = new javax.swing.JCheckBox();
        readPhrases = new javax.swing.JCheckBox();
        targetPhrases = new javax.swing.JTextField();
        replacementPhrases = new javax.swing.JTextField();
        javax.swing.JButton targetBrowse = new javax.swing.JButton();
        javax.swing.JButton replacementBrowse = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setName("Form"); // NOI18N
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                formWindowClosing();
            }
        });

        arreglar.setText("Seleccionar archivos y arreglar");
        arreglar.setName("arreglar"); // NOI18N
        arreglar.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                arreglarActionPerformed();
            }
        });

        progress.setName("progress"); // NOI18N

        jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1), "Arreglos:", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 1, 12), UIManager.getDefaults().getColor("Label.foreground"))); // NOI18N
        jPanel1.setName("jPanel1"); // NOI18N

        arreglosScrollPane.setName("arreglosScrollPane"); // NOI18N

        arreglos.setModel(new ArreglosListModel());
        arreglos.setName("arreglos"); // NOI18N
        arreglos.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
            public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
                arreglosValueChanged();
            }
        });
        arreglosScrollPane.setViewportView(arreglos);

        generateHTMLReport.setText("Generar reporte HTML");
        generateHTMLReport.setName("generateHTMLReport"); // NOI18N

        fix.setText("Aplicar arreglos");
        fix.setName("fix"); // NOI18N

        panelFrases.setName("panelFrases"); // NOI18N

        savePhrases.setText("Guardar frases:");
        savePhrases.setName("savePhrases"); // NOI18N

        readPhrases.setText("Aplicar arreglos de frases:");
        readPhrases.setName("readPhrases"); // NOI18N

        targetPhrases.setName("targetPhrases"); // NOI18N

        replacementPhrases.setName("replacementPhrases"); // NOI18N

        targetBrowse.setText("Examinar...");
        targetBrowse.setName("targetBrowse"); // NOI18N
        targetBrowse.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                targetBrowseActionPerformed();
            }
        });

        replacementBrowse.setText("Examinar...");
        replacementBrowse.setName("replacementBrowse"); // NOI18N
        replacementBrowse.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                replacementBrowseActionPerformed();
            }
        });

        javax.swing.GroupLayout panelFrasesLayout = new javax.swing.GroupLayout(panelFrases);
        panelFrases.setLayout(panelFrasesLayout);
        panelFrasesLayout.setHorizontalGroup(
            panelFrasesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelFrasesLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(panelFrasesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(readPhrases)
                    .addComponent(savePhrases))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(panelFrasesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(targetPhrases, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 124, Short.MAX_VALUE)
                    .addComponent(replacementPhrases, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 124, Short.MAX_VALUE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(panelFrasesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(replacementBrowse, javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(targetBrowse, javax.swing.GroupLayout.Alignment.TRAILING))
                .addContainerGap())
        );
        panelFrasesLayout.setVerticalGroup(
            panelFrasesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(panelFrasesLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(panelFrasesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(savePhrases)
                    .addComponent(targetPhrases, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(targetBrowse))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(panelFrasesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(readPhrases)
                    .addComponent(replacementPhrases, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(replacementBrowse))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(panelFrases, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(generateHTMLReport)
                .addContainerGap(256, Short.MAX_VALUE))
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(arreglosScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 422, Short.MAX_VALUE))
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(fix)
                .addContainerGap(299, Short.MAX_VALUE))
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
                .addComponent(arreglosScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 259, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(generateHTMLReport)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(fix)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(panelFrases, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(arreglar, javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(progress, javax.swing.GroupLayout.DEFAULT_SIZE, 444, Short.MAX_VALUE))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(arreglar)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(progress, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void arreglarActionPerformed() {//GEN-FIRST:event_arreglarActionPerformed
        formWindowClosing();

        new Thread() {

            @Override
            public void run() {
                arreglar();
            }

        }.start();
    }//GEN-LAST:event_arreglarActionPerformed

    private void formWindowClosing() {//GEN-FIRST:event_formWindowClosing
        SwingUtils.save(this.getClass(), generateHTMLReport, fix, savePhrases,
                readPhrases, targetPhrases, replacementPhrases);
    }//GEN-LAST:event_formWindowClosing

    private void targetBrowseActionPerformed() {//GEN-FIRST:event_targetBrowseActionPerformed
        JFileChooser jfc = new JFileChooser();
        jfc.setFileSelectionMode(JFileChooser.FILES_ONLY);
        jfc.setDialogTitle("Archivo de frases");

        if (jfc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            targetPhrases.setText(jfc.getSelectedFile().getAbsolutePath());
        }
    }//GEN-LAST:event_targetBrowseActionPerformed

    private void replacementBrowseActionPerformed() {//GEN-FIRST:event_replacementBrowseActionPerformed
        JFileChooser jfc = new JFileChooser();
        jfc.setFileSelectionMode(JFileChooser.FILES_ONLY);
        jfc.setDialogTitle("Archivo de frases");

        if (jfc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            replacementPhrases.setText(jfc.getSelectedFile().getAbsolutePath());
        }
    }//GEN-LAST:event_replacementBrowseActionPerformed

    private void arreglosValueChanged() {//GEN-FIRST:event_arreglosValueChanged
        Object[] arreglosSeleccionados = arreglos.getSelectedValues();
        
        for (int i = 0; i < arreglosSeleccionados.length; i++) {
            String nombreArreglo = arreglosSeleccionados[i].toString();
            
            if (nombreArreglo.contains("SpellValidator")) {
                panelFrases.setVisible(true);
                return;
            }
        }
        
        esconderPanelFrases();
    }//GEN-LAST:event_arreglosValueChanged

    private void esconderPanelFrases() {
        savePhrases.setSelected(false);
        readPhrases.setSelected(false);
        panelFrases.setVisible(false);
    }
    
    private void arreglar() {
        if (arreglos.getSelectedIndices().length == 0) {
            JOptionPane.showMessageDialog(this,
                    "Por favor seleccione por lo menos un arreglo antes de continuar.");
            return;
        }

        List<Thread> threads = new LinkedList<Thread>();

        Collection<File> lista = ListaArchivos.getLista(Main.class, ".xml",
                ".wpx");

        if (lista == null) {
            System.exit(0);
        }

        initPhrases();

        total = lista.size();
        done = 0;
        archivos = new PilaSincronizada<File>(lista);

        ThreadPrintStream.init();

        for (int a = 0; a < NUMERO_THREADS; a++) {
            Thread thread = new Thread() {

                @Override
                public void run() {
                    File proximo = null;

                    while ((proximo = archivos.pop()) != null) {
                        arreglar(proximo);
                        done++;
                        updateProgress();

                    }
                }

            };
            threads.add(thread);
            thread.start();
        }

        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                Debug.error(e);
            }
        }

        ThreadPrintStream.deinit();

        System.out.println("Listo");

        String report = ValidationUtils.generateReport(messages,
                fix.isSelected());

        if (generateHTMLReport.isSelected()) {
            try {
                File file = File.createTempFile("arreglos", ".html");
                IOUtils.write(report, new FileOutputStream(file), "UTF-8");
                Servicios.abrirNavegador("file://" + file.getAbsolutePath());
            } catch (IOException e) {
                Debug.error(e);
            }
        }

        if (savePhrases.isSelected()) {
            try {
                File file = new File(targetPhrases.getText());
                IOUtils.write(generatePhrasesCSV(), new FileOutputStream(file),
                        "UTF-8");
            } catch (IOException e) {
                Debug.error(e);
            }
        }
        int res = JOptionPane.showConfirmDialog(this,
                "Arreglos terminados, desea salir?");

        if (res == JOptionPane.OK_OPTION) {
            System.exit(0);
        } else {
            progress.setValue(0);
        }
    }

    public static String generatePhrasesCSV() {
        StringBuilder sb = new StringBuilder();

        for (String texto : SpellValidator.getAllPhrases()) {
            sb.append(CSVUtils.printLine(new String[] { String.valueOf(texto.
                        hashCode()), texto })).append('\n');
        }

        return sb.toString();
    }

    private void initPhrases() {
        if (readPhrases.isSelected()) {
            try {
                String[][] parse = CSVUtils.parse(IOUtils.toString(
                        new FileInputStream(replacementPhrases.getText()),
                        "UTF-8"));

                for (String[] s : parse) {
                    SpellValidator.addReplacement(Integer.parseInt(s[0]), s[1]);
                }
            } catch (IOException e) {
                Debug.error(e);
            }
        }
    }

    private void updateProgress() {
        progress.setValue(100 - 100 * (total - done) / total);
    }

    private void arreglar(File file) {
        try {
            System.out.println("Arreglando " + file);

            if (file.getName().endsWith(".xml")) {
                Formulario formulario = ParserFormulario.parse(
                        file.getAbsolutePath());

                if (arreglar(formulario)) {
                    new FileOutputStream(file).write(formulario.toXml().getBytes(
                            "ISO-8859-1"));
                }
            } else {
                WebPage webPage = WebPageXml.parse(file.getAbsolutePath());

                if (arreglar(webPage)) {
                    new SerializadorXml().serializar(webPage,
                            new FileOutputStream(file));
                }

            }

        } catch (Exception t) {
            addMessageThrowable(null, null, t);
        }
    }

    public boolean arreglar(Formulario formulario) {
        boolean arreglado = GUARDAR_SIEMPRE;

        for (ArregloFormulario arregloFormulario : get(ArregloFormulario.class)) {
            arreglado |= arregloFormulario.arreglar(formulario);
        }

        return arreglado;
    }

    public boolean arreglar(WebPage webPage) {
        boolean arreglado = GUARDAR_SIEMPRE;
        messages.put(webPage.getURI(), new LinkedList<ValidationMessage>());

        for (Validator validator : get(Validator.class)) {
            try {
                Collection<ValidationMessage> msgs = validator.validate(webPage,
                        ValidationUtils.getFullWebPage(webPage, 
                        new HardDiskWebPageProvider()));
                messages.get(webPage.getURI()).addAll(msgs);
                for (ValidationMessage message : msgs) {
                    if (message.isFixable() && fix.isSelected()) {
                        message.fix();
                        arreglado = true;
                    }
                }
            } catch (Exception t) {
                addMessageThrowable(validator, webPage, t);
            }
        }

        final String output = System.out.toString();

        if (StringUtils.isNotBlank(output)) {
            ValidationMessage message = new ValidationMessage(null, "",
                    webPage, webPage, ValidationMessage.Severity.ERROR) {

                @Override
                public String toString() {
                    return output;
                }

            };
            messages.get(webPage.getURI()).add(message);
        }

        return arreglado;
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) throws Exception {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                new Main().setVisible(true);
            }

        });
    }

    private javax.swing.JList arreglos;
    private javax.swing.JCheckBox fix;
    private javax.swing.JCheckBox generateHTMLReport;
    private javax.swing.JPanel panelFrases;
    private javax.swing.JProgressBar progress;
    private javax.swing.JCheckBox readPhrases;
    private javax.swing.JTextField replacementPhrases;
    private javax.swing.JCheckBox savePhrases;
    private javax.swing.JTextField targetPhrases;
    // End of variables declaration//GEN-END:variables

}
