
// ZUtils.java
// Andrew Davison, ad@coe.psu.ac.th, Nov. 2022

/* Helper functions for zt-exec
   (ZeroTurnaround Process Executor):
      https://github.com/zeroturnaround/zt-exec

   API Docs:
    https://javadoc.io/static/org.zeroturnaround/zt-exec/1.8/index.html?org/zeroturnaround/exec/ProcessExecutor.html


*/
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
import java.nio.file.*;

import javax.swing.*;
import java.awt.image.*;
import javax.imageio.*;

import org.zeroturnaround.exec.*;
import org.apache.commons.csv.*;



public class ZUtils
{
  // used by getOSType()
  public static final int WINDOWS = 0;
  public static final int LINUX = 1;
  public static final int MAC = 2;
  public static final int OTHER = 3;



  // --------- common zt-exec use-cases ----------------


  public static String cmdOut(String... args)
  // run the command and return any output as a string;
  // errors are printed to stdout
  {
    // System.out.println("Executing " + Arrays.toString(args));
    try {
      return new ProcessExecutor().command(args)
            .exitValueNormal()     // only allow normal exit
            .readOutput(true)
            .execute().outputUTF8();
    }
    catch (InvalidExitValueException e) {
      System.out.println("Error (" + e.getExitValue() + 
                 "): " + e.getResult().outputUTF8());
    }
    catch(Exception e)
    { System.out.println(e.getMessage()); }
    return null;
  }  // end of cmdOut()



  public static int cmdExit(String... args)
  // run the command and return its exit value only
  {
    // System.out.println("Executing " + Arrays.toString(args));
    try {
      return new ProcessExecutor().command(args)
                   .execute().getExitValue();
    }
    catch(Exception e) { }
    return 1;
  }  // end of cmdExit()


/*
  public static boolean cmdRun(String... args)
  // run the command, print any errors, and return true/false
  {
    // System.out.println("Executing " + Arrays.toString(args));
    try {
      new ProcessExecutor().command(args)
              .exitValueNormal().readOutput(true).execute();
      return true;
    }
    catch (InvalidExitValueException e) {
      System.out.println("Exit Error (" + e.getExitValue() + 
                 "): " + e.getResult().outputUTF8());
    }
    catch(Exception e) 
    {   System.out.println(e);  }
    return false;
  }  // end of cmdRun()
*/

  
  // ----------------- CSV functions ----------------
  // Uses Apache Commons CSV (https://commons.apache.org/proper/commons-csv/)


  public static TreeMap<String,String> buildMap(String csvStr)
  /* Assume that the CSV data consists of two rows -- 
     the first holds the headers, and the second the data.
     Build a map using the headers as keys pointing to the data.
  */
  {
    // extract headers and record fields
    List<String> headers = getCSVHeaders(csvStr);
    int numHeaders = headers.size();

    CSVRecord rec = getCSVRecord(csvStr);
    List<String> fields = rec.toList();
    int numFields = fields.size();

    if (numHeaders != numFields) {
      System.out.println("Mismatch in no. of headers (" +
             numHeaders + ") and fields (" + numHeaders + ")");
      return null;
    }

    // build map of headers --> fields
    TreeMap<String,String> map = new TreeMap<>();
    for(int i=0; i < numFields; i++)
      map.put(headers.get(i), fields.get(i));
    return map;
  }  // end of buildMap()


  public static void printMap(TreeMap<String,String> map)
  {
    int count = 1;
    System.out.println("Map: -------------------------------\n");
    for (String header: map.keySet()) {
      System.out.println(count + ". " + header + ": " + 
                                        map.get(header) + "\n");
      count++;
    }
    System.out.println("--------------------------------------");

  }  // end of printMap()


  public static CSVRecord getCSVRecord(String csvStr)
  /* Assume that the CSV data consists of two rows -- 
     the first holds the headers. and the second the data
  */
  {
    try {
      Reader in = new StringReader(csvStr);
    
      CSVFormat.Builder builder = CSVFormat.DEFAULT.builder();
      CSVFormat format = builder.setHeader().
                           setSkipHeaderRecord(true).
                           setTrim(true).build();
      CSVParser parser = new CSVParser(in, format);
    
      List<CSVRecord> recs = parser.getRecords();
      return recs.get(0);   // all the data in one row
    }
    catch(IOException e)
    { System.out.println(e);  }

    return null;
  }  // end of getCSVRecord()


  public static List<String> getCSVHeaders(String csvStr)
  {
    try {
      Reader in = new StringReader(csvStr);
    
      CSVFormat.Builder builder = CSVFormat.DEFAULT.builder();
      CSVFormat format = builder.setHeader().
                           setSkipHeaderRecord(true).
                           setTrim(true).build();

      CSVParser parser = new CSVParser(in, format);
      return parser.getHeaderNames();
    }
    catch(IOException e)
    { System.out.println(e);  }

    return null;
  }  // end of getCSVHeaders()



  // ------------------- temp files -------------------------



  public static String writeToTemp(String[] lines)
  // write lines of text to a temporay file, and return the
  // file name
  {
	try {
      File f = File.createTempFile("temp", ".txt");
      f.deleteOnExit();   // Delete when JVM exits
      String path = f.getAbsolutePath();

      BufferedWriter bw = new BufferedWriter(new FileWriter(f));
      for(String line : lines) {
        bw.write(line);
        bw.newLine();
      }
      bw.close();

      return path;
	}
	catch (IOException e) 
    {  System.out.println("Could not create temp file");  }

    return null;
  }  // end of writeToTemp()



  public static String makeTemp(String ext)
  // create a temporary file with the given extension,
  // and return the file name
  {
	try {
      File f = File.createTempFile("temp", ext);
      f.deleteOnExit();   // Delete when JVM exits
      return f.getAbsolutePath();
	}
	catch (IOException e) 
    {  System.out.println("Could not create temp file with extension " + ext);  }
    return null;
  }  // end of writeToTemp()


  // ----------------------- system info ---------------------

  public static int getOSType() 
  // return an integer representing the probable OS type
  {
    String OS = System.getProperty("os.name").toLowerCase();
    if (OS.contains("mac") || OS.contains("darwin"))
      return MAC;
    else if (OS.contains("wind"))
      return WINDOWS;
    else if (OS.contains("nix") || OS.contains("nux") ||
             OS.contains("aix"))
      return LINUX;
    
    System.out.println("Unknown OS type");
    return OTHER;
  }  // end of getOSType()


  public static double getOSVersion() 
  {
    String verStr = System.getProperty("os.version").trim();
    try {
      return Double.parseDouble(verStr);
    }
    catch (NumberFormatException e)
    { System.out.println("Could not extract OS version as a double");  }
    return -1;
  }  // end of getOSVersion()


  public static boolean inDir(String dirNm, String appNm)
  // is the appNm part of a file / subdirectory name
  // located in the dirNm dir?
  {
    String nm = appNm.toLowerCase();

    File dir = new File(dirNm);
    String contents[] = dir.list();

    for(int i=0; i < contents.length; i++)
      if (contents[i].toLowerCase().contains(nm))
        return true;

    return false;
  }  // end of inDir()


  // ----------------- file IO ------------------------------

  public static BufferedImage loadImage(String fnm)
  {
    try {
      return ImageIO.read(new File(fnm));
    }
    catch(Exception e) {
      System.out.println("Cannot load " + fnm + "\n");
      return null;
    }
  }  // end of loadImage()


  public static String stripExt(String fnm)
  {
    int dotPos = fnm.lastIndexOf('.');
    if (dotPos == -1)
      return fnm;
    else
      return fnm.substring(0, dotPos);
  }


  public static String getName(String fnm)
  // remove path and extension from filename
  {
    Path p = Paths.get(fnm);
    String fileName = p.getFileName().toString();
    return stripExt(fileName);
  }  // end of getName()

}  // end of ZUtils class