1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
use lazy_static::lazy_static;
use std::sync::Mutex;
use std::time::{SystemTime, UNIX_EPOCH};

use crate::value::Value;
use crate::value::Value::*;
use std::collections::HashMap;

pub type NativeFn = fn(Vec<Value>) -> Result<Value, String>;

lazy_static! {
    // format: name, (arity, function) if arity is None, the function is variadic
    pub static ref NATIVE_FUNCTIONS: Mutex<HashMap<&'static str, (Option<usize>, NativeFn)>> =
        Mutex::new(HashMap::from([
            ("clock", (Some(0), clock as NativeFn)),
            (
                "to_string",
                (Some(1), |args| {
                    Ok(PhoenixString(value_to_string(&args[0])))
                })
            ),
            (
                "type",
                (Some(1), |args| {
                    Ok(PhoenixString(get_type_string(&args[0]).to_string()))
                })
            ),
            ("printf", (None, printf as NativeFn)),
            ("int", (Some(1), int as NativeFn)),
            ("float", (Some(1), float as NativeFn))
        ]));
}

pub fn clock(_args: Vec<Value>) -> Result<Value, String> {
    let start = SystemTime::now();
    let since_the_epoch = match start.duration_since(UNIX_EPOCH) {
        Ok(n) => n,
        Err(_) => {
            return Err("Could not get time!".to_string());
        }
    };
    // println!("debug: time: {}", since_the_epoch.as_millis());
    Ok(Long(since_the_epoch.as_millis() as i64))
}

fn value_to_string(v: &Value) -> String {
    match v {
        Float(x) => format!("{}", x),
        Long(x) => format!("{}", x),
        Bool(x) => format!("{}", x),
        Nil => "NIL".to_string(),
        PhoenixString(x) => x.to_string(),
        _ => {
            todo!("to_string() not implemented for this type")
        }
    }
}

pub const fn get_type_string(v: &Value) -> &str {
    match v {
        Float(_) => "float",
        Long(_) => "long",
        Bool(_) => "bool",
        Nil => "nil",
        PhoenixString(_) => "string",
        PhoenixFunction(_) => "function",
        PhoenixClass(_) => "class",
        PhoenixList(_) => "list",
        NativeFunction(_, _) => "native_function",
        PhoenixPointer(_) => "pointer",
        PhoenixBoundMethod(_) => "bound_method",
        _ => "unknown (Please report this bug)",
    }
}

/// This formats and prints messages to the console like print!("{} {}", "Hello", "World"); in rust
fn printf(args: Vec<Value>) -> Result<Value, String> {
    match &args[0] {
        PhoenixString(s) => {
            // get all {} in the string and replace it with the corresponding argument
            let mut args = args.clone();
            args.remove(0);
            let mut i = 0;
            let mut text = s.clone();
            while i < text.len() {
                if text.chars().nth(i).unwrap() == '{' && text.chars().nth(i + 1).unwrap() == '}' {
                    text.remove(i);
                    text.remove(i);
                    if args.is_empty() {
                        return Err(format!(
                            "[printf] To few arguments! Expected {} got {}!",
                            i + 1,
                            args.len()
                        ));
                    }
                    text.insert_str(i, &value_to_string(args.get(0).unwrap()));
                    args.remove(0);
                }
                i += 1;
            }
            println!("{}", text);
        }
        _ => {
            return Err("First argument must be a string!".to_string());
        }
    }

    Ok(Nil)
}

pub fn int(args: Vec<Value>) -> Result<Value, String> {
    Ok(match &args[0] {
        Float(f) => Long(*f as i64),
        Long(l) => Long(*l),
        Bool(b) => Long(*b as i64),
        PhoenixString(s) => Long(match s.parse::<i64>() {
            Ok(i) => i,
            Err(_) => {
                return Err(format!("Could not convert \"{s}\" to int!"));
            }
        }),
        _ => {
            return Err(format!(
                "Could not convert {} to int!",
                get_type_string(&args[0])
            ));
        }
    })
}

pub fn float(args: Vec<Value>) -> Result<Value, String> {
    Ok(match &args[0] {
        Float(f) => Float(*f),
        Long(l) => Float(*l as f32),
        Bool(b) => Float(*b as i32 as f32),
        PhoenixString(s) => Float(
            match if s.ends_with('f') {
                &s[0..s.len() - 1]
            } else {
                s
            }
            .parse::<f32>()
            {
                Ok(i) => i,
                Err(_) => {
                    return Err(format!("Could not convert {} to float!", s));
                }
            },
        ),
        _ => {
            return Err(format!(
                "Could not convert {} to float!",
                get_type_string(&args[0])
            ));
        }
    })
}