/*
 * CODE LICENSE =====================
 * Copyright (c) 2015, Apocalypse Laboratories
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * 4. You adhere to the Media License detailed below.  If you do not, this license
 * is automatically revoked and you must purge all copies of the software you
 * possess, in source or binary form.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * MEDIA LICENSE ====================
 * All images and other graphical files (the "graphics") included with this
 * software are copyright (c) 2015 Apocalypse Laboratories.  You may not distribute
 * the graphics or any program, source code repository, or other digital storage
 * media containing them without written permission from Apocalypse Laboratories.
 * This ban on distribution only applies to publicly available systems.
 * A password-protected network file share, USB drive, or other storage scheme that
 * cannot be easily accessed by the public is generally allowed.  If in doubt,
 * contact Apocalypse Laboratories.  If Apocalypse Laboratories allows or denies
 * you permission, that decision is considered final and binding.
 */
package net.apocalypselabs.symat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Random;
import javax.swing.JOptionPane;
import org.etherpad_lite_client.EPLiteClient;
import org.etherpad_lite_client.EPLiteException;

/**
 *
 * @author Skylar
 */
public class Pads extends javax.swing.JInternalFrame {

    public static final String PADS_URL = "https://pad.symatapp.com";

    /**
     * Creates new form Pads
     */
    public Pads() {
        initComponents();
        padPane.setListData(getPads());
    }

    /**
     * Add the given pad ID to the local pad list.
     *
     * @param id the pad ID.
     */
    public static void addPad(String id) {
        String pads = PrefStorage.getSetting("pads");

        // Check for dupes
        for (String p : pads.split("\\|")) {
            if (p.equals(id)) {
                return;
            }
        }

        if (pads.equals("")) {
            pads = id;
        } else {
            pads += "|" + id;
        }
        PrefStorage.saveSetting("pads", pads);
    }

    /**
     * Delete the pad with the given ID from local memory.
     * <br />It will still exist online.
     *
     * @param id the pad ID.
     */
    public static void delPad(String id) {
        String pads = PrefStorage.getSetting("pads");
        String result = "";
        int i = 0;
        for (String pad : pads.split("\\|")) {
            if (!pad.equals(id)) {
                if (i > 0) {
                    result += "|";
                }
                result += pad;
                i++;
            }
        }

        PrefStorage.saveSetting("pads", result);
    }

    /**
     * Get an array of saved pads.
     *
     * @return String[] of pad IDs
     */
    public static String[] getPads() {
        String pads = PrefStorage.getSetting("pads");
        if (!pads.equals("")) {
            if (pads.contains("|")) {
                return pads.split("\\|");
            } else {
                String[] padlist = {pads};
                return padlist;
            }
        } else {
            String[] padlist = {};
            return padlist;
        }
    }

    /**
     * Create a new pad on the server with the given ID.
     *
     * @param id The pad ID to create. If blank is auto-generated.
     * @param content Text to initialize the pad with.
     * @return Created pad ID.
     * @throws Exception if things break.
     */
    public static String genPad(String id, String content) throws Exception {
        // Generate ID if blank
        if (id.equals("")) {
            id = genID();
        }

        // Create pad with given text.
        try {
            getClient().createPad(id, content);
        } catch (EPLiteException ex) {
            getClient().setText(id, content);
        }
        return id;
    }

    /**
     * Generate a random pad ID.
     *
     * @return the ID.
     */
    public static String genID() {
        int length = 15;
        String[] chars = ("0123456789"
                + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                + "abcdefghijklmnopqrstuvwxyz").split("");
        String out = "";
        Random rng = new Random();

        for (int i = 0; i < length; i++) {
            out += chars[rng.nextInt(chars.length)];
        }

        return out;
    }

    /**
     * Get a client for the pad API.
     *
     * @return the client.
     * @throws IOException if things break.
     */
    public static EPLiteClient getClient() throws Exception {
        // Load the API key from a file, so it's not included with Git.
        String apikey;
        BufferedReader reader = new BufferedReader(
                new InputStreamReader(
                        Pads.class
                        .getResourceAsStream("/padkey")));
        apikey = reader.readLine();

        // New client
        return new EPLiteClient(PADS_URL, apikey);
    }

    /**
     * Get the pad text.
     *
     * @param id the pad ID.
     * @return the text, or error message.
     */
    public static String getPad(String id) {
        String text = "";
        try {
            text = getClient().getText(id).getOrDefault("text", "").toString();
        } catch (Exception ex) {
            text = "Error: Could not get pad contents: " + ex.getMessage();
        }
        return text;
    }

    public static void setPad(String id, String content) {
        try {
            getClient().setText(id, content);
        } catch (Exception ex) {
            Debug.stacktrace(ex);
            JOptionPane.showInternalMessageDialog(
                    MainGUI.mainPane,
                    "Could not sync pad contents: " + ex.getMessage(),
                    "Error",
                    JOptionPane.ERROR_MESSAGE);
        }
    }

    /**
     * Delete the given pad ID.
     *
     * @param id the pad ID.
     * @param purge If true will delete from server also.
     */
    public static void delPad(String id, boolean purge) {
        delPad(id);
        if (purge) {
            try {
                getClient().deletePad(id);
            } catch (Exception ex) {
                // Meh.  No big deal.
            }
        }
    }

    public static void loadPad(String pad) {
        String theme = "";
        if (PrefStorage.getSetting("theme").equals("dark")) {
            theme = "?theme=terminal";
        }
        MainGUI.loadFrame(new WebBrowser("Pad " + pad,
                PADS_URL + "/p/" + pad + theme,
                WebBrowser.PAD_LOGO));
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jScrollPane1 = new javax.swing.JScrollPane();
        padPane = new javax.swing.JList();
        jLabel1 = new javax.swing.JLabel();
        openBtn = new javax.swing.JButton();
        delBtn = new javax.swing.JButton();
        previewBtn = new javax.swing.JButton();
        saveBtn = new javax.swing.JButton();
        jScrollPane2 = new javax.swing.JScrollPane();
        previewPane = new javax.swing.JTextArea();
        shareBtn = new javax.swing.JButton();
        jLabel2 = new javax.swing.JLabel();
        purgeBtn = new javax.swing.JButton();
        addBtn = new javax.swing.JButton();

        setClosable(true);
        setIconifiable(true);
        setMaximizable(true);
        setResizable(true);
        setTitle("Collaboration");
        setMinimumSize(new java.awt.Dimension(450, 280));
        setPreferredSize(new java.awt.Dimension(450, 280));

        padPane.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        padPane.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
            public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
                padPaneValueChanged(evt);
            }
        });
        jScrollPane1.setViewportView(padPane);

        jLabel1.setText("My Pads:");

        openBtn.setText("Open");
        openBtn.setToolTipText("Open pad for editing");
        openBtn.setEnabled(false);
        openBtn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                openBtnActionPerformed(evt);
            }
        });

        delBtn.setText("Delete");
        delBtn.setToolTipText("Remove pad from list");
        delBtn.setEnabled(false);
        delBtn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                delBtnActionPerformed(evt);
            }
        });

        previewBtn.setText("Preview");
        previewBtn.setToolTipText("Preview pad contents");
        previewBtn.setEnabled(false);
        previewBtn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                previewBtnActionPerformed(evt);
            }
        });

        saveBtn.setText("Save");
        saveBtn.setToolTipText("Save pad locally");
        saveBtn.setEnabled(false);
        saveBtn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                saveBtnActionPerformed(evt);
            }
        });

        previewPane.setEditable(false);
        previewPane.setColumns(1);
        previewPane.setRows(1);
        previewPane.setTabSize(4);
        jScrollPane2.setViewportView(previewPane);

        shareBtn.setText("Share");
        shareBtn.setToolTipText("Share pad");
        shareBtn.setEnabled(false);
        shareBtn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                shareBtnActionPerformed(evt);
            }
        });

        jLabel2.setText("Preview:");

        purgeBtn.setText("Purge");
        purgeBtn.setToolTipText("Delete pad completely");
        purgeBtn.setEnabled(false);
        purgeBtn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                purgeBtnActionPerformed(evt);
            }
        });

        addBtn.setText("+");
        addBtn.setToolTipText("Add pad by ID");
        addBtn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addBtnActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                    .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 132, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
                        .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 69, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addComponent(addBtn)))
                .addGap(6, 6, 6)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                    .addComponent(delBtn, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(saveBtn, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(openBtn, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(previewBtn, javax.swing.GroupLayout.DEFAULT_SIZE, 84, Short.MAX_VALUE)
                    .addComponent(shareBtn, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(purgeBtn, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPane2)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jLabel2)
                        .addGap(0, 144, Short.MAX_VALUE)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel1)
                    .addComponent(jLabel2)
                    .addComponent(addBtn))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPane2)
                    .addComponent(jScrollPane1)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(openBtn)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(saveBtn)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(delBtn)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(purgeBtn)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(previewBtn)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(shareBtn)))
                .addContainerGap())
        );

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

    private void delBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_delBtnActionPerformed
        int ans = JOptionPane.showInternalConfirmDialog(this,
                "Remove pad from list?  It will not be removed from the server.",
                "Delete?",
                JOptionPane.OK_CANCEL_OPTION);
        if (ans == JOptionPane.OK_OPTION) {
            delPad(getSelectedPad());
        }
        updateList();
    }//GEN-LAST:event_delBtnActionPerformed

    private void previewBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_previewBtnActionPerformed
        previewPane.setText(getPad(getSelectedPad()));
        updateList();
    }//GEN-LAST:event_previewBtnActionPerformed

    private void openBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openBtnActionPerformed
        loadPad(getSelectedPad());
    }//GEN-LAST:event_openBtnActionPerformed

    private void purgeBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_purgeBtnActionPerformed
        int ans = JOptionPane.showInternalConfirmDialog(this,
                "Remove pad from server and list?  This cannot be undone!",
                "Delete?",
                JOptionPane.OK_CANCEL_OPTION);
        if (ans == JOptionPane.OK_OPTION) {
            delPad(getSelectedPad(), true);
        }
        updateList();
    }//GEN-LAST:event_purgeBtnActionPerformed

    private void saveBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveBtnActionPerformed
        MainGUI.loadFrame(new CodeEditor(getPad(getSelectedPad()), true));
        updateList();
    }//GEN-LAST:event_saveBtnActionPerformed

    private void padPaneValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_padPaneValueChanged
        // Enable/disable action buttons to prevent errors.
        boolean enable = true;
        if (padPane.getSelectedValue() == null) {
            enable = false;
        }
        openBtn.setEnabled(enable);
        saveBtn.setEnabled(enable);
        delBtn.setEnabled(enable);
        shareBtn.setEnabled(enable);
        previewBtn.setEnabled(enable);
        purgeBtn.setEnabled(enable);
    }//GEN-LAST:event_padPaneValueChanged

    private void addBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addBtnActionPerformed
        String id = JOptionPane.showInternalInputDialog(this,
                "What is the pad ID?",
                "Add Pad",
                JOptionPane.QUESTION_MESSAGE);
        if (id != null && !id.equals("")) {
            if (id.contains("pad.symatapp.com/p/")) {
                id = id.substring(id.lastIndexOf('/') + 1);
                Debug.println(id);
            }
            addPad(id);
        }
        updateList();
    }//GEN-LAST:event_addBtnActionPerformed

    private void shareBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_shareBtnActionPerformed
        JOptionPane.showInternalMessageDialog(this,
                new SharePad(getSelectedPad()),
                "Share Pad",
                JOptionPane.PLAIN_MESSAGE);
    }//GEN-LAST:event_shareBtnActionPerformed

    private String getSelectedPad() {
        return padPane.getSelectedValue().toString();
    }

    private void updateList() {
        padPane.setListData(getPads());
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton addBtn;
    private javax.swing.JButton delBtn;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JButton openBtn;
    private javax.swing.JList padPane;
    private javax.swing.JButton previewBtn;
    private javax.swing.JTextArea previewPane;
    private javax.swing.JButton purgeBtn;
    private javax.swing.JButton saveBtn;
    private javax.swing.JButton shareBtn;
    // End of variables declaration//GEN-END:variables
}
