package basics.utl;

import java.awt.Dimension;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Properties;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import basics.testing.Units;

/**
 * @author kst
 */
public class SysUtl
{
public static final String   NEWLINE          = System.getProperty("line.separator");
private static String        lastMessage      = "";
// private static Logger logger = Logger.getLogger(OsUtl.class);
private static final String  KEY_USER_HOME    = "user.home";
private static final String  KEY_WORK_DIR     = "user.dir";
private static final String  KEY_OS_NAME      = "os.name";
private static final String  KEY_JAVA_VENDOR  = "java.vendor";
private static final String  KEY_JAVA_HOME    = "java.home";
private static final String  KEY_VENDOR_APPLE = "Apple";
private static final String  KEY_MAC_OSX      = "mac os x";
private static final String  KEY_MAC_OS       = "mac os";
private static final String  KEY_LINUX        = "Linux";
private static final String  KEY_WINDOWS      = "Windows";
private static final String  KEY_OS2          = "OS/2";
private static final String  MAC_PREFERENCES  = "/Library/Preferences";
private static final String  MAC_JAVA_HOME    = "/Library/Java/Home";
private static final String  LINUX_ETC        = "/etc";
private static final boolean isMacOS;
private static final boolean isMacOSX;
private static final boolean isLinux;
private static final boolean isWindows;
private static final boolean isOS2;
private static final String  prefDir;
private static final String  localPrefDir;
private static final String  javaHome;
private static final String  homeDir          = System.getProperty(KEY_USER_HOME);
private static final String  workDir          = System.getProperty(KEY_WORK_DIR);
static
{
   String osname = System.getProperty(KEY_OS_NAME);
   String vendor = System.getProperty(KEY_JAVA_VENDOR);
   String jhome = System.getProperty(KEY_JAVA_HOME);
   isMacOSX = osname.toLowerCase().startsWith(KEY_MAC_OSX);
   isMacOS = (!isMacOSX)
      && ((vendor.indexOf(KEY_VENDOR_APPLE) >= 0) || (osname.indexOf(KEY_MAC_OS) >= 0));
   isLinux = (osname.indexOf(KEY_LINUX) >= 0);
   isWindows = (osname.indexOf(KEY_WINDOWS) >= 0);
   isOS2 = (osname.indexOf(KEY_OS2) >= 0);
   if(isMacOSX)
   {
      String pref = homeDir + MAC_PREFERENCES;
      String localPref = MAC_PREFERENCES;
      prefDir = pref;
      localPrefDir = localPref;
      if((jhome == null) || (jhome.length() == 0))
         jhome = MAC_JAVA_HOME;
   }
   else
   {
      prefDir = homeDir;
      localPrefDir = (isLinux) ? LINUX_ETC : workDir;
   }
   javaHome = jhome;
}

private SysUtl()
{
}

public static boolean isMacOS()
{
   return isMacOS;
}

public static boolean isMacOSX()
{
   return isMacOSX;
}

public static boolean isMac()
{
   return(isMacOSX || isMacOS);
}

public static boolean isLinux()
{
   return isLinux;
}

public static boolean isWindows()
{
   return isWindows;
}

public static boolean isOS2()
{
   return isOS2;
}

public static String getHomeDirectory()
{
   return homeDir;
}

public static String getWorkingDirectory()
{
   return workDir;
}

public static String getPrefsDirectory()
{
   return prefDir;
}

public static String getLocalPrefsDirectory()
{
   return localPrefDir;
}

public static String getJavaHome()
{
   return javaHome;
}

public static Properties getProperties()
{
   Properties envVars = new Properties();
   try
   {
      Process p = null;
      Runtime r = Runtime.getRuntime();
      String OS = System.getProperty(KEY_OS_NAME).toLowerCase();
      if(OS.indexOf("windows 9") > -1)
      {
         p = r.exec("command.com /c set");
      }
      else
         if((OS.indexOf("nt") > -1) || (OS.indexOf("windows 2000") > -1)
            || (OS.indexOf("windows xp") > -1))
         {
            // thanks to JuanFran for the xp fix!
            p = r.exec("cmd.exe /c set");
         }
         else
         {
            // our last hope, we assume Unix (thanks to H. Ware for the fix)
            p = r.exec("env");
         }
      BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
      String line;
      while((line = br.readLine()) != null)
      {
         int idx = line.indexOf('=');
         String key = line.substring(0, idx);
         String value = line.substring(idx + 1);
         envVars.setProperty(key, value);
         // System.out.println( key + " = " + value );
      }
   }
   catch(Throwable t)
   {
      throw new RuntimeException("Failure reading OS environment properties. ", t);
   }
   return envVars;
}

static String lastCmd = null;

public static String readln()
{
   String cmd = null;
   try
   {
      BufferedReader d = new BufferedReader(new InputStreamReader(System.in));
      if(d != null)
      {
         cmd = d.readLine().trim();
         if(cmd != null && cmd.equalsIgnoreCase("."))
            cmd = lastCmd;
         cmd = cmd.length() == 0 ? null : cmd;
      }
      if(cmd != null)
         lastCmd = cmd;
      return cmd;
   }
   catch(Exception e)
   {
      return null;
   }
}

public static void main(String [] args)
{
   System.out.println("JavaHome:   " + getJavaHome());
   System.out.println("AppWork:    " + getWorkingDirectory());
   System.out.println("UserHome:   " + getHomeDirectory());
   System.out.println("UserPrefs:  " + getPrefsDirectory());
   System.out.println("LocalPrefs: " + getLocalPrefsDirectory());
   System.out.println("");
   System.out.print("OS:     ");
   if(isMacOSX())
      System.out.println("Mac OS X");
   else
      if(isMacOS())
         System.out.println("Mac OS 8/9");
      else
         if(isLinux())
            System.out.println("Linux");
         else
            if(isWindows())
               System.out.println("Windows");
            else
               if(isOS2())
                  System.out.println("OS/2");
               else
                  System.out.println("unknown");
   System.out.print("OS- properties:     ");
   System.out.print(getProperties().toString());
}

//public static void trace(String msg, String logfile)
//{
//   String m = msg + " > " + SysUtl.getCallerLocation(0);
//   PrintWriter filewriter = null;
//   try
//   {
//      File file = new File(logfile);
//      filewriter = file.exists() ? new PrintWriter(new FileWriter(logfile, true))
//         : new PrintWriter(file, "utf-8");
//      filewriter.println(m);
//      filewriter.flush();
//   }
//   catch(Exception e)
//   {
//   }
//   finally
//   {
//      try
//      {
//         filewriter.close();
//      }
//      catch(Exception egal)
//      {
//      }
//   }
//}
public static void trace(Object obj)
{
   System.out.println((obj == null ? "Null >" : (obj.toString() + " > "))
      + SysUtl.getCallerLocation(0));
}

public static void trace(String msg, Object...obj)
{
   System.out.println(SUtl.sprint(msg, obj) + " > " + SysUtl.getCallerLocation(0));
}

public static void traceB(int stepback, String msg, Object...obj)
{
   System.out.println(SUtl.sprint(msg, obj) + " > " + SysUtl.getCallerLocation(stepback));
}

public static void terror(Throwable t, String msg, Object...obj)
{
   if(t == null && Empty.is(msg))
      System.err.println("Unexpected error. > " + SysUtl.getCallerLocation(0));
   else
   {
      StringBuilder st = new StringBuilder();
      if(!Empty.is(msg))
         st.append(SUtl.sprint(msg, obj));
      if(t != null)
      {
         st.append(" ");
         st.append(recognizeError(t));
         st.append(" CallStack{");
         st.append(stackTraceToString(t));
         st.append("}");
      }
      st.append(" > ");
      st.append(SysUtl.getCallerLocation(0));
      System.err.println(st.toString());
   }
}

public static String recognizeError(Throwable t)
{
   if(t.getCause() != null)
      return t.getCause().toString();
   else
      if(t.getMessage() != null)
         return t.getMessage();
   return "Unrecognized problem.";
}

public static String stackTraceToString(Throwable t)
{
   StringBuilder st = new StringBuilder();
   if(t == null)
      return null;
   else
      for(StackTraceElement se : t.getStackTrace())
      {
         String fn = se.getFileName();
         st.append(fn.substring(0, fn.length() - 5));
         st.append(".");
         st.append(se.getMethodName());
         st.append(":");
         st.append(se.getLineNumber());
         st.append(" ");
      }
   return st.toString().trim();
}

public static synchronized String getCallerLocation(int shift)
{
   int depth = 3 + shift;
   int defaultWidth = 0;// 16;
   boolean showMethodName = true;
   StringWriter sw = new StringWriter();
   PrintWriter pw = new PrintWriter(sw);
   String estack = null;
   String relevantLine = null;
   int ibegin = -1;
   int iend = -1;
   int pos = -1;
   StringBuffer result = new StringBuffer();
   // throw an exception and get the exception-stack
   Throwable t = new Exception();
   synchronized(sw)
   {
      t.printStackTrace(pw);
      estack = sw.toString();
      sw.getBuffer().setLength(0);
   }
   // analyze the exception stack
   // find the relavant 'at name.class'
   // pending on 'depth'
   for(int i = 0; i < depth; i++)
   {
      if((pos = estack.indexOf("at ", ibegin + 1)) == -1)
      {
         break;
      }
      ibegin = pos;
   }
   if(ibegin == -1)
      return "";
   // the beginning of 'relevantLine' denotes the class.method()
   relevantLine = estack.substring(ibegin + 3);
   iend = relevantLine.indexOf("(");
   if(iend <= -1)
   {
      return relevantLine.length() > 10 ? relevantLine.substring(0, 10) + "..." : relevantLine;
   }
   String location = relevantLine.substring(0, iend);
   int x = -1, y = 0, z = 0;
   do
   {
      x = location.indexOf(".", x + 1);
      if(x != -1)
      {
         z = y; // + 1;
         y = x;
      }
   } while(x != -1);
   if(!showMethodName)
   {
      int xx = location.lastIndexOf(".");
      if(xx > -1)
         location = location.substring(0, xx);
   }
   String loc = location.substring(z == 0 ? z : z + 1);
   String fill = "";
   if(defaultWidth > 2)
   {
      int d = defaultWidth - loc.length();
      if(d < 0)
      {
         loc = loc.substring(0, defaultWidth);
      }
      else
         if(d > 0)
         {
            fill = FormatterUtl.padr("", " ", d);
         }
   }
   result.append(loc);
   x = relevantLine.indexOf(".java:") + 5;
   y = relevantLine.indexOf(")", x);
   x = relevantLine.indexOf(".java:") + 5;
   y = relevantLine.indexOf(")", x);
   String lineNo = relevantLine.substring(x, y);
   lineNo = FormatterUtl.padr(lineNo, " ", 4);
   result.append(lineNo);
   result.append(fill);
   result.append(" ");
   return result.toString();
}

public static synchronized String getCallerLocation()
{
   return getCallerLocation(0);
}

public static void unittest()
{
   if(isLinux())
      Units.printout("OS             : Linux");
   if(isWindows())
      Units.printout("OS             : Windows");
   if(isMacOS())
      Units.printout("OS             : MacOS");
   if(isMacOSX())
      Units.printout("OS             : MacOSX");
   if(isOS2())
      Units.printout("OS2");
   Units.printout("Location       : " + getCallerLocation());
   Units.printout("JavaHome       : " + getJavaHome());
   Units.printout("Local prefs dir: " + getLocalPrefsDirectory());
   Units.printout("Working dir    : " + getWorkingDirectory());
   //   Units.printout("Properties     : " + getProperties().toString());
}

/**
 * println, but caching the last message, for avoid endless printouts 
 * of the same content in case of an error. 
 * @param msg
 */
public static void println(String msg)
{
   if(lastMessage.equals(msg))
      return;
   System.out.println(msg);
   lastMessage = msg;
}

public static String callStack()
{
   try
   {
      throw new Exception("");
   }
   catch(Exception e)
   {
      return FormatterUtl.stackTraceToString(e);
   }
}

public static void popup(String msg)
{
   JFrame j = new JFrame();
   j.add(new JTextArea(msg));
   j.setSize(new Dimension(250, 150));
   j.setVisible(true);
}
}
