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
#[cfg(feature = "lsp")]
pub(crate) mod lsp;
pub(crate) mod meta;

pub use self::meta::{
    author::{DocumentAuthor, DocumentAuthorIter},
    class::{DocumentClass, DocumentClassArticle},
    datetime::DocumentTime,
    title::DocumentTitle,
    toc::{TableOfContent, TocConfig, TocNode},
};
use crate::plugin_system::Parser;
use chrono::{NaiveDateTime, Utc};
use notedown_ast::{
    value::{LiteralPair, OrderedMap},
    ASTNode, Value,
};
use notedown_error::{NoteError, Result};
use std::collections::BTreeMap;
use yggdrasil_shared::{TextAdaptor, TextIndex, Url};

#[cfg(feature = "native")]
pub(crate) mod native_wrap {
    pub use crate::VMFileSystem;
    pub use chrono::{DateTime, TimeZone};
    pub use std::{
        fs::read_to_string,
        time::{SystemTime, UNIX_EPOCH},
    };
}
#[cfg(feature = "wasm")]
pub(crate) mod wasm_wrap {}
#[cfg(feature = "native")]
use native_wrap::*;
#[cfg(feature = "wasm")]
use wasm_wrap::*;

#[derive(Debug)]
pub struct NoteDocument {
    url: Url,
    /// used to check weather the file needs re-parse
    fingerprint: u128,
    text: TextIndex,
    ast: ASTNode,
    variable: OrderedMap,
    errors: Vec<NoteError>,
    meta: DocumentMeta,
}

#[derive(Debug, Clone)]
pub struct DocumentMeta {
    title: DocumentTitle,
    authors: BTreeMap<String, DocumentAuthor>,
    date: DocumentTime,
    toc: TocNode,
}

impl PartialEq for NoteDocument {
    fn eq(&self, other: &Self) -> bool {
        self.fingerprint.eq(&other.fingerprint)
    }
}

impl NoteDocument {
    #[inline]
    pub fn set_value_raw(&mut self, pair: LiteralPair) -> Option<LiteralPair> {
        self.variable.insert_raw(pair)
    }
    #[inline]
    pub fn set_value(&mut self, name: String, value: Value) -> Option<Value> {
        self.variable.insert(name, value)
    }
    #[inline]
    pub fn get_text(&self) -> String {
        self.text.text()
    }

    #[inline]
    pub fn get_text_index(&self) -> &TextIndex {
        &self.text
    }

    #[inline]
    pub fn get_ast(&self) -> &ASTNode {
        &self.ast
    }

    #[inline]
    pub fn extend_error_one(&mut self, e: NoteError) {
        self.errors.push(e)
    }
    #[inline]
    pub fn extend_error_iter(&mut self, e: impl Iterator<Item = NoteError>) {
        self.errors.extend(e.into_iter())
    }
    #[inline]
    pub fn can_gc(&self) -> bool {
        false
    }
}

impl NoteDocument {
    #[inline]
    pub async fn update_text(&mut self) -> Result<()> {
        let full = &Self::load_url(&self.url).await?;
        self.text.apply_change_full(full);
        Ok(())
    }
    #[inline]
    pub async fn update_document(&mut self, parse: &Parser) -> Result<()> {
        let mut errors = vec![];
        let parsed = parse(&self.text.text(), &mut errors)?;
        todo!()
    }
}

impl NoteDocument {
    pub async fn load_url(url: &Url) -> Result<String> {
        #[cfg(feature = "native")]
        match url.scheme() == "file" {
            true => Self::load_local_url(url).await,
            false => Self::load_remote_url_native(url).await,
        }
        #[cfg(feature = "wasm")]
        match url.scheme() == "file" {
            true => Err(NoteError::runtime_error("Can not load local file from wasm")),
            false => Self::load_remote_url_wasm(url).await,
        }
    }
    #[cfg(feature = "native")]
    #[inline(always)]
    async fn load_local_url(url: &Url) -> Result<String> {
        let path = url.to_file_path()?;
        Ok(read_to_string(path)?)
    }
    #[cfg(feature = "native")]
    #[inline(always)]
    async fn load_remote_url_native(url: &Url) -> Result<String> {
        todo!("Remote: {}", url)
    }
    #[cfg(feature = "wasm")]
    #[inline(always)]
    async fn load_remote_url_wasm(url: &Url) -> Result<String> {
        todo!("Remote: {}", url)
    }
}