/// String library for Atlas `std::string`
///
/// Provides a String struct with various string manipulation functions.

import "std/vector";

// Miscellaneous functions for raw strings
public extern str_len(s: string) -> uint64;
public extern trim(s: string) -> string;
public extern to_upper(s: string) -> string;
public extern to_lower(s: string) -> string;
public extern split(s: string, sep: string) -> [string];
public extern str_cmp(s1: string, s2: string) -> int64;
public extern to_chars(s: string) -> [char];
public extern from_chars(s: [char]) -> string;


public struct String {
public:
    s: string;
    len: uint64;

    String(s: string) {
        this.s = s;
        this.len = str_len(s);
    }

    ~String() {}

    fun from_chars(s: [char]) -> String {
        return new String(from_chars(s));
    }

    /// Example: String::str_len("Hello") -> 5
    fun str_len(s: string) -> uint64 {
        return str_len(s);
    }

    fun len(this) -> uint64 {
        return this.len;
    }

    fun is_empty(this) -> bool {
        return this.len == 0 as uint64;
    }
    //todo: implement as_bytes
    //fun as_bytes(this) -> [uint8] {}
    fun concat(this, other: String) -> String {
        //I should really implement the <- operator for copying objects
        let self_chars = to_chars(this.s);
        let other_chars = to_chars(other.s);
        let res = new [char; len(self_chars) + len(other_chars)];
        let i = 0;
        while i < len(self_chars) {
            res[i] = self_chars[i];
            i = i + 1;
        }
        return new String(from_chars(res));
    }

    fun push(this, c: char) -> unit {
        //Same... I should implement the <- operator
        let self_chars = to_chars(this.s);
        let res = new [char; len(self_chars) + 1];
        let i = 0;
        while i < len(self_chars) {
            res[i] = self_chars[i];
            i = i + 1;
        }
        res[i] = c;
        this.s = from_chars(res);
        this.len = str_len(this.s);
        return;
    }

    fun push_str(this, s: String) -> unit {
        let self_chars = to_chars(this.s);
        let s_chars = to_chars(s.s);
        let res = new [char; len(self_chars) + len(s_chars)];
        let i = 0;
        while i < len(self_chars) {
            res[i] = self_chars[i];
            i = i + 1;
        }
        let j = 0;
        while j < len(s_chars) {
            res[i] = s_chars[j];
            i = i + 1;
            j = j + 1;
        }
        this.s = from_chars(res);
        this.len = str_len(this.s);
        return;
    }

    fun find(this, sub_string: String) -> int64 {
        let res = -1;
        let i = 0;
        let j = 0;
        let self_chars = to_chars(this.s);
        let sub_chars = to_chars(sub_string.s);
        while i < len(self_chars) {
            if self_chars[i] == sub_chars[j] {
                const size_sub_chars: int64 = len(sub_chars) as int64 - 1;
                if j == size_sub_chars {
                    res = i - j;
                    //break is not implemented yet...
                    //break;
                }
                j = j + 1;
            } else {
                j = 0;
            }
            i = i + 1;
        }
        return res;
    }

    fun get(this, i: uint64) -> char {
        return this.s[i];
    }

    fun set(this, i: uint64, c: char) -> unit {
        this.s[i] = c;
        return;
    }

    fun to_str(this) -> string {
        return this.s;
    }

    fun to_chars(this) -> [char] {
        return to_chars(this.s);
    }

    fun to_upper(this) -> String {
        return new String(to_upper(this.s));
    }

    fun to_lower(this) -> String {
        return new String(to_lower(this.s));
    }

    fun trim(this) -> String {
        return new String(trim(this.s));
    }

    fun split(this, sep: string) -> [String] {
        let s: [string] = split(this.s, sep);
        let i = 0;
        let res = new [String; len(s)];
        while i < len(s) {
            res[i] = new String(s[i]);
            i = i + 1;
        }
        return res;
    }
}
