/*
 * Copyright (c) 2014 Paul Scherrer Institute. All rights reserved.
 */

import ch.psi.pshell.core.Context;
import ch.psi.pshell.ui.Importer;
import ch.psi.pshell.ui.Plugin;
import ch.psi.utils.Str;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 *
 */
public class importer implements Plugin {
    public static class MyImporter implements Importer{
        StringBuffer sb;
        @Override
        public String getDescription() {
            return "X-Treme scan files";
        }

        @Override
        public String[] getExtensions() {
            return new String[]{"txt"};
        }
        
        List<String>[] getBlocks(List<String> lines){
            ArrayList<List<String>> ret = new  ArrayList<>();
            
            List<String> block = new ArrayList<>();
            for (String line:lines){
                line = line.trim();
                if (!line.isEmpty()){
                    block.add(line);
                    if (line.equals("start")){
                        ret.add(block);
                        block = new  ArrayList<>();
                    }
                }
            }
            if (block.size()>0){
                ret.add(block);
            }            
            return ret.toArray(new List[0]);
        }
        
        String getRanges(String fileName) throws Exception{
            File file = new File(fileName);
            if (!file.exists()){
                file = Paths.get(Context.getInstance().getSetup().getScriptPath(), fileName).toFile();
                if (!file.exists()){
                   throw new Exception("Invalid ranges file: " + fileName);
                }
            }
            StringBuffer ret = new StringBuffer();         
            ret.append("ranges = [");
            for (String str : Files.readAllLines(file.toPath())){
                str = str.trim();
                str = Str.removeMultipleSpaces(str);
                String[] tokens = str.split(" ");
                if (tokens.length ==  3){
                    ret.append("(").append(tokens[0]).append(",").append(tokens[1]).append(",").append(tokens[2]).append("),");
                }
            }
            ret.append("]");
            return ret.toString();
        }
        
        String[] getTokens(String line){
            return line.replaceAll("\\s+", " ").split(" ");//remove multiple spaces
        } 
        
        HashMap<String, String> getPars(String[] tokens){
            HashMap<String, String> pars =  new  HashMap<>();
            for (String par : new String[]{"off", "delay", "repeat", "file", "ppe"}){
                for (int i = 1; i<tokens.length-1; i++){                            
                    if (tokens[i].equals(par)){
                        pars.put(par, tokens[i+1]);
                        break;
                    }
                }
            }                    
            return pars;
        }
        
        boolean waitTemp = false;
        boolean refill1K = false;
        boolean noBeamCkeck = false;
        boolean waitInj = false;
        String fileName = null;
        void parse(List<String> block, int repeat){
            String ident = "";
            if (repeat > 1){
                ident = "    ";
                sb.append("for i in range(").append(repeat).append("):\n");
            }
            String scanCommand = null;
            
            String atEnd = null;
            for (String line:block){
                try{
                    String[] tokens = getTokens(line);
                    HashMap<String, String> pars = getPars(tokens);                    
                    String delayStr = pars.get("delay") != null ? ", delay=" + pars.get("delay") : "";
                    System.out.println("----");
                    System.out.println(line);
                    System.out.println(pars);
                    
                    switch (tokens[0]){
                        case "set":
                            switch (tokens[1]){
                                case "Hx":
                                    sb.append(ident).append("set_hx(").append(tokens[2]).append(")").append("\n");
                                    continue;
                                case "Hz":
                                    sb.append(ident).append("set_hz(").append(tokens[2]).append(")").append("\n");
                                    continue;
                                case "pol":
                                    String offset = pars.get("off") != null ? pars.get("off") : "None";
                                    switch (tokens[2]){                                        
                                        case "c+":                                                                                        
                                            sb.append(ident).append("set_pol_cplus(offset = ").append(offset).append(")").append("\n");
                                            continue;
                                        case "c-":                                            
                                            sb.append(ident).append("set_pol_cminus(offset = ").append(offset).append(")").append("\n");
                                            continue;
                                        case "pol":                                            
                                            sb.append(ident).append("set_pol_lin(angle = ").append(tokens[3]).append(", offset = ").append(offset).append(")").append("\n");
                                            continue;
                                    }                                    
                                    continue;
                                case "hor":
                                    sb.append(ident).append("sample_hor.move(").append(tokens[2]).append(")").append("\n");
                                    continue;                                    
                                case "vert":
                                    sb.append(ident).append("sample_vert.move(").append(tokens[2]).append(")").append("\n");
                                    continue;                                    
                                case "rot":
                                    sb.append(ident).append("sample_rot.move(").append(tokens[2]).append(")").append("\n");
                                    continue;                                    
                                case "needle":
                                    sb.append(ident).append("sample_vert.write(").append(tokens[2]).append(")").append("\n");
                                    continue;                                    
                                case "temp":
                                    sb.append(ident).append("set_temp(").append(tokens[2]).append(delayStr).append(")").append("\n");
                                    continue;                                    
                                case "E":
                                    sb.append(ident).append("set_energy(").append(tokens[2]).append(delayStr).append(")").append("\n");
                                    continue;                                    
                                case "cff":
                                    sb.append(ident).append("set_cff(").append(tokens[2]).append(")").append("\n");
                                    continue;
                                case "slit":
                                    sb.append(ident).append("set_slit(").append(tokens[2]).append(")").append("\n");
                                    continue;
                                case "har":
                                    sb.append(ident).append("set_har(").append(tokens[2]).append(")").append("\n");
                                    continue;
                                case "volt":
                                    sb.append(ident).append("set_volt(").append(tokens[2]).append(", ").append(tokens[3]).append(delayStr).append(")").append("\n");
                                    continue;
                                case "FE":
                                    sb.append(ident).append("set_fe(").append(tokens[2]).append(")").append("\n");
                                    continue;
                            }
                            break;        
                        case "open":
                            sb.append(ident).append("open_valve(").append(delayStr.substring(2)).append(")").append("\n");
                            continue;                                    
                        case "close":
                            sb.append(ident).append("close_valve()").append("\n");
                            continue;                                    
                        case "range":
                            sb.append(ident).append("set_range(").append(tokens[1]).append(", ").append(tokens[2]).append(")\n");
                            continue;                                    
                        case "wait_temp":
                            waitTemp = true;
                            continue;                                    
                        case "refill1K":
                            refill1K = true;
                            continue;            
                        case "no_beam_check":
                            noBeamCkeck = true;
                            continue;          
                        case "wait_inj":
                            waitInj = true;
                            continue;          
                        case "file":
                            fileName = tokens[1];                            
                            continue;                                                                    
                        case "at_end":
                            atEnd = tokens[1]+"()";
                            continue;  
                        case "otf":
                        case "hyst":
                        case "scan":
                            System.out.println("GOT");
                            if (scanCommand!=null){
                                throw new Exception("Two scan commands in a single block");
                            }
                            scanCommand = line;                            
                            continue;  
                    }
                    sb.append("# TODO: ").append(line).append("\n");                 
                } catch (Exception ex){
                    sb.append("# Exception: ").append(ex.getMessage()).append( " in ").append(line).append("\n");                 
                }                
            }
            
            if (scanCommand!=null){
                try{
                    if (waitTemp){
                        sb.append(ident).append("wait_temp()").append("\n");
                    }
                    if (refill1K){
                        sb.append(ident).append("refill_1k()").append("\n");
                    }
                    if (waitInj){
                        sb.append(ident).append("wait_inj()").append("\n");
                    }
                    if (!noBeamCkeck){
                        sb.append(ident).append("wait_beam()").append("\n");
                    }
                    if (fileName!=null){
                        sb.append(ident).append("set_file('").append(fileName).append("')\n");
                    }                            

                    String[] tokens = getTokens(scanCommand);
                    HashMap<String, String> pars = getPars(tokens);                                    
                    String delayStr = pars.get("delay") != null ? ", delay=" + pars.get("delay") : "";
                    switch (tokens[0]){
                        case "otf":                            
                            String init = "start=" + tokens[1] + ", ";
                            String finish = "end=" + tokens[2] +
                                    ", ";
                            String time = "time=" + tokens[3];
                            sb.append(ident).append("otf(").append(init).append(finish).append(time).append(delayStr).append(")\n");
                            break;
                        case "hyst":
                            String ppe = "";
                            if (pars.containsKey("ppe")){
                                //TODO
                                scanCommand = scanCommand.substring(0, scanCommand.lastIndexOf("ppe")).trim();
                                ppe = ", ppe=" + pars.get("ppe");
                            }
                            String energies = "energies=[" + scanCommand.substring(scanCommand.lastIndexOf(" E ") + 3).trim().replace(" ", ", ") + "]" + ppe;                            
                            tokens = getTokens(scanCommand.substring(0, scanCommand.lastIndexOf(" E ")).trim());
                            String field;
                            switch (tokens[1]){
                                case "cont":
                                    field = "field=" + tokens[2] + ", ";
                                    if (tokens.length == 4){
                                        String file = tokens[3];
                                        sb.append(ident).append("hyst_cont_mult(").append(field).append(getRanges(file)).append(", ").append(energies).append(")\n");
                                    } else {
                                        String start = "init_field=" + tokens[3] + ", ";
                                        String end = "final_field=" + tokens[4] + ", ";
                                        String ramp = "ramp_speed=" + tokens[5] + ", ";
                                        sb.append(ident).append("hyst_cont(").append(field).append(start).append(end).append(ramp).append(energies).append(")\n");
                                    }
                                    break;
                                case "step":
                                    String forward = tokens[2].equals("for") ? "forward = True, ": "forward = False, ";
                                    field = "field=" + tokens[3] + ", ";
                                    if (tokens.length == 5){
                                        String file = tokens[4];
                                        sb.append(ident).append("hyst_step_mult(").append(forward).append(field).append(getRanges(file)).append(", ").append(energies).append(")\n");
                                    } else {
                                        String start = "init_field=" + tokens[4] + ", ";
                                        String end = "final_field=" + tokens[5] + ", ";
                                        String ramp = "ramp_speed=" + tokens[6] + ", ";
                                        sb.append(ident).append("hyst_step(").append(forward).append(field).append(start).append(end).append(ramp).append(energies).append(")\n");
                                    }
                                    break;
                                default:
                                    throw new Exception("Invalid scan command type");                                
                            }
                            break;
                        case "scan":
                            switch (tokens[1]){
                                case "E":
                                    if (tokens.length == 3){                                        
                                        String file = tokens[2];
                                        sb.append(ident).append("scan_e_mult(").append(getRanges(file)).append(")\n");
                                    } else {
                                        sb.append(ident).append("scan_e(").append(tokens[2]).append(", ").append(tokens[3]).append(", ").append(tokens[4]).append(")\n");
                                    }
                                    break;
                                case "V":
                                    sb.append(ident).append("scan_v(").append(tokens[2]).append(", ").append(tokens[3]).append(", ").append(tokens[4]).append(", ").append(tokens[5]).append(")\n");
                                    break;
                                default:
                                    throw new Exception("Invalid scan command type");
                            }
                            break;                    
                        default:
                            throw new Exception("Invalid scan command type");
                    }                                        
                } catch (Exception ex){
                    sb.append("# Exception: ").append(ex.getMessage()).append( " in ").append(scanCommand).append("\n");       
                }
            }            
            
            if (atEnd!=null){
                sb.append(atEnd).append("\n");     
            }                            
        }
        
        @Override
        public String importFile(File file) throws Exception {    
            waitTemp = false;
            refill1K = false;
            noBeamCkeck = false;
            waitInj = false;
            fileName = null;
            
            sb = new StringBuffer();            
            List<String>[] blocks = getBlocks(Files.readAllLines(file.toPath()));
            for (List<String> block : blocks){
                for (String line:block){
                    sb.append("#").append(line).append("\n");                    
                }   
                //Execution block
                if (block.size()>=2) {
                    String[] tokens = getTokens(block.get(block.size()-1));
                    if (tokens[0].equals("start")){                        
                        HashMap<String, String> pars = getPars(tokens);                    
                        if (pars.get("file")!=null){
                            sb.append("# TODO: 'start file' not supported").append("\n");  
                        }
                        int repeat = 1;
                        try{                        
                            repeat = Integer.valueOf( pars.get("repeat"));
                        }catch (Exception ex){                        
                        }
                        
                        block.remove(block.size()-1);
                        parse(block, repeat);
                    }
                }
                sb.append("\n");
            }
            return sb.toString();
        }
        
    }
    @Override
    public void onStart() {        
        Importer.addServiceProvider(MyImporter.class);
    }    
}
