use jni::objects::{JObject, JValue, JString, JList};
use jni::JNIEnv;
use crate::error::Error;
const LONGTYPE:u16 = 'J' as u16;
const INTTYPE:u16 = 'I' as u16;
const SHORTTYPE:u16 = 'S' as u16;
const BYTETYPE:u16 = 'B' as u16;
const BOOLEANTYPE:u16 = 'Z' as u16;
const DOUBLETYPE:u16 = 'D' as u16;
const FLOATTYPE:u16 = 'F' as u16;
const ARRAYTYPE:u16 = 'a' as u16;
const BINARYTYPE:u16 = 'b' as u16;
#[derive(Debug, PartialEq, Clone)]
pub struct ValueObject {
pub value:Value,
pub timestamp:u64,
pub quality:Quality
}
#[derive(Debug, PartialEq, Clone)]
pub enum Value {
String(String),
Long(i64),
Array(Vec<ValueObject>),
Binary(Vec<u8>),
Boolean(bool),
Double(f64),
}
#[derive(Debug, PartialEq, Clone)]
pub enum Quality {
Good,
Bad
}
impl ValueObject {
pub(crate) fn from_jobject(env:&JNIEnv, jobject:JObject) -> Result<ValueObject, Error> {
let type_char = env.call_method(jobject, "getType", "()C", &[]);
if type_char.is_err() {
return Err(Error::InternalError(format!("Error calling getType on value object: {}", type_char.unwrap_err())))
}
match type_char.unwrap() {
JValue::Char(char) => {
match char {
LONGTYPE | INTTYPE | SHORTTYPE | BYTETYPE => {
let value = env.call_method(jobject, "getLongValue", "()J", &[]);
if value.is_err() {
return Err(Error::InternalError(format!("Error calling getLongValue on value object: {}", value.unwrap_err())))
}
let value = value.unwrap();
let timestamp = env.call_method(jobject, "getTimeMillis", "()J", &[]);
if timestamp.is_err() {
return Err(Error::InternalError(format!("Error calling getTimeMillis on value object: {}", timestamp.unwrap_err())))
}
let timestamp = timestamp.unwrap();
let quality = env.call_method(jobject, "getQuality", "()I", &[]);
if quality.is_err() {
return Err(Error::InternalError(format!("Error calling getTimeMillis on value object: {}", quality.unwrap_err())))
}
let quality = quality.unwrap().i().unwrap();
let quality = if quality == 0 {Quality::Good} else {Quality::Bad};
return Ok(ValueObject{value:Value::Long(value.j().unwrap()), timestamp:timestamp.j().unwrap() as u64, quality:quality})
},
DOUBLETYPE | FLOATTYPE => {
let value = env.call_method(jobject, "getDoubleValue", "()D", &[]);
if value.is_err() {
return Err(Error::InternalError(format!("Error calling getDoubleValue on value object: {}", value.unwrap_err())))
}
let value = value.unwrap();
let timestamp = env.call_method(jobject, "getTimeMillis", "()J", &[]);
if timestamp.is_err() {
return Err(Error::InternalError(format!("Error calling getTimeMillis on value object: {}", timestamp.unwrap_err())))
}
let timestamp = timestamp.unwrap();
let quality = env.call_method(jobject, "getQuality", "()I", &[]);
if quality.is_err() {
return Err(Error::InternalError(format!("Error calling getTimeMillis on value object: {}", quality.unwrap_err())))
}
let quality = quality.unwrap().i().unwrap();
let quality = if quality == 0 {Quality::Good} else {Quality::Bad};
return Ok(ValueObject{value:Value::Double(value.d().unwrap()), timestamp:timestamp.j().unwrap() as u64, quality:quality})
},
BOOLEANTYPE => {
let value = env.call_method(jobject, "getBoolValue", "()Z", &[]);
if value.is_err() {
return Err(Error::InternalError(format!("Error calling getBoolValue on value object: {}", value.unwrap_err())))
}
let value = value.unwrap();
let timestamp = env.call_method(jobject, "getTimeMillis", "()J", &[]);
if timestamp.is_err() {
return Err(Error::InternalError(format!("Error calling getTimeMillis on value object: {}", timestamp.unwrap_err())))
}
let timestamp = timestamp.unwrap();
let quality = env.call_method(jobject, "getQuality", "()I", &[]);
if quality.is_err() {
return Err(Error::InternalError(format!("Error calling getTimeMillis on value object: {}", quality.unwrap_err())))
}
let quality = quality.unwrap().i().unwrap();
let quality = if quality == 0 {Quality::Good} else {Quality::Bad};
return Ok(ValueObject{value:Value::Boolean(value.z().unwrap()), timestamp:timestamp.j().unwrap() as u64, quality:quality})
},
BINARYTYPE => {
let value = env.call_method(jobject, "getBinaryValue", "()[B", &[]);
if value.is_err() {
return Err(Error::InternalError(format!("Error calling getBinaryValue on value object: {}", value.unwrap_err())))
}
let value = value.unwrap().l().unwrap();
let timestamp = env.call_method(jobject, "getTimeMillis", "()J", &[]);
if timestamp.is_err() {
return Err(Error::InternalError(format!("Error calling getTimeMillis on value object: {}", timestamp.unwrap_err())))
}
let timestamp = timestamp.unwrap();
let quality = env.call_method(jobject, "getQuality", "()I", &[]);
if quality.is_err() {
return Err(Error::InternalError(format!("Error calling getTimeMillis on value object: {}", quality.unwrap_err())))
}
let quality = quality.unwrap().i().unwrap();
let quality = if quality == 0 {Quality::Good} else {Quality::Bad};
return Ok(ValueObject{value:Value::Binary(env.convert_byte_array(*value).unwrap()), timestamp:timestamp.j().unwrap() as u64, quality:quality})
},
ARRAYTYPE => {
let list = JList::from_env(env, jobject);
if list.is_err() {
return Err(Error::InternalError(format!("Error getting list from value object")))
}
let list = list.unwrap();
let timestamp = env.call_method(jobject, "getTimeMillis", "()J", &[]);
if timestamp.is_err() {
return Err(Error::InternalError(format!("Error calling getTimeMillis on value object: {}", timestamp.unwrap_err())))
}
let timestamp = timestamp.unwrap();
let quality = env.call_method(jobject, "getQuality", "()I", &[]);
if quality.is_err() {
return Err(Error::InternalError(format!("Error calling getTimeMillis on value object: {}", quality.unwrap_err())))
}
let quality = quality.unwrap().i().unwrap();
let quality = if quality == 0 {Quality::Good} else {Quality::Bad};
let mut vec = Vec::<ValueObject>::with_capacity(list.size().unwrap() as usize);
for value in list.iter().unwrap() {
let value = ValueObject::from_jobject(env, value);
if value.is_err() {
return Err(value.unwrap_err());
}
vec.push(value.unwrap());
}
return Ok(ValueObject{value:Value::Array(vec), timestamp:timestamp.j().unwrap() as u64, quality:quality})
},
_ => {
let value = env.call_method(jobject, "getStringValue", "()Ljava/lang/String;", &[]);
if value.is_err() {
return Err(Error::InternalError(format!("Error calling getStringValue on value object: {}", value.unwrap_err())))
}
let value = value.unwrap();
let timestamp = env.call_method(jobject, "getTimeMillis", "()J", &[]);
if timestamp.is_err() {
return Err(Error::InternalError(format!("Error calling getTimeMillis on value object: {}", timestamp.unwrap_err())))
}
let timestamp = timestamp.unwrap();
let quality = env.call_method(jobject, "getQuality", "()I", &[]);
if quality.is_err() {
return Err(Error::InternalError(format!("Error calling getTimeMillis on value object: {}", quality.unwrap_err())))
}
let quality = quality.unwrap().i().unwrap();
let quality = if quality == 0 {Quality::Good} else {Quality::Bad};
let value = value.l().unwrap();
let value = env.get_string(JString::from(value));
if value.is_err() {
return Err(Error::InternalError(format!("Error getting string from Java string")));
}
let value = value.unwrap();
let test_value = value.to_str();
let string_value;
if test_value.is_err() {
eprintln!("Warning: string value contained invalid utf8 characters, returning a lossy string");
string_value = value.to_string_lossy().to_string();
} else {
string_value = test_value.unwrap().to_string();
}
return Ok(ValueObject{value:Value::String(string_value), timestamp:timestamp.j().unwrap() as u64, quality:quality})
}
}
},
_=> return Err(Error::InternalError("getType failed to return a char".to_string()))
}
}
}