package org.wikiwebserver.sync.gui;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.TitledBorder;

import org.wikiwebserver.sync.DataLocation;
import org.wikiwebserver.sync.HTTPDataLocationConnector;
import org.wikiwebserver.sync.LocalFileDataLocation;
import org.wikiwebserver.sync.Syncher;

public class JSiteSync extends JPanel {
    
    

    
    public static void main(final String[] args) {
        try {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (Exception e) { }

            
            SwingUtilities.invokeAndWait(new Runnable() {
                public void run() {
                   JFrame frame = new JFrame("JSiteSync");
                   JSiteSync siteSync = new JSiteSync();
                   siteSync.setDefaultLocation(1, null);
                   
                   DataLocation remote = null;
                   String urlString = getConfigString("server", args, null);
                   if (urlString != null) {
                       try {
                           URL url = new URL(urlString);
                           String superPassword = getConfigString("superPassword", args, null);
                           if (superPassword != null) {
                               // Custom settings for syncing WikiWebServer
                               File baseFile = new File("/home/dev/WikiWebServer/WikiWebServer");
                               LocalFileDataLocation local = new LocalFileDataLocation(baseFile, 1);
                               List<File> excludedFiles = new ArrayList<File>();
                               excludedFiles.add(new File(baseFile, "org/wikiwebserver/core"));
                               excludedFiles.add(new File(baseFile, "logs"));
                               excludedFiles.add(new File(baseFile, "user"));
                               excludedFiles.add(new File(baseFile, "gallery"));
                               excludedFiles.add(new File(baseFile, ".store"));                               
                               local.setExcludedFiles(excludedFiles);
                               siteSync.setDefaultLocation(1, local);
                               
                               remote = new HTTPDataLocationConnector(url, superPassword, 2);
                               siteSync.setDefaultLocation(2, remote);
                           }
                           String userId = getConfigString("userID", args, null);                           
                           if (userId != null) {
                               String sessionAuth = getConfigString("sessionAuth", args, null);
                               remote = new HTTPDataLocationConnector(url, userId, sessionAuth, 2);
                               siteSync.setDefaultLocation(2, remote);
                           }
                       } catch (MalformedURLException ex) {
                           ex.printStackTrace();
                       }
                   }
                   
                   
                   siteSync.construct();
                   
                   frame.getContentPane().add(siteSync);
                   frame.pack();
                   frame.setSize(620, 400);                   
                   frame.setVisible(true);
                   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                }
            });
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    
    private static String getConfigString(String name, String[] args, String failValue) {
        for (int i=0; i<args.length; i++) {
            if (args[i].equals("-" + name)) {
                if (i == args.length-1 || args[i+1].startsWith("-")) {
                    System.err.println("Value expected for " + args[i]);
                    System.exit(1);
                }
                return args[i+1];
            }
        }
        return failValue;
    }      
    
    
    
    private static final long serialVersionUID = 1L;
    
    private SyncToolBar toolBar;
    private JTabbedPane tabber;
    private JCheckBox maintainHistoryBox;
    private Map<Integer, DataLocation> defaultDataLocations = new HashMap<Integer, DataLocation>();
    private List<DataLocationPanel> dataLocationPanels = new ArrayList<DataLocationPanel>();
    private FileItemTableModel tableModel = new FileItemTableModel(); 
    
    private Syncher syncher;
    
    public JSiteSync() {   
        syncher = new Syncher();
    }
    
    public Syncher getSyncher() {
        return syncher;
    }
    
    public void construct() {
        
        setLayout(new BorderLayout());
        toolBar = new SyncToolBar(this);
        add(toolBar, BorderLayout.PAGE_START);        
        
        tabber = new JTabbedPane();
        tabber.addTab("Sites", constructSitePanel());
        tabber.addTab("Differences", new JLabel(""));
        tabber.addTab("Progress", new JLabel(""));
        
        addDataLocation();
        addDataLocation();
        
        add(tabber, BorderLayout.CENTER);
    }
    
    private JComponent constructProcessingPanel(JComponent comp) {
        
        JPanel panel = new JPanel(new BorderLayout());
        panel.add(comp, BorderLayout.NORTH);      
        
        return panel;
    }     
    
    private JComponent constructDifferencesPanel() {
        
        JTable table = new JTable(tableModel);
        table.setAutoCreateRowSorter(true);           
        table.getTableHeader().setResizingAllowed(true);
        table.getColumnModel().getColumn(0).setPreferredWidth(130);
        table.getColumnModel().getColumn(1).setPreferredWidth(30);        
        table.getColumnModel().getColumn(2).setPreferredWidth(240);
        table.getColumnModel().getColumn(3).setPreferredWidth(80);     
        table.getColumnModel().getColumn(4).setPreferredWidth(100);   
        table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
        table.setDefaultRenderer(JLabel.class, new IconCellRenderer()); 
        table.setDefaultRenderer(Date.class, new DateTimeCellRenderer()); 
        table.setDefaultRenderer(Long.class, new FileSizeCellRenderer()); 
        table.setShowVerticalLines(false);
        table.setRowSelectionAllowed(false);
        table.setColumnSelectionAllowed(false);
        table.setCellSelectionEnabled(false);
        
        return new JScrollPane(table);
    }
    
    public void addDataLocation() {
        syncher.interrupt();
        syncher.clearSyncCommands();
        tabber.setSelectedIndex(0);  
        int site = dataLocationPanels.size()+1;
        dataLocationPanels.add(new DataLocationPanel(getDefaultLocation(site)));     
        
        tabber.setComponentAt(0, constructSitePanel());
    }
    
    public void setDefaultLocation(int site, DataLocation defaultLocation) {
        defaultDataLocations.put(site, defaultLocation);
    }
    
    public DataLocation getDefaultLocation(int site) {
        DataLocation location = defaultDataLocations.get(site);
        if (location == null) {
            File file = new File(System.getProperty("user.home"));
            File docs = new File(file, "My Documents\\");
            if (docs.exists()) file = docs;
            location = new LocalFileDataLocation(file, site);
        }
        return location;
    }
    
    public void removeDataLocation() {
        syncher.interrupt();
        syncher.clearSyncCommands();
        tabber.setSelectedIndex(0);  
        if (dataLocationPanels.size() > 2) {
            dataLocationPanels.remove(dataLocationPanels.size()-1);
            tabber.setComponentAt(0, constructSitePanel());
        }
    }    
    
    public JComponent constructSitePanel() {
        
        if (dataLocationPanels.size() == 0) {
            return new JLabel("No sites specified");
        }

        JPanel stack = stack(dataLocationPanels);
        
        JPanel locPanel = new JPanel(new BorderLayout());
        locPanel.add(stack, BorderLayout.NORTH);      
        
        JScrollPane scroller =  new JScrollPane(locPanel);
        
        JPanel panel = new JPanel(new BorderLayout());
        panel.add(scroller, BorderLayout.CENTER);  
        
        maintainHistoryBox = new JCheckBox("Maintain site history (required to sync deletes)");
        maintainHistoryBox.setSelected(true);
        panel.add(maintainHistoryBox, BorderLayout.SOUTH);    
        
        return panel;
    }   
    
    private JPanel stack(List<? extends JComponent> comps) {
        JPanel stack = null;
        for (JComponent c : comps) {
            stack = stack(stack, c);
        }
        return stack;
    }    
    
    private JPanel stack(JPanel previousStack, JComponent comp) {
        JPanel stack = new JPanel(new BorderLayout());
        stack.setOpaque(false);
        if (previousStack != null) {
            stack.add(previousStack, BorderLayout.NORTH);
        }
        stack.add(comp, BorderLayout.CENTER);
        return stack;
    }
    


    
    private DataLocation[] getLocations() throws MalformedURLException {
        DataLocation[] locations = new DataLocation[dataLocationPanels.size()];
        for (int i=0; i<locations.length; i++) {
            DataLocationPanel panel = dataLocationPanels.get(i);
            locations[i] = panel.getDataLocation();
        }
        return locations;
    }

    public void findDifferencesInBackground() throws MalformedURLException {
        ProcessingPanel procPanel = new ProcessingPanel(this, syncher, Syncher.Action.DIFF);
        tabber.setComponentAt(1, constructProcessingPanel(procPanel));
        tabber.setSelectedIndex(1);        
        syncher.setDataLocations(getLocations());
        setupId();
        syncher.findDifferencesInBackground();     
    }
    
    public void performSyncInBackground() throws MalformedURLException {
        ProcessingPanel procPanel = new ProcessingPanel(this, syncher, Syncher.Action.SYNC);
        tabber.setComponentAt(2, constructProcessingPanel(procPanel));
        tabber.setSelectedIndex(2);  
        syncher.setDataLocations(getLocations());
        setupId();
        syncher.performSyncInBackground();
    }
    
    private void setupId() throws MalformedURLException {
        if (maintainHistoryBox.isSelected()) {
            String key = "";
            try {
                key = InetAddress.getLocalHost().getHostAddress();
            } catch (Exception ex) {}
            for (DataLocationPanel lp : dataLocationPanels) {
                key += lp.getDataLocation().getClass();
                key += lp.getDataLocation().getBasePath().toString();
            }
            syncher.generateId(key);
        } else syncher.clearId();
    }
    
    public void interrupt() {
        syncher.interrupt();
    }

    public void showDifferences() {
        tableModel.setData(syncher.getSyncCommands().getFileItemsInOperationOrder());            
        tabber.setComponentAt(1, constructDifferencesPanel());
    }
    
    public void showException() {

        JPanel panel = new JPanel(new BorderLayout());   
        
        JPanel grid = new JPanel(new GridLayout(4, 1));
        grid.setBorder(new TitledBorder("Details"));
        
        grid.add(new JLabel(syncher.getException().getMessage()));
        grid.add(new JLabel("Site: " + syncher.getCurrentSite()));        
        if (syncher.getCurrentItem() != null) {
            grid.add(new JLabel("Path: " + syncher.getCurrentItem().getRelPath()));
        }    
        if (syncher.getCurrentTask() != null) {
            grid.add(new JLabel("Action: " + syncher.getCurrentTask()));
        }           
        panel.add(grid, BorderLayout.NORTH);         
        
        Exception ex = syncher.getException();
        
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        PrintStream printer = new PrintStream(out);
        ex.printStackTrace(printer);
        
        JTextArea textArea = new JTextArea(new String(out.toByteArray()));        
        panel.add(new JScrollPane(textArea), BorderLayout.CENTER); 
        
        if (tabber.getTabCount() < 4) {
            tabber.addTab("Exception", panel);
        } else {
            tabber.setComponentAt(3, panel);
            tabber.setSelectedIndex(3);
        }
        tabber.setSelectedComponent(panel);
    }    
    
    public void updateButtons() {
        toolBar.updateButtons();
    }
}

