/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package de.nplusc.izc.InstallPak;

import de.nplusc.izc.tools.IOtools.FileTK;
import de.nplusc.izc.tools.baseTools.Messagers;
import de.nplusc.izc.tools.baseTools.Tools;
import java.awt.EventQueue;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.JTextArea;
import javax.swing.text.DefaultCaret;
import org.yaml.snakeyaml.Yaml;

/**
 *
 * @author LH
 */
public class YInstallAction implements InstallAction
{
    //idx1=position in queue idx2: 0=type,1_onwards=arguments
    Map<String,Object> iZsetupScript;
    Map<String,List<List<String>>> pkg;
    /**
     * Generates the InstallAction Object
     * @param filepath Absolute and preprocessed path to the iZsetup-file
     */
    public YInstallAction(String filepath)
    {
        try
        {
            pkg = new LinkedHashMap<>();
            Yaml y = new Yaml();
            iZsetupScript = (Map<String,Object>) y.load(new FileInputStream(filepath));
            List<String> rootPackages = (List<String>) iZsetupScript.get("Segments");
            for (String id : rootPackages)
            {
                pkg.put(id, getElement(id));
            }
        }
        catch (FileNotFoundException ex)
        {
            ex.printStackTrace();
        }
    }
    
    public void doInstall()
    {
        doInstall(new JProgressBar(0,1000),null,new JLabel(),new JTextArea());
    }
    
    public void doInstall(final JProgressBar j,JProgressBar subProgress,JLabel statusLine,JTextArea logPane)
    {
        
        DefaultCaret caret = (DefaultCaret) logPane.getCaret();
        caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
        int[] checkpoints_main = new int[pkg.size()+1];
        int chunks = checkpoints_main.length;
        checkpoints_main[0]=0;
        //progressbar_chunks
        for (int i = 0; i < chunks; i++)
        {
            checkpoints_main[i+1]=(1000/chunks)*i+1;
        }
        Set<String> chunkIDs = pkg.keySet();
        int i=0;
        for (String id : chunkIDs)
        {
            statusLine.setText("Kalkuliere zu kopierende Datenmenge");
            logPane.append("Kalkuliere zu kopierende Datenmenge\n");
            List<String[]> actions = new ArrayList<>();
            //program,copydir,copyfile,mkdir -> internal use only,copyfile2 -> internal use only
            boolean mixed=false; //wenn mixed ->  jedes file gleiche schrittzahl im gebälk
            String[] type = new String[0];
            long totalSize=0;
            for (List<String> actionRaw : pkg.get(id))
            {
                switch(actionRaw.get(1))
                {
                    case "program"://intended slipthru
                    case "copyfile":
                        actions.add(actionRaw.toArray(type));
                        mixed=true;
                    break;
                    case "copydir":
                        String indir = actionRaw.get(2);
                        String statusMessage = "Kopiere neue Dateien für "+actionRaw.get(0);
                        String target = actionRaw.get(3);
                        String[] filesToCopy = FileTK.walkDirectoryRecursively(indir);
                        for (String element : filesToCopy)
                        {
                            //message,"copyfile2",size,in,out
                            //message,"mkdir",0,which_dir
                            String element_r = FileTK.getRelativePath(element, indir).substring(3);
                            if(new File(element).isDirectory())
                            {
                                actions.add(new String[]{statusMessage,"mkdir","0",(target+"\\"+element_r).replace("\\\\", "\\")});
                            }
                            else
                            {
                                long fs = new File(element).length();
                                totalSize+=fs;
                                actions.add(new String[]{statusMessage,"copyfile2",fs+"",element,(target+"\\"+element_r).replace("\\\\", "\\")});
                            }
                        }
                    break;
                }
            }
            if (mixed)
            {
                totalSize = actions.size();
            }
            
            final int pbarbefore = j.getValue();
            final int totalSizeToProgressBarDivisor = (int) (totalSize / checkpoints_main[i+1]-checkpoints_main[i]);
            
            int installedSize=0;
            
            //eigentliches setup
            for (String[] actionLines : actions)
            {
                int sizetoAdd=1;
                statusLine.setText(actionLines[0]);
                if(!actionLines[1].equals("copyfile2"))
                {
                    logPane.append(actionLines[0]+"\n");
                }
                boolean largerFile=false;
                //program,copydir,copyfile,mkdir -> internal use only,copyfile2 -> internal use only
                switch(actionLines[1])
                {
                    case "program":
                    Tools.runCmdWithOutToTextField(logPane, Arrays.copyOfRange(actionLines, 2, actionLines.length-1));//die internen metadaten killen
                    break;
                    case "copyfile":
                    case "copyfile2":
                        try
                        {
                            if(Integer.valueOf(actionLines[2])>100000)
                            {
                                largerFile=true;
                            }
                            if(largerFile&&subProgress!=null)
                            {
                                subProgress.setVisible(true);
                            }
                            String logExisting = logPane.getText();
                            String progressLine = "Kopiere %s (%s %)"; //dateiname und fortschritt
                            int fs=0;
                            logPane.setText(logExisting+"\n"+String.format(progressLine, FileTK.getFileName(actionLines[3]),fs));
                            byte[] cp = new byte[4096]; //copybuffer
                        
                            FileInputStream fis = new FileInputStream(actionLines[3]);
                            FileOutputStream fos = new FileOutputStream(actionLines[4]);
                            int bytesDone=0,bytesTotal=Integer.valueOf(actionLines[2]);
                            int x= fis.read(cp);
                            while(x >-1)
                            {
                                fos.write(cp, 0, x);
                                bytesDone+=x;
                                double progress=bytesDone/bytesTotal;
                                if(fs+1==((int)progress))
                                {
                                    fs++;
                                    logPane.setText(logExisting+"\n"+String.format(progressLine, FileTK.getFileName(actionLines[3]),fs));
                                    //PBAR!!
                                }
                                x= fis.read(cp);
                            }
                        }
                        catch (FileNotFoundException ex)
                        {
                            ex.printStackTrace();
                        }
                        catch (IOException ex)
                        {
                            ex.printStackTrace();
                        }
                        
                        
                    break;
                    case "mkdir":
                        FileTK.ensuredirExistence(new File(actionLines[2]+"\\nope.null"));//erlaubt auch dateien in vorhandene strukturen zu patchen
                    sizetoAdd=0;
                    break;
                }
                
               
                installedSize+=sizetoAdd;
                final int cInstalledSize = installedSize;
                EventQueue.invokeLater(new Runnable()
                {
                    public void run()
                    {
                        j.setValue(pbarbefore + (cInstalledSize / totalSizeToProgressBarDivisor));
                    }
                });
                
            }
            
            
            i++;
        }
    }
    
    
    private List<List<String>> getElement(String metaKey)
    {
        List<List<String>> ret = new ArrayList<>();
        Map<String, List<String>> rawData = (Map<String, List<String>>) iZsetupScript.get(metaKey);
        if (rawData == null)
        {
            Messagers.SingleLineMsg("Package " + metaKey + " nicht deklariert! Error 0x0005414D", "Okay");
            System.exit(0x0005414D);
        }
        if (rawData.containsKey("Condition"))
        {
            if (rawData.containsKey("preCond"))
            {
                String fid = rawData.get("preCond").get(0);
                ret.addAll(getElement(fid));
            }//else:nope
            
            boolean conditionResult = SharedUtils.checkCondition(rawData.get("Condition").get(0));
            if (conditionResult)
            {
                if (rawData.containsKey("ifnode"))
                {
                    String fid = rawData.get("ifnode").get(0);
                    ret.addAll(getElement(fid));
                }//else:nope
            }
            else
            {
                if (rawData.containsKey("elsenode"))
                {
                    String fid = rawData.get("elsenode").get(0);
                    return getElement(fid);
                }//else:nope
            }
            if (rawData.containsKey("postCond"))
            {
                String fid = rawData.get("postCond").get(0);
                ret.addAll(getElement(fid));
            }//else:nope
        }
        else
        {
            Set<String> lk = rawData.keySet();
            for (String id : lk)
            {
                if(!id.toLowerCase().startsWith("comment:"))
                {
                    ret.add(rawData.get(id));
                }
            }
        }
        return ret;
    }
}
