package code;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.text.MessageFormat;
import java.util.List;
import java.util.stream.Stream;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.sun.jna.Native;

import net.sourceforge.peers.sip.syntaxencoding.SipHeaderFieldName;
import net.sourceforge.peers.sip.syntaxencoding.SipHeaderFieldValue;
import net.sourceforge.peers.sip.syntaxencoding.SipHeaders;

public class common {
	
	/**
	 * Check if value is a valid String
	 * Not Null, and length at least 1 character
	 * @param vv String value
	 * @return true if valid
	 */
	public static boolean ValidString(String vv) {
		if (vv != null) {
			if (vv.length()>0) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Check if value is a valid Port number
	 * @param vv between 1 ~ 65535
	 * @return true if valid
	 */
	public static boolean ValidPortNumber(int vv) {
		if (vv>0) {
			if (vv<65536) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Check if valus is a valid array of string
	 * array is not null, array length at least 1
	 * and all members of array is valid string
	 * @param vv array of string
	 * @return true if valid
	 */
	public static boolean ValidArrayOfString(String[] vv) {
		if (vv!=null) {
			if (vv.length>0) {
				return Stream.of(vv).allMatch(ss-> ValidString(ss));
			}
		}
		return false;
	}
	
	public static boolean ValidArrayOfBytes(byte[] vv) {
		if (vv!=null) {
			if (vv.length>0) {
				return true;
			}
		}
		return false;
	}
	
	public static boolean ValidListOfString(List<String> vv) {
		if (vv!=null) {
			if (vv.size()>0) {
				return true;
			}
		}
		return false;
	}
	
	public static String getClassValues(Class<?> cc, Object obj) {
		Field[] fields = cc.getFields();
		
		if (fields!=null && fields.length>0) {
			int length = fields.length;
			String[] fs = new String[length];
			for(int i=0;i<length;i++) {
				Field ff = fields[i];
				try {
					fs[i] = ff.getName()+":"+ff.get(obj);
				} catch (IllegalArgumentException | IllegalAccessException e) {
					fs[i] = ff.getName()+":N/A";
					System.out.println("getClassValues exception="+e.getMessage());
				}
				
			}
			
			return String.join(", ", fs);
			
		} else return "";
	}
	
	public static JsonObject GetJsonObjectValue_JsonObject(JsonObject jo, String key) {
		if (jo!=null) {
			if (jo.has(key)) {
				JsonElement je = jo.get(key);
				if (je!=null && je.isJsonObject()) {
					return je.getAsJsonObject();
				}
			}
		}
		return null;
	}
	
	public static JsonArray GetJsonObjectValue_JsonArray(JsonObject jo, String key) {
		if (jo!=null) {
			if (jo.has(key)) {
				JsonElement je = jo.get(key);
				if (je!=null && je.isJsonArray()) {
					return je.getAsJsonArray();
				}
			}
		}
		return null;
	}
	
	public static double GetJsonObjectValue_Double(JsonObject jo, String key) {
		if (jo!=null) {
			if (jo.has(key)) {
				try {
					return jo.get(key).getAsDouble();
				} catch(UnsupportedOperationException | NumberFormatException | IllegalStateException e) {
					System.out.println("GetJsonObjectValue_Double exception key="+key+", message="+e.getMessage());
				}
				
			}
		}
		return 0;
	}
	
	public static float GetJsonObjectValue_Float(JsonObject jo, String key) {
		if (jo!=null) {
			if (jo.has(key)) {
				try {
					return jo.get(key).getAsFloat();
				} catch(UnsupportedOperationException | NumberFormatException | IllegalStateException e) {
					System.out.println("GetJsonObjectValue_Float exception key="+key+", message="+e.getMessage());
				}
				
			}
		}
		return 0.0f;
	}
	
	public static  long GetJsonObjectValue_Long(JsonObject jo, String key) {
		if (jo!=null) {
			if (jo.has(key)) {
				try {
					return jo.get(key).getAsLong();
				} catch(UnsupportedOperationException | NumberFormatException | IllegalStateException e) {
					System.out.println("GetJsonObjectValue_Long exception key="+key+", message="+e.getMessage());
				}
				
			}
		}
		return 0l;
	}
	
	public static int GetJsonObjectValue_Integer(JsonObject jo, String key) {
		if (jo!=null) {
			if (jo.has(key)) {
				try {
					return jo.get(key).getAsInt();
				} catch(UnsupportedOperationException | NumberFormatException | IllegalStateException e) {
					System.out.println("GetJsonObjectValue_Integer exception key="+key+", message="+e.getMessage());
				}
				
			}
		}
		return 0;
	}
	
	public static String GetJsonObjectValue_String(JsonObject jo, String key) {
		if (jo!=null) {
			if (jo.has(key)) {
				try {
					return jo.get(key).getAsString();
				} catch(UnsupportedOperationException | IllegalStateException e) {
					System.out.println("GetJsonObjectValue_String exception key="+key+", message="+e.getMessage());
				}
				
			}
		}
		return null;
	}
	
	public static String[] GetJsonObjectValue_Strings(JsonObject jo, String key) {
		if (jo!=null) {
			if (jo.has(key)) {
				JsonElement je = jo.get(key);
				if (je.isJsonArray()) {
					JsonArray ja = je.getAsJsonArray();
					int length = ja.size();
					if (length>0) {
						String[] result = new String[length];
						for(int ii=0;ii<length;ii++) {
							result[ii] = ja.get(ii).getAsString();
						}
						return result;
					}
				}
			}
		}
		return new String[0];
	}
	
	public static boolean GetJsonObjectValue_Boolean(JsonObject jo, String key) {
		if (jo!=null) {
			if (jo.has(key)) {
				try {
					return jo.get(key).getAsBoolean();
				} catch(UnsupportedOperationException | IllegalStateException e) {
					System.out.println("GetJsonObjectValue_Boolean exception key="+key+", message="+e.getMessage());
				}
				
			}
		}
		return false;
	}
	
	public static void SetJsonObjectValue(JsonObject jo, String key, int value) {
		if (jo!=null) {
			jo.remove(key);
			jo.addProperty(key, value);
		}
	}
	public static void SetJsonObjectValue(JsonObject jo, String key, boolean value) {
		if (jo!=null) {
			jo.remove(key);
			jo.addProperty(key, value);
		}
	}
	
	public static void SetJsonObjectValue(JsonObject jo, String key, String[] value) {
		if (jo!=null) {
			jo.remove(key);
			JsonArray arr = new JsonArray();
			if (ValidArrayOfString(value)) {
				for(String ss : value) if (ValidString(ss)) arr.add(ss);
			}
			jo.add(key, arr);
		}
	}
	
	public static void SetJsonObjectValue(JsonObject jo, String key, String value) {
		if (jo!=null) {
			jo.remove(key);
			jo.addProperty(key, value);
		}
	}
	
	/**
	 * Concat arrays of bytes
	 * @param arrays 
	 * @return array of bytes
	 */
	public static byte[] ConcatBytes(byte[]... arrays) {
		if (arrays!=null && arrays.length>0) {
			try(ByteArrayOutputStream os = new ByteArrayOutputStream()){
				for(byte[] arr : arrays) {
					if (arr != null && arr.length>0) os.write(arr);
				}
				return os.toByteArray();
			} catch (IOException e) {
				System.out.println("ConcatBytes exception = "+e.getMessage());
			}
		}
		return new byte[0];
	}
	
	
	/**
	 * Concat two arrays of bytes
	 * @param a
	 * @param b
	 * @return array of bytes
	 */
	public static byte[] ConcatBytes(byte[] a, byte[] b) {
		int alen = ValidArrayOfBytes(a) ? a.length : 0;
		int blen = ValidArrayOfBytes(b) ? b.length : 0;
		if (blen>0) {
			byte[] result = new byte[alen+blen];
			System.arraycopy(a, 0, result, 0, alen);
			System.arraycopy(b, 0, result, alen, blen);
			return result;
			
		} else return a;		// gak ada b, gak ada yang mau ditambah
	}
	
	public static String ConcatListOfString(List<String> rr) {
		if (rr!=null && rr.size()>0) {
			StringBuilder str = new StringBuilder();
			rr.forEach(ss -> str.append(ss));
			return str.toString();
		}
		return null;
	}
	
	public static String ConcatString(String...strings) {
		if (strings!=null && strings.length>0) {
			StringBuilder str = new StringBuilder();
			for(String ss : strings) {
				str.append(ss);
			}
			return str.toString();
		}
		return null;
	}
	
	/**
	 * Create two level Command for Axis
	 * format : { cmd : { key : value} }
	 * @param cmd Command
	 * @param key Key 
	 * @param value Value
	 * @return JsonObject if valid, or null if invalid
	 */
	public static JsonObject Create_2Level_Command(String cmd, String key, String value) {
		if (ValidString(cmd)) {
			if (ValidString(key)) {
				if (ValidString(value)) {
					JsonObject ja = new JsonObject();
					JsonObject jb = new JsonObject();
					jb.addProperty(key, value);
					ja.add(cmd, jb);
					return ja;
				}
			}
		}
		return null;
	}
	
	public static JsonObject Create_2Level_Command(String cmd, String key, JsonObject value) {
		if (ValidString(cmd)) {
			if (ValidString(key)) {
				if (value!=null) {
					JsonObject ja = new JsonObject();
					JsonObject jb = new JsonObject();
					jb.add(key, value);
					ja.add(cmd, jb);
					return ja;
				}
			}
		}
		return null;
	}
	
	public static JsonObject Create_2Level_Command(String cmd, String key, JsonArray value) {
		if (ValidString(cmd)) {
			if (ValidString(key)) {
				if (value!=null) {
					JsonObject ja = new JsonObject();
					JsonObject jb = new JsonObject();
					jb.add(key, value);
					ja.add(cmd, jb);
					return ja;
				}
			}
		}
		return null;
	}
	
	public static JsonObject Create_1Level_Command(String command, String value) {
		if (ValidString(command)) {
			JsonObject ja = new JsonObject();
			if (ValidString(value)) {
				ja.addProperty(command, value);
				return ja;
			} 
		}
		return null;
	}
	
	public static JsonObject Create_1Level_Command(String command, JsonObject value) {
		if (ValidString(command)) {
			if (value!=null) {
				JsonObject ja = new JsonObject();
				ja.add(command, value);
				return ja;
			}
			
		}
		return null;
	}
	
	/**
	 * Extract Library from Resource path to current folder
	 * @param currentfolder path to extract
	 * @param libname library name
	 * @return absolute path of extracted library, or return IOException message
	 */
	public static String extract_library(String currentfolder, String libname) {
		try {
			File existinglib = new File(currentfolder, System.mapLibraryName(libname));
			File xx = Native.extractFromResourcePath(libname);
			
			if (existinglib.exists() && existinglib.isFile() && existinglib.length()==xx.length()) {
				xx.delete();	// gak perlu
				return existinglib.getAbsolutePath();
			} else {
				if (xx.renameTo(existinglib)) {
					return existinglib.getAbsolutePath();
				} else return xx.getAbsolutePath();
				
			}
		} catch (IOException e) {
			return MessageFormat.format("IOException Libname={0} Message={1}", libname, e.getMessage());
		}
	}
	
	public static boolean Strings_Valid_and_Match(String str1, String str2) {
		if (ValidString(str1)) {
			if (ValidString(str2)) {
				return str1.equals(str2);
			}
		}
		return false;
	}
	
	public static void Sleep(long ms) {
		try {
			Thread.sleep(ms);
		} catch (InterruptedException e) {
			
		}
	}
	
	public static void Sleep(int sec) {
		try {
			Thread.sleep(sec * 1000);
		}catch (InterruptedException e) {
			
		}
	}
	
	/**
	 * Check if ByteBuffer ready
	 * Ready means not null, and capacity() not zero
	 * @param bx ByteBuffer to check
	 * @return true if bytebuffer ready
	 */
	public static boolean ByteBufferReady(ByteBuffer bx) {
		if (bx!=null) {
			return bx.capacity()!=0;
		}
		return false;
	}
	
	/**
	 * Check if ByteBuffer in Write Mode
	 * Write Mode : limit = capacity
	 * @param bx ByteBuffer to check
	 * @return true if in write mode
	 */
	public static boolean IsWriteMode(ByteBuffer bx) {
		if (ByteBufferReady(bx)) {
			return bx.limit()==bx.capacity();
		}
		return false;
	}
	
	/**
	 * Check if ByteBuffer in Read Mode
	 * Read Mode : limit != capacity
	 * @param bx ByteBuffer to check
	 * @return true if in read mode
	 */
	public static boolean IsReadMode(ByteBuffer bx) {
		if (ByteBufferReady(bx)) {
			return bx.limit()!=bx.capacity();
		}
		return false;
	}
	
	/**
	 * Change to Write Mode
	 * @param bx ByteBuffer to change
	 * @return true if success
	 */
	public static boolean ChangeToWriteMode(ByteBuffer bx) {
		if (ByteBufferReady(bx)) {
			bx.compact();
		}
		return false;
	}
	
	/**
	 * Change to Read Mode
	 * @param bx ByteBuffer to change
	 * @return true if success
	 */
	public static boolean ChangeToReadMode(ByteBuffer bx) {
		if (ByteBufferReady(bx)) {
			bx.flip();
			return true;
		}
		return false;
	}
	
	/**
	 * How much byte space available to write
	 * Available : limit - position (remaining)
	 * @param bx ByteBuffer to check
	 * @return 0 if not available or wrong mode
	 */
	public static int AvailableToWrite(ByteBuffer bx) {
		if (IsWriteMode(bx)) {
			return bx.remaining();
		}
		return 0;
	}
	
	/**
	 * How Much byte space available to Read
	 * Available : limit - position (remaining)
	 * @param bx ByteBuffer to check
	 * @return 0 if not available or wrong mode
	 */
	public static int AvailableToRead(ByteBuffer bx) {
		if (IsReadMode(bx)) {
			return bx.remaining();
		}
		return 0;
	}
	
	/**
	 * Get SIP Header Value
	 * @param head SipHeaders to get
	 * @param headername header name to get
	 * @return null if failed
	 */
	public static String GetSIPHeaderValue(SipHeaders head, String headername) {
		if (head!=null) {
			SipHeaderFieldName _fn = new SipHeaderFieldName(headername);
			if (head.contains(_fn)) {
				SipHeaderFieldValue _fv = head.get(_fn);
				if (_fv!=null) {
					return _fv.getValue();
				}
			}
		}
		return null;
	}
	
}
