/*
 * 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));
            System.out.println("############################\n"+iZsetupScript+"\n");
            List<String> rootPackages = (List<String>) iZsetupScript.get("Segments");
            for (String id : rootPackages)
            {
                pkg.put(id, getElement(id));
            }
        }
        catch (FileNotFoundException ex)
        {
            ex.printStackTrace();
        }
    }
    
    @Override
    public void doInstall()
    {
        doInstall(new JProgressBar(0,1000),null,new JLabel(),new JTextArea());
    }
    
    private double segmentSize = 1.0;
    private int installedSize;
    private int[] checkpoints_main;
    private int i_g;
    private long totalSize;
    private double fs;
    //private List<String> lmaa2;
    @Override
    public void doInstall(final JProgressBar pbarTotal,JProgressBar subProgress,JLabel statusLine,JTextArea logPane)
    {
        pbarTotal.setValue(0);
        pbarTotal.setMaximum(1000);
        DefaultCaret caret = (DefaultCaret) logPane.getCaret();
        caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
        checkpoints_main = new int[pkg.size()+1];
        int chunks = checkpoints_main.length-1;
        checkpoints_main[0]=0;
        //progressbar_chunks
        Tools.DebugHelperPrint(chunks+"", true, "iZsetup.enableFinerDebug");
        for (int i = 0; i < chunks; i++)
        {
            checkpoints_main[i+1]=(1000/chunks)*(i+1);
        }
        Set<String> chunkIDs = pkg.keySet();
        //int i=0;
        for (int i=0;i<chunkIDs.size();i++)
        {
            i_g=i;
            String id = chunkIDs.toArray(new String[]{})[i];
            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];
            System.out.println("baseDir=="+Main.baseDir+"\n");
            totalSize=0;
            for (List<String> actionRaw : pkg.get(id))
            {
                System.out.println(actionRaw+"\n");
                switch(actionRaw.get(1))
                {
                    
                    case "program"://intended slipthru
                        actions.add(actionRaw.toArray(type));
                        mixed=true;
                    break;
                    case "copyfile":
                        String fn = Tools.replaceHashmarkedVars(actionRaw.get(2), "basedir", Main.baseDir);
                        String fsi = new File(fn).length()+"";
                        actions.add(new String[]{actionRaw.get(0),"copyfile",fsi,fn,(actionRaw.get(3))});
                        break;
                    case "copydir":
                        String indir = Tools.replaceHashmarkedVars(actionRaw.get(2), "basedir", Main.baseDir);
                        String statusMessage = "Kopiere neue Dateien für "+actionRaw.get(0);
                        String target = actionRaw.get(3);
                        String[] filesToCopy = FileTK.walkDirectoryRecursively(indir);
                        for (String element : filesToCopy)
                        {
                            element = Tools.replaceHashmarkedVars(element, "basedir", Main.baseDir);
                            element = Tools.processEnvVarsinLine(element);
                            //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("\\\\", "\\")});
                            }
                        }
                        int lmaa=1;
                    break;
                }
            }
            if (mixed)
            {
                totalSize = actions.size();
            }
            Tools.DebugHelperPrint("Chunk"+i+"totalSize:"+totalSize, true, "iZsetup.enableCoreDebug");
            Tools.DebugHelperPrint("checkpointValue"+checkpoints_main[i], true, "iZsetup.enableCoreDebug");
            //pbarbefore = pbarTotal.getValue();
            segmentSize = (checkpoints_main[i+1]-checkpoints_main[i]);
            Tools.DebugHelperPrint("antidivbyzero:"+(checkpoints_main[i+1]-checkpoints_main[i]), true, "iZsetup.enableCoreDebug");
            installedSize=0;
            
            //eigentliches setup
            for (String[] actionLines : actions)
            {
                if(subProgress!=null)
                {
                    subProgress.setVisible(false);
                }
                int sizetoAdd=1;
                //List<String> lmaa = Arrays.asList("");
                //List<String> lmaa2= new ArrayList<>();
                
                for (int j = 0; j < actionLines.length; j++)
                {
                    actionLines[j]=Tools.replaceHashmarkedVars(actionLines[j], "basedir", Main.baseDir);
                    actionLines[j]=Tools.processEnvVarsinLine(actionLines[j]);
                }
               /*for (String x : lmaa)
                {
                    String y = Tools.replaceHashmarkedVars(x, "basedir", Main.baseDir);
                    lmaa2.add(y);
                }*/
               // lmaa.forEach(x -> {);
                //actionLines = lmaa2.toArray(actionLines);
                statusLine.setText(actionLines[0]);
                if(!(actionLines[1].equals("copyfile2")||actionLines[1].equals("mkdir")))
                {
                    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));//die internen metadaten killen
                    break;
                    case "copyfile":
                    case "copyfile2":
                        FileInputStream fis=null;
                        FileOutputStream fos=null;
                        Tools.DebugHelperPrint(Arrays.toString(actionLines), true, "iZsetup.enableCoreDebug");
                        try
                        {
                            if(Integer.valueOf(actionLines[2])>1000000)
                            {
                                largerFile=true;
                            }
                            if(largerFile&&subProgress!=null)
                            {
                                subProgress.setVisible(true);
                                subProgress.setMinimum(0);
                                subProgress.setMaximum(1000);
                                subProgress.setValue(0);
                            }
                            String logExisting = logPane.getText();
                            String progressLine = "Kopiere %s (%s %%)"; //dateiname und fortschritt
                            fs=0;
                            logPane.setText(logExisting+"\n"+String.format(progressLine, FileTK.getFileName(actionLines[3]),(int)fs));
                            byte[] cp = new byte[4096]; //copybuffer
                        
                            fis = new FileInputStream(actionLines[3]);
                            FileTK.ensuredirExistence(actionLines[4]);
                            Tools.DebugHelperPrint("file_totals"+actionLines[2], true, "iZsetup.enableCoreDebug");
                            fos = new FileOutputStream(actionLines[4]);
                            int bytesDone=0,bytesTotal=Integer.valueOf(actionLines[2]);
                            int x= fis.read(cp);
                            int bytesWrittenSinceLastUpdate=0;
                            while(x >-1)
                            {
                                fos.write(cp, 0, x);
                                bytesDone+=x;
                                //double progress=bytesDone/bytesTotal;
                                if(bytesTotal>1000000 && bytesWrittenSinceLastUpdate>100000 && bytesWrittenSinceLastUpdate>bytesTotal/1000)
                                {
                                    bytesWrittenSinceLastUpdate=0;
                                    fs=((double)bytesDone/(double)bytesTotal)*1000;
                                    logPane.setText(logExisting+"\n"+String.format(progressLine, FileTK.getFileName(actionLines[3]),(int)fs/10));
                                    if(subProgress!=null)
                                    {
                                        EventQueue.invokeLater(() ->
                                        {
                                            Tools.DebugHelperPrint("cprogress|"+fs, true, "iZsetup.enableProgressDebug");
                                            subProgress.setValue((int)fs);
                                        });
                                        
                                    }
                                    //PBAR!!
                                }
                                bytesWrittenSinceLastUpdate+=x;
                                x= fis.read(cp);
                            }
                            logPane.setText(logExisting+"\n"+String.format(progressLine, FileTK.getFileName(actionLines[3]),100));
                            if(!mixed)
                            {
                                sizetoAdd = bytesTotal;
                            }
                            
                        }
                        catch (FileNotFoundException ex)
                        {
                            ex.printStackTrace();
                        }
                        catch (IOException ex)
                        {
                            ex.printStackTrace();
                        }
                        finally
                        {
                            try
                            {
                                fis.close();
                                fos.close();
                            }
                            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(() ->
                {
                    pbarTotal.setValue((int) (checkpoints_main[i_g] + ((double)installedSize / (double)totalSize)*segmentSize));
                });
                
            }
            
            EventQueue.invokeLater(() ->
                {
                    pbarTotal.setValue(checkpoints_main[i_g]);
                });
            //i++;
        }
        EventQueue.invokeLater(() ->
                {
                    pbarTotal.setValue(1000);
                });
    }
   
    
    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;
    }
}
