package usda.weru.mcrew;

//
//Title:        WEPS XML Management File Updater
//Version:
//Author:       Jim Frankenberger
//Company:      USDA-ARS
//Date:         June 23, 2003
//Description:  Updates WEPS XML management files with the latest crop and operation database records. Also allows names to
//              be fixed with the correct subdirectories for crops and operations.
//
import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.file.TVFS;
import de.schlichtherle.truezip.fs.FsSyncException;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileFilter;

import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import org.apache.commons.io.FileUtils;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; 
import org.openide.util.Exceptions;
import usda.weru.util.ConfigData;
import usda.weru.util.Util;
import usda.weru.util.WepsFileTypes;

class XMLManagementUpdater implements Runnable {

    StatusDialog statDialog;
    int finalCount = 0;
    List<String> tempDb = new ArrayList<>();
    Thread mainUpdateThread = null;
    Component parent;//Reference to the parent window-MCREW
    private static int THREAD_COUNTER;

    //
    // XMLFileFilter
    //
    // Generic class to implement a FilenameFilter to return all files with a specific extension and also
    // to walk down into directories.
    //
    private static class XMLFileFilter implements FilenameFilter {

        String theFilter;

        XMLFileFilter(String filter) {
            theFilter = filter;
        }

        //Accept all directories and all MAN files.
        @Override
        public boolean accept(java.io.File dir, String name) {
            TFile f = new TFile(dir, name);
            if (f.isDirectory()) {
                return true;
            }
            if (name.endsWith("." + theFilter)) {
                return true;
            }
            return false;
//        String fullname="";
//         try {
//            fullname = dir.getCanonicalPath() + "/" + name;
//         } catch (Exception e) {
//                
//         }
//        
//        File f = new File(fullname);
//        if (f.isDirectory()) {
//            return true;
//        }
//      
//        String ext = null;
//        String s = name;
//        int i = s.lastIndexOf('.');
//
//        if (i > 0 &&  i < s.length() - 1) {
//            ext = s.substring(i+1).toLowerCase();
//        }
//        String extension = ext;
//        if (extension != null) {
//            if (extension.equals(theFilter)) {
//               return true;
//            } else {
//                return false;
//            }
//        }
//
//        return false;
//
        }
    }

    /**
     * This function just takes the directory , performs recursion, searches for the 
     * desired files, puts them in a vector and returns them.
     */
    public ArrayList<String> onlyRecursion(String dir, String filterName) {
        ArrayList<String> dirVector = new ArrayList<>();
        //XMLFileFilter filter = new XMLFileFilter("man");
        XMLFileFilter filter = new XMLFileFilter(filterName);
        TFile currentDir = new TFile(dir);

        if (currentDir.isDirectory() == false) {
            JOptionPane.showMessageDialog(null, "Directory is not valid: " + dir, "Error", JOptionPane.INFORMATION_MESSAGE);
            return null;
        }


        /*
         // test it skel directory exists to hold temp files
         File fs = new File(ConfigData.getDirectoryName(XMLConstants.smanagement_skeleton));
         if ((!fs.exists()) || (!fs.isDirectory())) {
         JOptionPane.showMessageDialog(null,"Skeleton directory is not valid: \n\n"+
         ConfigData.getDirectoryName(XMLConstants.smanagement_skeleton),
         "Error",JOptionPane.INFORMATION_MESSAGE);
         return null;    
         }
         */
        Stack<TFile> alldirs = new Stack<TFile>();
        alldirs.push(currentDir);

        // Build a list of the files to process.
        while (alldirs.empty() == false) {
            currentDir = alldirs.pop();

            for (java.io.File f : currentDir.listFiles(filter)) {
                try {
                    String dname = f.getCanonicalPath();
                    
                    if (!f.isDirectory()) {
                        dirVector.add(dname);
                    } else {
                        dirVector.add(dname);
                        alldirs.push(new TFile(f));
                    } //end of if-else
                } catch (IOException ex) {
                    LogManager.getLogger(XMLManagementUpdater.class).error(ex);
                }
            }//end of for loop
        }//end of while
        return dirVector;
    }//end of onlyRecursion
    
    
    /**
     * This function just takes the directory , searches for the 
     * desired files, puts them in a vector and returns them.
     */
    public ArrayList<String> nonRecursion(String dir, String filterName) {
        ArrayList<String> dirVector = new ArrayList<>();
        //XMLFileFilter filter = new XMLFileFilter("man");
        XMLFileFilter filter = new XMLFileFilter(filterName);
        TFile currentDir = new TFile(dir);

        if (currentDir.isDirectory() == false) {
            JOptionPane.showMessageDialog(null, "Directory is not valid: " + dir, "Error", JOptionPane.INFORMATION_MESSAGE);
            return null;
        }


        /*
         // test it skel directory exists to hold temp files
         File fs = new File(ConfigData.getDirectoryName(XMLConstants.smanagement_skeleton));
         if ((!fs.exists()) || (!fs.isDirectory())) {
         JOptionPane.showMessageDialog(null,"Skeleton directory is not valid: \n\n"+
         ConfigData.getDirectoryName(XMLConstants.smanagement_skeleton),
         "Error",JOptionPane.INFORMATION_MESSAGE);
         return null;    
         }
         */
        Stack<TFile> alldirs = new Stack<TFile>();
        alldirs.push(currentDir);

        // Build a list of the files to process.
        while (alldirs.empty() == false) {
            currentDir = alldirs.pop();

            for (java.io.File f : currentDir.listFiles(filter)) {
                try {
                    String dname = f.getCanonicalPath();
                    if (!f.isDirectory()) {
                        dirVector.add(dname);
                    }
                } catch (IOException ex) {
                    LogManager.getLogger(XMLManagementUpdater.class).error(ex);
                }
            }//end of for loop
        }//end of while
        return dirVector;
    }//end of onlyRecursion
    
    private String destDirTemp = "";
    public void setDestTemp(String st){
        destDirTemp = st;
    }
    ArrayList<String> db = new ArrayList<>();
    
    /* The method updateDirectory() is called to update all the .man files 
     * in a specific directory, if the recurse flag is set then all management files
     * in any subdirectories are also updated.
     */
    public void updateDirectory(Component parent, String dir, boolean recurse) {
        int count = 0;
        this.parent = parent;
        setReplacementDir(dir);
        count = doRecursion(dir, recurse);
        if (count != 0) {
            if(inplace){
            doUpdate(parent, db);
            }else{
                doUpdateSetDest(parent,db,destDirTemp);
            }
        }

    }//end of method  updateDirectory   
    private String repDir = "";
    private boolean inplace = false;
    public void setInPlace(boolean sett){
        inplace = sett;
    }
    private String getReplacementDir(){
        return repDir;
    }
    private void setReplacementDir(String d){
        repDir = d;
    }
    

    public int doRecursion(String dir, boolean recurse) {

        //XMLFileFilter filter = new XMLFileFilter("man");        
        TFile currentDir = new TFile(dir);

        if (currentDir.isDirectory() == false) {
            JOptionPane.showMessageDialog(null, "Directory is not valid: " + dir, "Error", JOptionPane.INFORMATION_MESSAGE);
            return 0;
        }

        // test it skel directory exists to hold temp files
        TFile fs = new TFile(MCREWConfig.getDirectoryName(XMLConstants.smanagement_skeleton));
        if ((!fs.exists()) || (!fs.isDirectory())) {
            JOptionPane.showMessageDialog(null, "Skeleton directory is not valid: \n\n"
                    + MCREWConfig.getDirectoryName(XMLConstants.smanagement_skeleton),
                    "Error", JOptionPane.INFORMATION_MESSAGE);
            return 0;
        }
        Stack<TFile> alldirs = new Stack<>();
        alldirs.push(currentDir);

        FileFilter filter = new FileFilter() {

            @Override
            public boolean accept(java.io.File file) {
                return file.isDirectory() || WepsFileTypes.Management.accept(file) || WepsFileTypes.Rotation.accept(file);
            }
        };

        // Build a list of the files to process.
        //process these files remotely?
        while (alldirs.empty() == false) {
            currentDir = alldirs.pop();
            java.io.File fnames[] = currentDir.listFiles(filter);
            for (java.io.File fname : fnames) {
                try {
                    String dname = fname.getCanonicalPath();
                    if (fname.isDirectory() == false) {
                        db.add(dname);
                    } else {
                        if (recurse) {
                            TFile dirname = new TFile(dname);
                            alldirs.push(dirname);
                        }
                    }
                } catch (IOException e) {
                    //System.out.println("Error: " + e);
                }
            }
        }
        return 1;
    }//end of doRecursion()

    public void doUpdate(Component parent, List<String> db) {
        doUpdateThread (parent, db);
    }
    private String destLocation= "";
    public void doUpdateSetDest(Component parent, List<String> db,String location) {
        destLocation = location;
        doUpdateWait(parent, db);
    }
    

    public void doUpdateThread(Component parent, List<String> db) {
        // Process all the files in db vector
        tempDb = db;
        this.parent = parent;
        //  //System.out.println("tempDb.size:"+tempDb.size());
        finalCount = 0;
        this.statDialog = new StatusDialog(parent, "Updating Management Files", this);
        this.statDialog.setMaxProgress(db.size());
        this.statDialog.setVisible(true);
        //Add a thread ID to track how many of these we are creating
        mainUpdateThread = new Thread(this, "XML Management Updater-" + THREAD_COUNTER++);
        mainUpdateThread.setPriority(1);
        mainUpdateThread.start();

    }//end of method doUpdate()

    public List<String> doUpdateWait(Component parent, List<String> db) {
        // Process all the files in db vector
        tempDb = db;
        this.parent = parent;
        //  //System.out.println("tempDb.size:"+tempDb.size());
        finalCount = 0;
        this.statDialog = new StatusDialog(parent, "Updating Management Files", this);
        this.statDialog.setMaxProgress(db.size());
        this.statDialog.setVisible(true);

        return update(false);

    }//end of method doUpdate()
    public boolean isIndv = false;
    public List<String> doUpdateWaitInPlace(Component parent, List<String> db, String des) {
        // Process all the files in db vector
        destLocation = des;
        tempDb = db;
        this.parent = parent;
        //  //System.out.println("tempDb.size:"+tempDb.size());
        finalCount = 0;
        this.statDialog = new StatusDialog(parent, "Updating Management Files", this);
        this.statDialog.setMaxProgress(db.size());
        this.statDialog.setVisible(true);

        return update(false);

    }//end of method doUpdateInPlace()
    
    public List<String> doUpdateWaitInPlace(Component parent, List<String> db) {
        // Process all the files in db vector
        tempDb = db;
        this.parent = parent;
        //  //System.out.println("tempDb.size:"+tempDb.size());
        finalCount = 0;
        this.statDialog = new StatusDialog(parent, "Updating Management Files", this);
        this.statDialog.setMaxProgress(db.size());
        this.statDialog.setVisible(true);

        return update(true);

    }//end of method doUpdateInPlace()
    
    @Override
//    @SuppressWarnings("deprecation")
    public void run() {
        update(true);
    }//end of run method
    
    protected List<String> update(boolean inplace) {
        //System.out.println("In place update? " + inplace);
        int count = 0;
        //String skelfile = MCREWConfig.getDirectoryName(XMLConstants.smanagement_skeleton) + "/temp.skel";
        String skelfile;
        
        List<String> retList = new ArrayList<>();
        
        SkelImportPanel skelimport = new SkelImportPanel(null, "Operation or crop not found",
                true, ManageData.WriteFileMode.UPDATE);
        String opDBDir = MCREWConfig.getDirectoryName(XMLConstants.soperation);
        String cropDBDir = MCREWConfig.getDirectoryName(XMLConstants.scrop);
        if (!skelimport.setDatabases(opDBDir, cropDBDir)) {
            //something didn't go right, let's quit
            return retList;
        }
        int totalFiles = tempDb.size();
        int toDelete;
        for (toDelete = 0; toDelete < tempDb.size(); toDelete++) {
            String s = tempDb.get(toDelete);
            
            if (!inplace) {
                File existingFile = new File (s);
                //if the destination isn't empty reset to be the new destination
                String projPath = ConfigData.getDefault().getDataParsed(ConfigData.CurrentProj);
                if(!(destLocation == null || destLocation.trim().isEmpty())){
                    String exFill = existingFile.getParentFile().getAbsolutePath()+File.separator;
                    
                    if(!exFill.equals(destLocation) && !isIndv){
                        exFill = exFill.replace(repDir, "");
                        projPath = destLocation+File.separator+ exFill + File.separator;// ;
                    }else{
                        projPath = destLocation;
                    }
                        
                }
                
                if (!projPath.contentEquals(existingFile.getParent())) {
                    File modFile = new File (projPath, existingFile.getName());
                    try {
                        FileUtils.copyFile(existingFile, modFile);
                        s = modFile.getAbsolutePath();
                        tempDb.set(toDelete, s);
                    } catch (IOException ex) {
                        LogManager.getLogger(XMLManagementUpdater.class).warn("management update file copy failed, existingFile="+
                                existingFile.getAbsolutePath()+ " modFile="+modFile.getAbsolutePath());
                        JOptionPane.showMessageDialog(parent, "WEPS management update failed,\n"+" could not update file:"+existingFile.getAbsolutePath(),
                                "WEPS management update Information", JOptionPane.INFORMATION_MESSAGE);
                        return retList;
                    }
                }
            }
            retList.add(s);

            // convert .man into a skeleton XML file in order to strip out all the crop and operation detail parameters
            SkeletonUpdater skel = new SkeletonUpdater();
            //Update the text field & progress bar in the status dialog
            this.statDialog.JTF_status.setText("Updating " + (toDelete + 1) + " out of " + totalFiles + " Management files.");
            this.statDialog.JTF_file.setText(s);
            this.statDialog.setProgress(toDelete + 1);
            this.statDialog.refresh();
            // This is what really does the conversion, read the management file and then write it out as a XML skel file
            if (skel.readWEPSManBrief(s)) {
                TFile manfile = new TFile(s);
                String basefile = manfile.getName();
                int end = basefile.lastIndexOf(".");
                if (end > 0) {
                    basefile = basefile.substring(0, end);
                }

                // This is a temp file in the skeleton directory with a __ at the beginning of the name
                skelfile = getTempSkelFileName(basefile);
                if (skel.writeSkeletonXMLFile(skelfile)) {
                    // now use the skel-to-man code to build a full file with the updated params
                    if (!skelimport.setDatabases(opDBDir, cropDBDir)) {
                        //something didn't go right, let's quit
                        LogManager.getLogger(XMLManagementUpdater.class).warn("skelimport.setDatabases failed, opDBDir="+opDBDir+ "cropDBDir="+cropDBDir);
                        // MEH: in middle of loop: cannot just bail.
                        //      log error(s) and then let finish and attempt to
                        //      cleanup.
                        //return;
                    }

                    // If there are operation or crop database files that are not found allow the user to fix these
                    if (skelimport.skel2man(skelfile, s)) {
                        count++;
                        
                    } else {
                        if (JOptionPane.showConfirmDialog(parent,
                                "Do you want to stop updating management files?", "WEPS Information",
                                JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
                            TFile tempSkelFile = new TFile(skelfile);
                            if (tempSkelFile.exists()) {
                                try {
                                    tempSkelFile.rm();
                                } catch (IOException ex) {
                                    LogManager.getLogger(XMLManagementUpdater.class).warn(
                                            "tempSkelFile.rm failed, tempSkelFile="+tempSkelFile.getAbsolutePath());
                                }
                            }
                            break;
                        }
                    }//end of inner if-else block
                } else {
                    if (JOptionPane.showConfirmDialog(parent, "Could not load skeleton file: "
                            + skelfile + "\n\nDo you want to stop updating management files?",
                            "WEPS Information", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
                        break;
                    }
                }


            }//end of if(skel.readWEPSManBrief(s))
        }//end of for loop
        
        try {
            // flush/close all of the TrueZip system before deleting temp files
            TVFS.umount();
        } catch (FsSyncException ex) {
            LogManager.getLogger(XMLManagementUpdater.class).warn(
                    "TrueZip system umount() exception:"+ex.getMessage());
        }
        String finalStrList = "";
        for(int index = 0; index < toDelete; index ++)
        {
            String s = tempDb.get(index);
            finalStrList += s+ "\n";
            TFile manfile = new TFile(s);
            String basefile = manfile.getName();
            int end = basefile.lastIndexOf(".");
            if (end > 0) {
                basefile = basefile.substring(0, end);
            }
            TFile tempSkelFile = new TFile(getTempSkelFileName(basefile));
            if (tempSkelFile.exists()) {
                tempSkelFile.deleteOnExit();
                //try{
                //tempSkelFile.rm();
                //} catch (IOException ex) {
                    //LogManager.getLogger(XMLManagementUpdater.class).warn(
                            //"tempSkelFile.rm #2 failed, tempSkelFile="+tempSkelFile.getAbsolutePath());
                //}
            }// cleanup the skel file used in the intermediate step
        }
        finalCount = count;
        
        //dispose of the status dialog
        statDialog.setVisible(false);
        statDialog.dispose();
        statDialog = null;
        //Display the total number of files updated.
        JDialog jdog = new JDialog();
//        jdog.setLayout(new GridLayout(2,
//          1,10,10));
        JTextArea textArea = new JTextArea(finalCount + " WEPS Management Files Updated\n"+finalStrList);
        JButton button;
        button = new JButton("CLOSE");
     
    button.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e)
        {
            jdog.dispose();
        }
    });
//    textArea.add(button);
        JScrollPane scrollPane = new JScrollPane(textArea,JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);  
        
//        textArea.setLineWrap(true);  
//        textArea.setWrapStyleWord(true); 
        textArea.setPreferredSize( new Dimension( 500, 250 ) );
        textArea.setEditable(false);
        scrollPane.setPreferredSize( new Dimension( 500, 250 ) );
        button.setMargin(null);
        
    button.setSize(120, 30);
//    scrollPane.
     
        
         JPanel sizePanel = new JPanel();
     sizePanel.add(button);
     //parameterPanel.add(parameterButton);
//     parameterPanel.add(sizePanel);
    
        jdog.setTitle("Files Being updated");
//        jdog.setSize(new Dimension( 500, 250 ));
        jdog.setResizable(true);
        jdog.add(scrollPane);
        jdog.add(sizePanel, BorderLayout.SOUTH);
//        jdog.add(sizePanel);
//        jdog.add
//        jdog.add(scrollPane);
        jdog.pack();
        jdog.setModal(true);
        
//    scrollPane.pack();
//        jdog.add()
    
//    jdog.setLocation(parent.getX()-50, parent.getY()-50);
    jdog.setVisible(true);
//        JOptionPane.showMessageDialog(parent, jdog,
//                        "WEPS Information", JOptionPane.INFORMATION_MESSAGE);
//        JOptionPane.showMessageDialog(parent, finalCount + " WEPS Management Files Updated\n"+tempDb.get(0),
//                "WEPS Information", JOptionPane.INFORMATION_MESSAGE);

        return retList;
    }//end of run method
    
    protected String getTempSkelFileName (String basefile) {
        return MCREWConfig.getDirectoryName(XMLConstants.smanagement_skeleton)
                        + TFile.separator + "__" + basefile + ".skel";
    }
    
}//end of class XMLManagementUpdater
class YourDialog extends JDialog implements ActionListener {

    private static final long serialVersionUID = 1L;

  JButton button;

  public YourDialog() {
     setSize(new Dimension( 500, 250 ));
     
     
     button = new JButton("Okay");
//     button.s.setBounds(300, 180, size.width, size.height);
     button.addActionListener(this);
     button.setBounds(200,200,40,40);
     add(button);
     pack();
     setVisible(true);
  }

  public void actionPerformed(ActionEvent e) {
      dispose();
  }
}

/* This class handles the behavior of the Status dialog.
 * This is the wrapper for the window that tracks the progress
 * of the update of the Management Files.
 ** @author  neha
 */
class StatusDialog extends usda.weru.mcrew.gui.StatusDialog_n {

    private static final long serialVersionUID = 1L;

    XMLManagementUpdater xmlMgmt;

    /** Creates a new instance of StatusDialog */
    public StatusDialog(Component parent, String title, XMLManagementUpdater xmlMgmtUpdater) {
        super((Frame) parent, false);
        this.xmlMgmt = xmlMgmtUpdater;
        setModal(false);
        setTitle(title);
        setLocation(100, 100);
        setVisible(true);
        JTF_status.setVisible(true);
        JPB_mcrew.setVisible(true);
        setVisible(true);
    }

    /*set the max number of files for the JProgressBar 
     *@param max
     */
    public void setMaxProgress(int max) {
        JPB_mcrew.setMaximum(max);
    }//end of setMaxProgress()

    public void setProgress(int current) {
        JPB_mcrew.setValue(current);
    }//end of setProgress

    public void setExpr(String expr) {
        JTF_status.setText(expr);
    }//end of setExpr

    public void refresh() {
        JP_main.revalidate();
    }
}//end of class StatusDialog

