
import ch.psi.pshell.core.JsonSerializer;
import ch.psi.pshell.epics.PsiCamera;
import ch.psi.utils.IO;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.logging.Level;
import javax.swing.DefaultComboBoxModel;

public class SfCamera extends PsiCamera{

    static String configFolder = "/afs/psi.ch/intranet/SF/Applications/config/camtool_n";
    
    public static String getConfigFolder() {
        return configFolder;
    }
    
    public static void setConfigFolder(String value) {
         configFolder = value;
    }
    
    public static ArrayList<String> getCameras() {
        ArrayList<String> ret = new ArrayList<>();
        File[] cameraConfigFiles = new File[0];
        cameraConfigFiles = IO.listFiles(configFolder, new String[]{"json"});
        Arrays.sort(cameraConfigFiles, (a, b) -> a.compareTo(b));
        DefaultComboBoxModel model = new DefaultComboBoxModel();
        for (File file : cameraConfigFiles) {
            String prefix = IO.getPrefix(file);
            if (!prefix.startsWith("#") && !prefix.endsWith("_parameters")) {
                ret.add(prefix);
            }
        }
        return ret;
    }
    
    public static class CameraConfig {

        public HashMap camera;

        public HashMap getCalibration() {
            return (HashMap) camera.get("calibration");
        }

        public ArrayList<Integer> getCalibrationRefMarker() {
            return (ArrayList<Integer>) getCalibration().get("reference_marker");
        }

        public double getCalOffsetX() {
            ArrayList<Integer> calibration = getCalibrationRefMarker();
            double ret = -(calibration.get(0) + calibration.get(2)) / 2;
            return ret;
        }

        public double getCalOffsetY() {
            ArrayList<Integer> calibration = getCalibrationRefMarker();
            double ret = -(calibration.get(1) + calibration.get(3)) / 2;
            return ret;
        }

        public double getScaleX() {
            ArrayList<Integer> calibration = getCalibrationRefMarker();
            double width = Math.abs(calibration.get(2) - calibration.get(0));
            return getCalibrationWidth() / width;
        }

        public double getScaleY() {
            ArrayList<Integer> calibration = getCalibrationRefMarker();
            double height = Math.abs(calibration.get(3) - calibration.get(1));
            return getCalibrationHeight() / height;
        }

        public Double getCalibrationHeight() {           
            Double ret = (Double) getCalibration().get("reference_marker_height");
            return (ret == null) ? 100.0 : ret;
        }

        public Double getCalibrationWidth() {
            Double ret =  (Double) getCalibration().get("reference_marker_width");
            return (ret == null) ? 100.0 : ret;
        }

        public Double getCalibrationHorizontalAngle() {
            return (Double) getCalibration().get("horizontal_camera_angle");
        }

        public Double getCalibrationVerticalAngle() {
            return (Double) getCalibration().get("vertical_camera_angle");
        }

        public boolean getMirrorX() {
            Boolean ret = (Boolean) camera.get("mirror_x");
            return (ret == null) ? false : ret;
        }

        public boolean getMirrorY() {
            Boolean ret = (Boolean) camera.get("mirror_y");
            return (ret == null) ? false : ret;
        }

        public int getRotate() {
            Integer ret = (Integer) camera.get("rotate");
            return (ret == null) ? 0 : ret;
        }

        public ArrayList<Integer> getRoi() {
            return (ArrayList<Integer>) camera.get("roi");
        }

        public Boolean getRoiEnable() {
            if ((camera.get("roi_enable") == null) || (camera.get("roi") == null)) {
                return false;
            }
            return (Boolean) camera.get("roi_enable");
        }
    }
    
    public static class CameraPars {

        public HashMap parameter;

        public Boolean getBackgroundSubtraction() {
            if (parameter.get("background_subtration") == null){
                return false;
            }
            return (Boolean) parameter.get("background_subtration");
        }
        
        public ArrayList<Integer> getRoi() {
            return (ArrayList<Integer>) parameter.get("region_of_interest");
        }
        
        public Boolean getRoiEnable() {
            ArrayList<Integer> roi = getRoi();
            return ((roi != null) && (roi.size()>=4));
        }        
        
        public HashMap getGoodRegion() {
            return (HashMap) parameter.get("good_region");
        }
        
        public Double getThresholde() {
            return (Double) parameter.get("threshold");
        }
        
               
    }
    
    
    CameraConfig setup;
    String json;
    public final String prefix;
    
    public SfCamera(String name, String prefix)  {
        super(name, prefix);
        this.prefix = prefix;
    }
    
    public String getJson(){
        return json;
    }

    public CameraConfig getSetup(){
        return setup;
    }
    
    @Override
    protected void doInitialize() throws IOException, InterruptedException{
        super.doInitialize();
        Path configFile = Paths.get(configFolder, prefix + ".json");        
        if (!configFile.toFile().exists()) {
            throw new IOException("Cannot open camera config file: " + configFile.toFile());
        }        
        json = new String(Files.readAllBytes(configFile));
        setup = (CameraConfig) JsonSerializer.decode(json, CameraConfig.class);
        
        CameraPars cameraPars = null;
        getConfig().roiX = 0;    getConfig().roiY = 0;
        getConfig().roiWidth = -1;   getConfig().roiHeight =-1;

        try{
            Path parsFile = Paths.get(configFolder, prefix + "_parameters.json");
            if (parsFile.toFile().exists()){
                String cameraParsJson = new String(Files.readAllBytes(parsFile));
                cameraPars = (CameraPars) JsonSerializer.decode(cameraParsJson, CameraPars.class);
                if (cameraPars.getRoiEnable()){
                    getConfig().roiX = cameraPars.getRoi().get(0);
                    getConfig().roiY = cameraPars.getRoi().get(2);
                    getConfig().roiWidth = cameraPars.getRoi().get(1);
                    getConfig().roiHeight = cameraPars.getRoi().get(3);
                }

            }
        } catch (Exception ex){
            getLogger().log(Level.WARNING, null, ex);
        }

        getConfig().flipHorizontally = setup.getMirrorX();
        getConfig().flipVertically = setup.getMirrorY();
        //getConfig().rotation = config.getRotate();
        //getConfig().rotationCrop = true;
        switch (setup.getRotate()) {
            case 1:
                getConfig().rotation = 270;
                break;
            case 2:
                getConfig().rotation = 180;
                break;
            case 3:
                getConfig().rotation = 90;
                break;
        }
        getConfig().rotationCrop = false;                
        try {
            getConfig().spatialCalOffsetX = setup.getCalOffsetX();
            getConfig().spatialCalOffsetY = setup.getCalOffsetY();
        } catch (Exception ex) {
            getConfig().spatialCalOffsetX = 0.0;
            getConfig().spatialCalOffsetY = 0.0;          
        }
        try {
            getConfig().spatialCalScaleX = -setup.getScaleX();
            getConfig().spatialCalScaleY = -setup.getScaleY();                    
        } catch (Exception ex) {            
            getConfig().spatialCalScaleX = 1.0;
            getConfig().spatialCalScaleY = 1.0;
        }
        getConfig().save();            
    }
    
}
