package customsocket;

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.objects.collections.List;
import anywheresoftware.b4a.objects.streams.File;
import anywheresoftware.b4a.objects.streams.File.InputStreamWrapper;

/**
 * JSON Array Object
 * @author rdkartono
 *
 */
@BA.ShortName("JsonArray")
public class JsonArray {

	private JSONArray ja;
	
	@BA.Hide
	/**
	 * Get Native JSONArray Object
	 * @return JSONArray object
	 */
	public JSONArray getObject() {
		return ja;
	}
	
	@BA.Hide
	/**
	 * Set Native JSONArray object
	 * @param value : source value
	 */
	public void setObject(JSONArray value) {
		ja = value;
	}
	
	/**
	 * Initialize empty JsonArray
	 */
	public void InitializeEmpty() {
		ja = new JSONArray();
		
	}
	
	/**
	 * Initialize JsonArray from String
	 * @param value : string with format of Json Array
	 * @return true if can be initialized
	 */
	public boolean InitializeFromString(String value) {
		if (value instanceof String) {
			if (!value.isEmpty()) {
				try {
					ja = new JSONArray(value);
					return true;	
				} catch(JSONException e) {
					BA.Log("Unable to InitializeFromString, Msg : "+e.getMessage());
				}
				
			}
		}
		return false;
	}
	
	/***
	 * Initialize JsonArray from a file containing plain string
	 * Reading result from file, will be used in InitializeFromString function
	 * @param directory : directory path
	 * @param filename : filename
	 * @return true if value can be translated to JsonArray
	 */
	public boolean InitializeFromFile(String directory, String filename) {
		try {
			if (File.Exists(directory, filename)) {
				InputStreamWrapper isw = File.OpenInput(directory, filename);
				if (isw.IsInitialized()) {
					int bytecount = isw.BytesAvailable();
					if (bytecount>0) {
						byte[] bb = new byte[bytecount];
						isw.ReadBytes(bb, 0, bytecount);
						isw.Close();
						String value = new String(bb);
						return InitializeFromString(value);
					}
				}
			}
		} catch (IOException e) {
			BA.Log("Unable to InitializeFromFile, Msg : "+e.getMessage());
		}
		return false;
	}
	
	/**
	 * Initialize JsonArray from B4X List
	 * @param value : B4X List
	 * @return true if value can be translated to JsonArray
	 */
	public boolean InitializeFromList(List value) {
		if (value instanceof List) {
			if (value.IsInitialized()) {
				if (value.getSize()>0) {
					try {
						ja = new JSONArray(value.getObject());
						return true;	
					} catch(JSONException e) {
						BA.Log("Unable to InitializeFromList, Msg : "+e.getMessage());
					}
					
				}
			}
		}
		return false;
	}
	
	/**
	 * Initialize JsonArray from array of Object
	 * @param value : array of object, which can be anything from String, int, long, etc.
	 * @return true if value can be translated to JsonArray
	 */
	public boolean InitializeFromObject(Object... value) {
		if (value != null) {
			if (value.length>0) {
				try {
					ja = new JSONArray(value);
					return true;
				} catch(JSONException e) {
					BA.Log("Unable to InitializeFromObject, Msg : "+e.getMessage());
				}
			}
		}
		return false;
	}
	
	/**
	 * Check if JsonArray is initialized
	 * @return true if initialized
	 */
	public boolean IsInitialized() {
		if (ja instanceof JSONArray) {
			return true;
		}
		return false;
	}
	
	/**
	 * Clear content of JsonArray
	 */
	public void Clear() {
		if (IsInitialized()) ja.clear();
	}
	
	/**
	 * Check if JsonArray has some value
	 * @return true if has value
	 */
	public boolean HasSomeValue() {
		if (IsInitialized()) {
			
			return !ja.isEmpty();
		}
		return false;
	}
	
	/**
	 * Get Size of JsonArray
	 * @return 0 if empty or not initialized
	 */
	public int getSize() {
		if (HasSomeValue()) {
			return ja.length();
		}
		return 0;
	}
	
	/**
	 * Get JsonArray in string format
	 * @return String value
	 */
	public String ToString() {
		if (HasSomeValue()) {
			return ja.toString();
		}
		return "";
	}
	
	/**
	 * Get JsonArray in pretty string format
	 * @param indentvalue how many space to indent string
	 * @return String value
	 */
	public String ToStringWithIndent(int indentvalue) {
		if (HasSomeValue()) {
			return ja.toString(indentvalue);
		}
		return "";
	}
	
	/**
	 * Write JsonArray to a File
	 * @param directory : directory path
	 * @param filename : filename
	 * @return true if can be written
	 */
	public boolean WriteToFile(String directory, String filename) {
		if (HasSomeValue()) {
			try {
				Writer ww = new FileWriter(File.Combine(directory, filename));
				ja.write(ww);
				ww.close();
				return true;
			} catch (IOException | JSONException e) {
				BA.Log("Unable to WriteToFile, Msg : "+e.getMessage());
			}
			
		}
		return false;
	}
	
	/**
	 * Write JsonArray to a file with indentation for pretty string
	 * @param directory : directory path
	 * @param filename : filename
	 * @param indentvalue : how many spaces added for indentation
	 * @return true if can be written
	 */
	public boolean WriteToFileWithIndent(String directory, String filename, int indentvalue) {
		if (HasSomeValue()) {
			try {
				Writer ww = new FileWriter(File.Combine(directory, filename));
				ja.write(ww, indentvalue, indentvalue);
				ww.close();
				return true;
			}catch (IOException | JSONException e) {
				BA.Log("Unable to WriteToFileWithIndent, Msg : "+e.getMessage());
			}
		}
		return false;
	}
	
	/**
	 * Get an Object at index
	 * @param index : zero-based index
	 * @return null if index not found
	 */
	public Object Get(int index) {
		if (HasSomeValue()) {
			try {
				Object result = ja.get(index);
				return result;
			} catch(JSONException e){
				BA.Log("Unable to Get, Msg : "+e.getMessage());
			}
			
		}
		return null;
	}
	
	/**
	 * Put an Object to JsonArray
	 * @param value : value to put
	 * @return true if can be put
	 */
	public boolean Put(Object value) {
		if (IsInitialized()) {
			try {
				ja.put(value);	
				return true;
			} catch(JSONException e) {
				BA.Log("Unable to Put, Msg : "+e.getMessage());
			}
			
		}
		return false;
	}
	
	/**
	 * Get a boolean at index
	 * @param index : zero-based index
	 * @param defaultvalue : if index not found, will return this value
	 * @return boolean value
	 */
	public boolean GetBoolean(int index, boolean defaultvalue) {
		if (HasSomeValue()) {
			try {
				boolean result = ja.getBoolean(index);
				return result;
			} catch(JSONException e){
				BA.Log("Unable to GetBoolean, Msg : "+e.getMessage());
			}
		}
		return defaultvalue;
	}
	
	/**
	 * Put an boolean to JsonArray
	 * @param value : value to put
	 * @return true if can be put
	 */
	public boolean PutBoolean(boolean value) {
		if (IsInitialized()) {
			try {
				ja.put(value);
				return true;
			} catch(JSONException e) {
				BA.Log("Unable to PutBoolean, Msg : "+e.getMessage());
			}
			
		}
		return false;
	}
	
	/**
	 * Get a double at index
	 * @param index : zero-based index
	 * @param defaultvalue : if index not found, will return this value
	 * @return double value
	 */
	public double GetDouble(int index, double defaultvalue) {
		if (HasSomeValue()) {
			try {
				double result = ja.getDouble(index);
				return result;
			} catch(JSONException e){
				BA.Log("Unable to GetDouble, Msg : "+e.getMessage());
			}
		}
		return defaultvalue;
	}
	
	/**
	 * Put an double to JsonArray
	 * @param value : value to put
	 * @return true if can be put
	 */
	public boolean PutDouble(double value) {
		if (IsInitialized()) {
			try {
				ja.put(value);
				return true;
			} catch(JSONException e) {
				BA.Log("Unable to PutDouble, Msg : "+e.getMessage());
			}
			
		}
		return false;
	}
	
	/**
	 * Get a float at index
	 * @param index : zero-based index
	 * @param defaultvalue : if index not found, will return this value
	 * @return float value
	 */
	public float GetFloat(int index, float defaultvalue) {
		if (HasSomeValue()) {
			try {
				float result = ja.getFloat(index);
				return result;
			} catch(JSONException e){
				BA.Log("Unable to GetFloat, Msg : "+e.getMessage());
			}
		}
		return defaultvalue;
	}
	
	/**
	 * Put an float to JsonArray
	 * @param value : value to put
	 * @return true if can be put
	 */
	public boolean PutFloat(float value) {
		if (IsInitialized()) {
			try {
				ja.put(value);
				return true;
			} catch(JSONException e) {
				BA.Log("Unable to PutFloat, Msg : "+e.getMessage());
			}
			
		}
		return false;
	}
	
	/**
	 * Get a int at index
	 * @param index : zero-based index
	 * @param defaultvalue : if index not found, will return this value
	 * @return int value
	 */
	public int GetInt(int index, int defaultvalue) {
		if (HasSomeValue()) {
			try {
				int result = ja.getInt(index);
				return result;
			} catch(JSONException e){
				BA.Log("Unable to GetInt, Msg : "+e.getMessage());
			}
		}
		return defaultvalue;
	}
	
	/**
	 * Put an int to JsonArray
	 * @param value : value to put
	 * @return true if can be put
	 */
	public boolean PutInt(int value) {
		if (IsInitialized()) {
			try {
				ja.put(value);
				return true;
			} catch(JSONException e) {
				BA.Log("Unable to PutInt, Msg : "+e.getMessage());
			}
			
		}
		return false;
	}
	
	/**
	 * Get a long at index
	 * @param index : zero-based index
	 * @param defaultvalue : if index not found, will return this value
	 * @return long value
	 */
	public long GetLong(int index, long defaultvalue) {
		if (HasSomeValue()) {
			try {
				long result = ja.getLong(index);
				return result;
			} catch(JSONException e){
				BA.Log("Unable to GetLong, Msg : "+e.getMessage());
			}
		}
		return defaultvalue;
	}
	
	/**
	 * Put an long to JsonArray
	 * @param value : value to put
	 * @return true if can be put
	 */
	public boolean PutLong(long value) {
		if (IsInitialized()) {
			try {
				ja.put(value);
				return true;
			} catch(JSONException e) {
				BA.Log("Unable to PutLong, Msg : "+e.getMessage());
			}
			
		}
		return false;
	}
	
	/**
	 * Get a JsonObject at index
	 * @param index : zero-based index
	 * @param defaultvalue : if index nt found, will return this value
	 * @return JsonObject value
	 */
	public JsonObject GetJsonObject(int index, JsonObject defaultvalue) {
		if (HasSomeValue()) {
			try {
				JSONObject jo = ja.getJSONObject(index);
				JsonObject result = new JsonObject();
				result.setObject(jo);
				return result;
			} catch(JSONException e){
				BA.Log("Unable to GetJsonObject, Msg : "+e.getMessage());
			}
			
		}
		return defaultvalue;
	}
	
	/**
	 * Put an JsonObject to JsonArray
	 * @param value : value to put
	 * @return true if can be put
	 */
	public boolean PutJsonObject(JsonObject value) {
		if (IsInitialized()) {
			if (value instanceof JsonObject) {
				if (value.HasSomeValue()) {
					JSONObject xx = value.getObject();
					if (xx instanceof JSONObject) {
						try {
							ja.put(xx);
							return true;
						} catch(JSONException e) {
							BA.Log("Unable to PutJsonObject, Msg : "+e.getMessage());
						}
					}
				}
				
			}
		}
		return false;
	}
	
	/**
	 * Get a String at index
	 * @param index : zero-based index 
	 * @param defaultvalue : if index not found, will return this value
	 * @return String value
	 */
	public String GetString(int index, String defaultvalue) {
		if (HasSomeValue()) {
			try {
				String result = ja.getString(index);
				return result;
			} catch(JSONException e){
				BA.Log("Unable to GetString, Msg : "+e.getMessage());
			}
		}
		return defaultvalue;
	}
	
	/**
	 * Put a String to JsonArray
	 * @param value : value to put
	 * @return true if can be put
	 */
	public boolean PutString(String value) {
		if (IsInitialized()) {
			if (value instanceof String) {
				try {
					ja.put(value);
					return true;
				} catch(JSONException e) {
					BA.Log("Unable to PutString, Msg : "+e.getMessage());
				}
			}
		}
		return false;
	}
	
	/**
	 * Put a B4X List to JsonArray
	 * @param value : value to put
	 * @return true if can be put
	 */
	public boolean PutList(List value) {
		if (IsInitialized()) {
			if (value instanceof List) {
				if (value.IsInitialized()) {
					if (value.getSize()>0) {
						try {
							ja.put(value.getObject());
							return true;
						} catch(JSONException e) {
							BA.Log("Unable to PutList, Msg : "+e.getMessage());
						}
					}
				}
			}
		}
		return false;
	}
	
	@SuppressWarnings("unchecked")
	/**
	 * Get a B4X List at index
	 * @param index : zero-based index
	 * @param defaultvalue : if index not found, will return this value
	 * @return List value
	 */
	public List GetList(int index, List defaultvalue) {
		if (HasSomeValue()) {
			try {
				Object xx = ja.get(index);
				List result = new List();
				result.Initialize();
				if (xx instanceof java.util.List<?>) {
					java.util.List<Object> mm = (java.util.List<Object>) xx;
					mm.forEach((vv)->{
						result.Add(vv);
					});
				} else {
					result.Add(xx);
				}
				
			} catch(JSONException e) {
				BA.Log("Unable to GetList, Msg : "+e.getMessage());
			}
		}
		return defaultvalue;
	}
}
