/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.cobi.kgg.ui.dialog;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javax.swing.JOptionPane;
import javax.swing.SwingWorker;
import java.util.logging.Logger;
import org.cobi.kgg.business.entity.Constants;
import static org.cobi.kgg.business.entity.Constants.KGG_RESOURCE_URL;
import static org.cobi.kgg.business.entity.Constants.PDATE;
import org.cobi.kgg.ui.GeneralNodeEntity;
import org.cobi.kgg.ui.GlobalManager;
import org.cobi.kgg.ui.ResouceNode;
import org.cobi.util.download.stable.DownloadTaskEvent;
import org.cobi.util.download.stable.DownloadTaskListener;
import org.cobi.util.download.stable.HttpClient4API;
import org.cobi.util.download.stable.HttpClient4DownloadTask;
import org.cobi.util.file.Zipper;
import org.netbeans.api.settings.ConvertAsProperties;
import org.openide.DialogDisplayer;
import org.openide.ErrorManager;
import org.openide.NotifyDescriptor;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.StatusDisplayer;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.ExplorerUtils;
import org.openide.explorer.view.BeanTreeView;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.util.Lookup;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.windows.TopComponent;
import org.openide.util.NbBundle.Messages;
import org.openide.util.Utilities;
import org.openide.windows.WindowManager;
import static org.cobi.kgg.business.entity.Constants.resourceFiles;
import org.cobi.util.net.URLOpener;

/**
 * Top component which displays something.
 */
@ConvertAsProperties(
        dtd = "-//org.cobi.kgg.ui.dialog//Resource//EN",
        autostore = false)
@TopComponent.Description(
        preferredID = "ResourceTopComponent",
        iconBase = "org/cobi/kgg/ui/png/16x16/Hard disk.png",
        persistenceType = TopComponent.PERSISTENCE_ALWAYS)
@TopComponent.Registration(mode = "explorer", openAtStartup = true)
@ActionID(category = "Window", id = "org.cobi.kgg.ui.dialog.ResourceTopComponent")
@ActionReference(path = "Menu/Window" /*, position = 333 */)
@TopComponent.OpenActionRegistration(
        displayName = "#CTL_ResourceAction",
        preferredID = "ResourceTopComponent")
@Messages({
    "CTL_ResourceAction=Resource",
    "CTL_ResourceTopComponent=Resource Window",
    "HINT_ResourceTopComponent=This is a Resource window"
})
public final class ResourceTopComponent extends TopComponent implements LookupListener, ExplorerManager.Provider, Constants {

    private final static Logger LOG = Logger.getLogger(ResourceTopComponent.class.getName());
    private final ExplorerManager mgr = new ExplorerManager();
    private Lookup.Result<ResouceNode> result = null;
    String[] refGeneFiles = new String[]{"hg17", "hg18", "hg19", "hg38"};

    public ResourceTopComponent() {
        initComponents();
        setName(Bundle.CTL_ResourceTopComponent());
        setToolTipText(Bundle.HINT_ResourceTopComponent());
        putClientProperty(TopComponent.PROP_CLOSING_DISABLED, Boolean.TRUE);
        putClientProperty(TopComponent.PROP_DRAGGING_DISABLED, Boolean.TRUE);
        putClientProperty(TopComponent.PROP_MAXIMIZATION_DISABLED, Boolean.TRUE);
        putClientProperty(TopComponent.PROP_SLIDING_DISABLED, Boolean.TRUE);
        putClientProperty(TopComponent.PROP_UNDOCKING_DISABLED, Boolean.TRUE);
        putClientProperty(TopComponent.PROP_KEEP_PREFERRED_SIZE_WHEN_SLIDED_IN, Boolean.TRUE);

        AbstractNode node = new AbstractNode(Children.LEAF);
        node.setDisplayName("KGG resource files");
        mgr.setRootContext(node);
        associateLookup(ExplorerUtils.createLookup(mgr, getActionMap()));
    }

    /**
     * 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.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jTabbedPane1 = new javax.swing.JTabbedPane();
        resourceTreeScrollPane = new BeanTreeView();

        setLayout(new java.awt.BorderLayout());
        add(resourceTreeScrollPane, java.awt.BorderLayout.CENTER);
    }// </editor-fold>//GEN-END:initComponents
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JTabbedPane jTabbedPane1;
    private javax.swing.JScrollPane resourceTreeScrollPane;
    // End of variables declaration//GEN-END:variables

    @Override
    public void componentOpened() {
        result = Utilities.actionsGlobalContext().lookupResult(ResouceNode.class);
        result.addLookupListener(this);

        //NbBundle.getBundle("org.netbeans.core.windows.view.ui.Bundle").getString("CTL_MainWindow_Title")
        try {
            if (GlobalManager.canConnect2Website) {
                String remoteVersionID = HttpClient4API.getContent(KGG_RESOURCE_URL + "kgg4/version.dat", GlobalManager.proxyBean);
                Date remoteDate = new SimpleDateFormat("MMMM d, yyyy", Locale.ENGLISH).parse(remoteVersionID);
                Date locatDate = new SimpleDateFormat("MMMM d, yyyy", Locale.ENGLISH).parse(PDATE);
                if (remoteDate.after(locatDate)) {
                    String infor = "<html>We have released a newer version of KGG4.<br> Please go to our  <a href=\"http://grass.cgs.hku.hk/limx/kgg/download.php\" target=\"_blank\">website</a> to download the  <a href=\"http://grass.cgs.hku.hk/limx/kgg/download.php\" target=\"_blank\">latest version of KGG4</a>!"
                            + "<br>Do you want to go to the download page?</html>";

                    Object[] options = {"Yes", "No"};
                    int response = JOptionPane.showOptionDialog(this, infor, "Message", JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE,
                            null, options, options[0]);
                    if (response == 0) {
                        URLOpener.openURL("http://grass.cgs.hku.hk/limx/kgg/download.php");
                    }
                }
            }
        } catch (Exception ex) {
            ErrorManager.getDefault().notify(ex);
        }

        if (GlobalManager.canConnect2Website) {
            ResourceCheckerSwingWorker checker = new ResourceCheckerSwingWorker();
            checker.execute();
        }

        //Locate the Output Window instance                
        final String OUTPUT_ID = "log";
        TopComponent outputWindow = WindowManager.getDefault().findTopComponent(OUTPUT_ID);

        //Determine if it is opened
        if (outputWindow != null) {
            //    outputWindow.open();
        }
        outputWindow = WindowManager.getDefault().findTopComponent("AnalysisOutputTopComponent");
        //Determine if it is opened
        if (outputWindow != null) {
            outputWindow.open();
        }

    }

    @Override
    public void componentClosed() {
        // TODO add custom code on component closing
        result.removeLookupListener(this);
        result = null;
    }

    @Override
    public void resultChanged(LookupEvent le) {
        Collection<? extends ResouceNode> allEvents = result.allInstances();
        if (!allEvents.isEmpty()) {
            ResouceNode event = allEvents.iterator().next();
            //System.out.println(event.toString());
        } else {
            // jLabel1.setText("[no selection]");
            //jLabel2.setText("");
        }
    }

    void writeProperties(java.util.Properties p) {
        // better to version settings since initial version as advocated at
        // http://wiki.apidesign.org/wiki/PropertyFiles
        p.setProperty("version", "1.0");
        // TODO store your settings
    }

    void readProperties(java.util.Properties p) {
        String version = p.getProperty("version");
        // TODO read your settings according to their version
    }

    class ResourceCheckerSwingWorker extends SwingWorker<List<HttpClient4DownloadTask>, String> {

        int MAX_TASK = 2;
        ExecutorService exec = Executors.newFixedThreadPool(MAX_TASK);
        CompletionService<String> serv = new ExecutorCompletionService<String>(exec);
        List<String> updatedURLFiles = new ArrayList<String>();
        int runningThread = 0;

        @Override
        protected List<HttpClient4DownloadTask> doInBackground() {
            List<HttpClient4DownloadTask> downloadTask = new ArrayList<HttpClient4DownloadTask>();
            try {
                updateResources();
                Thread.sleep(5000);
                publish("Checking resources ...");
                ArrayList<String> updatedFiles = new ArrayList<String>();
                ArrayList<String> updatedMD5s = new ArrayList<String>();

                for (String resourceFile : resourceFiles) {
                    String urlFile = KGG_RESOURCE_URL + resourceFile;
                    String urlFileMD5 = HttpClient4API.getContent(urlFile + ".md5", GlobalManager.proxyBean);
                    File localFile = new File(GlobalManager.RESOURCE_PATH + resourceFile);
                    if (resourceFile.endsWith(".gz")) {
                        if (localFile.exists()) {
                            long netFileLen = HttpClient4API.getContentLength(urlFile, GlobalManager.proxyBean);
                            long fileSize = localFile.length();
                            if (netFileLen > 1024 && fileSize != netFileLen) {
                                updatedFiles.add(resourceFile);
                                updatedMD5s.add(urlFileMD5);
                            }
                        } else {
                            updatedFiles.add(resourceFile);
                            updatedMD5s.add(urlFileMD5);
                        }

                    } else {
                        localFile = new File(GlobalManager.RESOURCE_PATH + File.separator + "snp" + File.separator + resourceFile);
                        //directly chcek the resource data based on the local file size with .gz
                        String fileSize = GlobalManager.resourceFileSize.get(resourceFile);
                    }
                }
                //specal download for refGeneFiles               
                for (String refFile : refGeneFiles) {
                    File localFile = new File(GlobalManager.RESOURCE_PATH + File.separator + refFile + "_refGene.txt.gz");
                    String url = "http://hgdownload.cse.ucsc.edu/goldenPath/" + refFile + "/database/refGene.txt.gz";
                    if (localFile.exists()) {
                        long fileSize = localFile.length();
                        long urlFieSize = HttpClient4API.getContentLength(url, GlobalManager.proxyBean);
                        if (urlFieSize > 100 && fileSize != urlFieSize) {
                            updatedFiles.add(url);
                        }
                    } else {
                        updatedFiles.add(url);
                    }
                }

                boolean toDownloadHGNC = false;

                String hgncFileName = "HgncGene.txt";
                File resourceFile = new File(GlobalManager.RESOURCE_PATH + "" + hgncFileName);

                if (!resourceFile.exists()) {
                    toDownloadHGNC = true;

                } else {
                    //two weeks  later 
                    long time = resourceFile.lastModified() / 1000 + 14 * 24 * 60 * 60;
                    Date fileData = new Date(time * 1000);
                    Date today = new Date();
                    //if too small or too early
                    if (resourceFile.length() < 2.2 * 1024 * 1024 || today.after(fileData)) {
                        //an incomplete file
                        resourceFile.delete();

                        toDownloadHGNC = true;
                    }
                }

                //downloading does not support multiple thread 
                String url = "http://www.genenames.org/cgi-bin/hgnc_downloads?col=gd_hgnc_id&col=gd_app_sym&col=gd_app_name&col=gd_prev_sym&col=gd_pub_chrom_map&col=gd_pub_acc_ids&col=gd_pub_eg_id&col=md_eg_id&col=md_mim_id&col=gd_locus_group&status=Approved&status_opt=2&where=&order_by=gd_hgnc_id&format=text&limit=&hgnc_dbtag=on&submit=submit";
                if (toDownloadHGNC) {
                    resourceFile = new File(GlobalManager.RESOURCE_PATH + "" + hgncFileName);
                    String msg1 = "Donwloading HGNC gene annotations ... ";
                    publish(msg1);
                    File parePath = resourceFile.getParentFile();
                    if (!parePath.exists()) {
                        parePath.mkdirs();
                    }
                    HttpClient4API.simpleRetriever(url, resourceFile.getCanonicalPath(), GlobalManager.proxyBean);
                }

                if (updatedFiles.isEmpty()) {
                    return downloadTask;
                }

                StringBuilder infor = new StringBuilder();
                infor.append("The following ");
                infor.append(" data will be downloaded and updated!\n");
                for (int i = 0; i < updatedFiles.size(); i++) {
                    infor.append(i + 1);
                    infor.append(".  ");
                    infor.append(updatedFiles.get(i));
                    infor.append("\n");
                }

                Object[] options = {"OK", "CANCEL"};
                int response = JOptionPane.showOptionDialog(new javax.swing.JFrame(), infor.toString(), "Message",
                        JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, options, options[0]);
                if (response == 1) {
                    return null;
                }

                // downloadProgressBar.setVisible(true);
                int filesNum = updatedFiles.size();
                boolean hasUpdateRef = false;
                //this.insertBriefRunningInfor("Updating resource files ...", true);
                publish("Updating resource files ...");
                for (int i = 0; i < filesNum; i++) {
                    final HttpClient4DownloadTask task = new HttpClient4DownloadTask(KGG_RESOURCE_URL + updatedFiles.get(i), 9, GlobalManager.proxyBean);
                    final String fileName = updatedFiles.get(i);
                    File folder = new File(GlobalManager.RESOURCE_PATH);
                    if (!folder.exists()) {
                        folder.mkdir();
                    }
                    if (!updatedFiles.get(i).contains("Path/hg")) {
                        task.setLocalPath(folder.getCanonicalPath() + File.separator + updatedFiles.get(i));
                        task.setDataMd5(updatedMD5s.get(i));
                        task.addTaskListener(new DownloadTaskListener() {
                            @Override
                            public void autoCallback(DownloadTaskEvent event) {
                                int progess = (int) (event.getTotalDownloadedCount() * 100.0 / event.getTotalCount());
                                //downloadProgressBar.setValue(progess);
                                StatusDisplayer.getDefault().setStatusText("Downloading: " + fileName + " RealTimeSpeed: " + event.getRealTimeSpeed() + " GlobalSpeed:" + event.getGlobalSpeed());
                            }

                            @Override
                            public void taskCompleted() throws Exception {
                                File savedFile = new File(task.getLocalPath());
                                GlobalManager.resourceFileSize.put(savedFile.getName(), task.getDataMd5());
                                Zipper ziper = new Zipper();
                                if (task.getLocalPath().endsWith("zip")) {
                                    ziper.extractZip(savedFile.getCanonicalPath(), savedFile.getParent());
                                    savedFile.delete();
                                }
                                /*
                                 *  else if (task.getLocalPath().endsWith(".gz")) {
                                 ziper.extractTarGz(task.getLocalPath(), savedFile.getParent());
                                 savedFile.delete();
                                 }
                                 */
                            }
                        });
                    } else {
                        for (String refFile : refGeneFiles) {
                            hasUpdateRef = true;
                            if (updatedFiles.get(i).contains(refFile)) {
                                url = "http://hgdownload.cse.ucsc.edu/goldenPath/" + refFile + "/database/refGene.txt.gz";
                                task.setUrl(url);
                                task.setLocalPath(folder.getCanonicalPath() + File.separator + refFile + "_refGene.txt.gz");
                                task.addTaskListener(new DownloadTaskListener() {
                                    @Override
                                    public void autoCallback(DownloadTaskEvent event) {
                                        int progess = (int) (event.getTotalDownloadedCount() * 100.0 / event.getTotalCount());
                                        //downloadProgressBar.setValue(progess);
                                        StatusDisplayer.getDefault().setStatusText("Downloading: " + fileName + " RealTimeSpeed: " + event.getRealTimeSpeed() + " GlobalSpeed:" + event.getGlobalSpeed());
                                    }

                                    @Override
                                    public void taskCompleted() throws Exception {
                                    }
                                });
                                break;
                            }
                        }
                    }
                    // task.call();
                    //exec.submit(task);
                    serv.submit(task);
                    downloadTask.add(task);
                    runningThread++;
                }

                for (int index = 0; index < runningThread; index++) {
                    Future<String> task = serv.take();
                    String download = task.get();
                    publish(download);
                }
                exec.shutdown();
                if (hasUpdateRef && !toDownloadHGNC) {
                    url = "http://www.genenames.org/cgi-bin/hgnc_downloads?col=gd_hgnc_id&col=gd_app_sym&col=gd_app_name&col=gd_prev_sym&col=gd_pub_chrom_map&col=gd_pub_acc_ids&col=gd_pub_eg_id&col=md_eg_id&col=md_mim_id&col=gd_locus_group&status=Approved&status_opt=2&where=&order_by=gd_hgnc_id&format=text&limit=&hgnc_dbtag=on&submit=submit";
                    resourceFile = new File(GlobalManager.RESOURCE_PATH + "" + hgncFileName);
                    String msg1 = "Donwloading HGNC gene annotations ... ";
                    publish(msg1);
                    File parePath = resourceFile.getParentFile();
                    if (!parePath.exists()) {
                        parePath.mkdirs();
                    }
                    HttpClient4API.simpleRetriever(url, resourceFile.getCanonicalPath(), GlobalManager.proxyBean);
                }
            } catch (Exception ex) {
                ErrorManager.getDefault().notify(ex);
            }
            return downloadTask;
        }

        @Override
        protected void process(List<String> chunks) {
            // TODO Auto-generated method stub  
            for (String message : chunks) {
                LOG.info(message);
                StatusDisplayer.getDefault().setStatusText(message);
            }
        }

        @Override
        protected void done() {
            try {
                String message;
                List<HttpClient4DownloadTask> downloadTask = get();
                if (downloadTask == null) {
                    message = "Cancel the downloading of resources!";
                    LOG.info(message);
                    StatusDisplayer.getDefault().setStatusText(message);
                } else if (downloadTask.isEmpty()) {
                    message = "Resources have already been the latest!";
                    NotifyDescriptor nd = new NotifyDescriptor.Message(message, NotifyDescriptor.INFORMATION_MESSAGE);
                    DialogDisplayer.getDefault().notifyLater(nd);
                } else {
                    //get() : Waits if necessary for the computation to complete, and then retrieves its result.   
                    for (HttpClient4DownloadTask task : downloadTask) {
                        message = (task.getLocalPath() + " has been updated successfully!");
                        LOG.info(message);
                        StatusDisplayer.getDefault().setStatusText(message);
                    }
                    updateResources();
                    message = ("The resource data has been updated successfully!");
                    LOG.info(message);
                    StatusDisplayer.getDefault().setStatusText(message);
                }
            } catch (Exception e) {
                ErrorManager.getDefault().notify(e);
            }
        }
    }

    public boolean updateResources() throws Exception {
        boolean addedSome = false;
        List<GeneralNodeEntity> allResourceList = new ArrayList<GeneralNodeEntity>();
        List<File> geneFiles = new ArrayList<File>();
        File omimFile = new File(GlobalManager.RESOURCE_PATH + "OMIMGenes.txt");
        if (omimFile.exists()) {
            geneFiles.add(omimFile);
        }

        File hgncGeneFile = new File(GlobalManager.RESOURCE_PATH + "HgncGene.txt");
        if (hgncGeneFile.exists()) {
            geneFiles.add(hgncGeneFile);
        }

        File tisuGeneFile = new File(GlobalManager.RESOURCE_PATH + "TissueSpecific.txt.gz");
        if (tisuGeneFile.exists()) {
            geneFiles.add(tisuGeneFile);
        }

        File omimGeneFile = new File(GlobalManager.RESOURCE_PATH + "OMIMGenes.txt.gz");
        if (omimGeneFile.exists()) {
            geneFiles.add(omimGeneFile);
        }

        for (String refFile : refGeneFiles) {
            File localFile = new File(GlobalManager.RESOURCE_PATH + File.separator + refFile + "_refGene.txt.gz");
            if (localFile.exists()) {
                geneFiles.add(localFile);
            }
        }

        File localFile = new File(GlobalManager.RESOURCE_PATH + File.separator + "hg19_gencode.v19.txt.gz");
        if (localFile.exists()) {
            geneFiles.add(localFile);
        }
        localFile = new File(GlobalManager.RESOURCE_PATH + File.separator + "hg38_gencode.v23.txt.gz");
        if (localFile.exists()) {
            geneFiles.add(localFile);
        }
        GeneralNodeEntity geneFileNode = new GeneralNodeEntity("Gene", geneFiles);
        allResourceList.add(geneFileNode);

        List<File> pathwaySet = new ArrayList<File>();
        // File gesaFileFolder = new File(GlobalManager.RESOURCE_PATH + "gene/");
        File gesaFileFolder = new File(GlobalManager.RESOURCE_PATH);
        File[] listOfFiles = gesaFileFolder.listFiles();
        if (listOfFiles != null) {
            for (File listOfFile : listOfFiles) {
                if (listOfFile.isFile()) {
                    String fileName = listOfFile.getName();
                    if (fileName.endsWith(".gmt") || fileName.endsWith(".gmt.gz")) {
                        pathwaySet.add(listOfFile);
                    }
                }
            }
        }
        GeneralNodeEntity pathwayFileNode = new GeneralNodeEntity("Pathway", pathwaySet);
        allResourceList.add(pathwayFileNode);

        List<File> ppiFiles = new ArrayList<File>();
        GeneralNodeEntity ppiFileNode = new GeneralNodeEntity("PPI", ppiFiles);
        allResourceList.add(ppiFileNode);

        File ppiFile = new File(GlobalManager.RESOURCE_PATH + "STRINGPPIV905.txt.gz");
        if (ppiFile.exists()) {
            ppiFiles.add(ppiFile);
        }

        ResouceNode prjNode = new ResouceNode(allResourceList);
        mgr.setRootContext(prjNode);

        BeanTreeView tables = (BeanTreeView) resourceTreeScrollPane;
        tables.expandAll();

        /*
         fileNode = new FileTreeNode(GlobalManager.RESOURCE_PATH + "gene/TissueSpecific.txt", "TissueSpecific.txt");
         if (TissueSpecificGene.getChildCount() == 0 && fileNode.exists()) {
         DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(fileNode);
         resourceTreeModel.insertNodeInto(newNode, TissueSpecificGene, TissueSpecificGene.getChildCount());
         addedSome = true;
         }
         */
        return (addedSome);
    }

    @Override
    public ExplorerManager getExplorerManager() {
        return mgr;
    }
}
