; Load prelude so we get fun, lambda, if, quoting, etc
(load-file "./k_prime_stdlib/prelude.kp")
; First quick lookup function, since maps are not built in
(fun get-value-helper (dict key i) (if (>= i (len dict))
                                                nil
                                                (if (= key (idx (idx dict i) 0))
                                                    (idx (idx dict i) 1)
                                                    (get-value-helper dict key (+ i 1)))))
(fun get-value (dict key) (get-value-helper dict key 0))

; Our actual method call function
(fun method-call (object method & arguments) (let (method_fn (get-value (meta object) method))
                                                            (if (= method_fn nil)
                                                                (println "no method " method)
                                                                (lapply method_fn (concat [object] arguments)))))
; Some nice syntactic sugar for method calls
; No params
(add_grammar_rule 'form ['form "\\." 'atom]
                            (lambda (o _ m) `(method-call ~o '~m)))
; params
(add_grammar_rule 'form ['form "\\." 'atom 'optional_WS "\\(" 'optional_WS 'space_forms 'optional_WS "\\)"]
                            (lambda (o _ m _ _ _ p _ _) `(method-call ~o '~m ,p)))

; object creation
(fun make_constructor (members methods)
    (eval `(lambda  ~members
        (with-meta [,members]
            [,(map_with_idx (lambda (i x) [array `'~x (lambda (o) (idx o i))]) members)
             ,(map (lambda (x) [array `'~(idx x 0) (idx x 1)]) methods)]))))

; object syntax
(add_grammar_rule 'form ["obj" 'WS 'atom "\\(" ['optional_WS 'atom] * 'optional_WS "\\)" 'optional_WS "{" 'optional_WS ['atom 'optional_WS 'form 'optional_WS] *  "}"] (lambda (_ _ name _ members _ _ _ _ _ methods _)
        `(set! ~name (make_constructor [,(map (lambda (x) `'~(idx x 1)) members)]
                                       [,(map (lambda (x) `['~(idx x 0) ~(idx x 2)]) methods)]))))

; Lambda syntax
(add_grammar_rule 'form ["\\|" 'optional_WS [ 'atom 'optional_WS ] * "\\|" 'optional_WS 'form ]
                    (lambda (_ _ params _ _ body) `(lambda (,(map (lambda (x) (idx x 0)) params)) ~body)))

; {} body translated to do and let
(add_grammar_rule 'block_member [ 'form ] |x| [x])
(add_grammar_rule 'block_member [ "let" 'optional_WS 'atom 'optional_WS "=" 'optional_WS 'form ]
                            |_ _ name _ _ _ rhs| `(~name ~rhs))
(fun construct_body (is_do current to_add i)
    (if (> (len to_add) i)
        (cond (and is_do (= (len (idx to_add i)) 1)) (construct_body true (concat current [(idx (idx to_add i) 0)]) to_add (+ i 1))
              (= (len (idx to_add i)) 1)             (concat current [(construct_body true [do (idx (idx to_add i) 0)] to_add (+ i 1))])
              true                                   (concat current [(construct_body false [let [(idx (idx to_add i) 0) (idx (idx to_add i) 1)] ] to_add (+ i 1))]))
        current))
(add_grammar_rule 'form ["{" 'optional_WS [ 'block_member 'optional_WS ] * "}"]
    |_ _ inner _| (construct_body true [do] (map |x| (idx x 0) inner) 0))

; Call functions with function first, c style (notice no whitespace)
(add_grammar_rule 'form [ 'form 'call_form ] |f ps| (concat [f] ps))

; fun syntax
(add_grammar_rule 'form [ "fun" 'WS 'atom 'optional_WS "\\(" 'optional_WS [ 'atom 'optional_WS ] * "\\)" 'optional_WS 'form ]
    |_ _ name _ _ _ params _ _ body| `(fun ~name (,(map |x| (idx x 0) params)) ~body))

; string interpolation
fun remove_dollar(done to_do i j) (cond (>= j (- (len to_do) 2))          (str done (slice to_do i -1))
                                        (= "\\$" (slice to_do j (+ j 2))) (remove_dollar (str done (slice to_do i j) "$") to_do (+ j 2) (+ j 2))
                                        true                              (remove_dollar done to_do i (+ j 1)))
fun fixup_str_parts(s) (remove_dollar "" (slice s 0 -2) 0 0)
(add_grammar_rule 'form [ "$\"" [ "(#|[%-[]|	|[]-~]|(\\\\)|(\\n)|(\\t)|(\\*)|(\\\\$)|
|[ -!]|(\\\\\"))*$" 'form ] * "(#|[%-[]|	|[]-~]|(\\\\)|(\\n)|(\\t)|(\\*)|(\\\\$)|
|[ -!]|(\\\\\"))*\"" ]
    |_ string_form_pairs end| `(str ,( flat_map |x| [ (fixup_str_parts (idx x 0)) (idx x 1) ] string_form_pairs) ~(fixup_str_parts end)))

(println $"unu |\$| $$"inner $(+ 1 2) post-inner" sual")

obj Point( x y ) {
    add |self other| { Point((+ self.x other.x) (+ self.y other.y)) }
    sub |self other| { Point((- self.x other.x) (- self.y other.y)) }
    to_str |self| { str("x: " self.x ", y: " self.y) }
}

fun say_hi(name) {
    println("hayo" name)
}

fun test() {
    let plus_1 = |x| (+ x 1)
    let a = 1
    let b = plus_1(a)
    println("some" b)

    say_hi("Marcus")

    let p1 = Point(1 2)
    let p2 = Point(3 4)
    let p3 = p1.add(p2)
    let p4 = p1.sub(p2)

    println("p1:" p1.to_str)
    println("p2:" p2.to_str)
    println("p3:" p3.to_str)
    println("p4:" p4.to_str)

    (+ a b)
}
println("Test result is" test())

