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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
//! # Positioning elements in HyperAST
//!
//! Because the HyperAST is a Direct Acyclic Graph (DAG),
//! any given sub-tree possibly has multiple global positions.
//! The global position, path, or offset of a subtree is global/contextual information, thus it cannot be stored efficiently on subtrees of a DAG.
//!
//! You can look at this module as an example of computing global metrics out of local ones.
//!
//! This module specifically helps with tasks related to positioning nodes globally,
//! - it helps maintain positional states during traversals
//! - it helps convert between positional representations
//!     - topological
//!     - path (list of offsets)
//!     - a file path, an offset and a length
//!     - with/out hidden nodes
//!
//! ## Incremental position storing
//! [structural_pos]
//!
//! ## topological index
//! [topological_offset]
//!     - post-order
//! ##  path
//! [offsets_and_nodes]
//!     - list of offsets
//! ## collection of path
//!     Optimisation related, sometimes necessary to have acceptable perfs.
//!     - list of paths
//!     - it of paths
//!     - topo ordered list of paths
//!       incremental compute
//!     - reversed dag of paths
//!       mem optimization,
//! ## with hidden nodes (spaces, abtract nodes,....)

use std::{fmt::Debug, path::PathBuf};

use num::traits::NumAssign;

use crate::{
    store::defaults::NodeIdentifier,
    types::{HyperAST, NodeId, TypedNodeId, WithChildren},
};

pub trait PrimInt: num::PrimInt + NumAssign + Debug {}

impl<T> PrimInt for T where T: num::PrimInt + NumAssign + Debug {}

pub trait TreePath<IdN = NodeIdentifier, Idx = u16> {
    fn node(&self) -> Option<&IdN>;
    fn offset(&self) -> Option<&Idx>;
    fn check<'store, HAST>(&self, stores: &'store HAST) -> Result<(), ()>
    where
        HAST: HyperAST<'store, IdN = IdN::IdN>,
        HAST::T: WithChildren<ChildIdx = Idx>,
        HAST::IdN: Eq,
        IdN: NodeId,
        IdN::IdN: NodeId<IdN = IdN::IdN>;
}

pub trait TreePathMut<IdN, Idx>: TreePath<IdN, Idx> {
    fn pop(&mut self) -> Option<(IdN, Idx)>;
    fn goto(&mut self, node: IdN, i: Idx);
    fn inc(&mut self, node: IdN);
    fn dec(&mut self, node: IdN);
}

pub trait TypedTreePath<TIdN: TypedNodeId, Idx>: TreePath<TIdN::IdN, Idx> {
    fn node_typed(&self) -> Option<&TIdN>;
    fn pop_typed(&mut self) -> Option<(TIdN, Idx)>;
    fn goto_typed(&mut self, node: TIdN, i: Idx);
}

pub mod position_accessors {
    // we want to represent the possible combination which produce a global position in the HyperAST
    // 1) root + file path + offset + len
    // 2) root + path (nodes to consider)
    // 3) root + topological index (nodes to consider)
    // 4) parents + offsets + node (nodes to consider)
    //
    // to test position equality roots must be equal,
    // a shared combination of attributes is chosen for the final comparison

    use super::*;

    pub trait RootedPosition<IdN> {
        fn root(&self) -> IdN;
    }

    pub trait SolvedPosition<IdN> {
        fn node(&self) -> IdN;
    }

    pub trait WithOffsets
    where
        Self::Idx: PrimInt,
    {
        type Idx;
        // type PreOrderIt: Iterator<Item=Self::Idx>;
        // fn iter_pre_order(&self) -> Self::PreOrderIt;
        // fn iter_pre_order(&self) -> Box<dyn Iterator<Item=Self::Idx>> {
        //     todo!()
        // }
        // type PostOrderIt: Iterator<Item=Self::Idx>;
        // fn iter_post_order(&self) -> Self::PostOrderIt;
    }

    pub trait WithPath<IdN>: WithOffsets {
        // fn iter_pre_order(&self) -> Self::PreOrderIt;
        // fn iter_post_order(&self) -> Self::PostOrderIt;
    }

    pub trait WithPreOrderOffsets: WithOffsets {
        // type Path: Iterator;
        // fn path(&self) -> Self::Path;
        type It: Iterator<Item = Self::Idx>;
        fn iter(&self) -> Self::It;
    }

    /// test invariants with [assert_invariants_pre]
    pub trait WithPreOrderPath<IdN>: WithPath<IdN> + WithPreOrderOffsets {
        // type Path: Iterator;
        // fn path(&self) -> Self::Path;
        type ItPath: Iterator<Item = (Self::Idx, IdN)>;
        fn iter_offsets_and_nodes(&self) -> Self::ItPath;
    }

    #[cfg(debug_assertions)]
    pub fn assert_invariants_pre<'store, IdN, P, HAST>(p: &P, store: &'store HAST)
    where
        IdN: std::cmp::Eq + std::hash::Hash + std::fmt::Debug + Clone + NodeId,
        P: WithPreOrderPath<IdN> + RootedPosition<IdN>,
        HAST: HyperAST<'store, IdN = IdN::IdN, Idx = P::Idx>,
        <IdN as NodeId>::IdN: PartialEq<<<IdN as NodeId>::IdN as NodeId>::IdN>,
        <IdN as NodeId>::IdN: std::fmt::Debug,
        <<IdN as NodeId>::IdN as NodeId>::IdN: std::fmt::Debug,
    {
        use crate::types::NodeStore;
        use std::collections::HashSet;
        let mut set: HashSet<IdN> = HashSet::default();
        let root = p.root();
        let mut prev = root.clone();
        let it = p.iter_offsets_and_nodes();
        let snd_it = p.iter();
        set.insert(root);
        for ((o0, x), o1) in it.into_iter().zip(snd_it) {
            assert_eq!(o0, o1);
            if !set.insert(x.clone()) {
                panic!("path returns 2 times the same node")
            }
            let b = store.node_store().resolve(prev.as_id());
            assert_eq!(x.as_id(), &b.child(&o0).expect("should have a child"));
            prev = x.clone();
        }
    }

    pub trait WithPostOrderOffsets: WithOffsets {
        // type Path: Iterator;
        // fn path(&self) -> Self::Path;
        type It: Iterator<Item = Self::Idx>;
        fn iter(&self) -> Self::It;
        // TODO into_iter ?
    }

    /// test invariants with [assert_invariants_post]
    pub trait WithPostOrderPath<IdN>: WithPath<IdN> + WithPostOrderOffsets {
        // type Path: Iterator;
        // fn path(&self) -> Self::Path;
        type ItPath: Iterator<Item = (Self::Idx, IdN)>;
        fn iter_offsets_and_parents(&self) -> Self::ItPath;
    }

    /// - p should only return each node once
    /// - resolved children should correspond
    #[cfg(debug_assertions)]
    pub fn assert_invariants_post<'store, IdN, P, HAST>(p: &P, store: &'store HAST)
    where
        IdN: std::cmp::Eq + std::hash::Hash + std::fmt::Debug + Clone + NodeId,
        P: WithPostOrderPath<IdN> + SolvedPosition<IdN>,
        HAST: HyperAST<'store, IdN = IdN::IdN, Idx = P::Idx>,
        <IdN as NodeId>::IdN: PartialEq<<<IdN as NodeId>::IdN as NodeId>::IdN>,
        <IdN as NodeId>::IdN: std::fmt::Debug,
        <<IdN as NodeId>::IdN as NodeId>::IdN: std::fmt::Debug,
    {
        use crate::types::NodeStore;
        use std::collections::HashSet;
        let mut set: HashSet<IdN> = HashSet::default();
        let node = p.node();
        let mut prev = node.clone();
        let it = p.iter_offsets_and_parents();
        let snd_it = p.iter();
        set.insert(node);
        for ((o0, x), o1) in it.into_iter().zip(snd_it) {
            assert_eq!(o0, o1);
            if !set.insert(x.clone()) {
                panic!("path returns 2 times the same node")
            }
            let b = store.node_store().resolve(x.as_id());
            assert_eq!(prev.as_id(), &b.child(&o0).expect("should have a child"));
            prev = x.clone();
        }
    }

    /// test invariants with [assert_invariants_post_full]
    pub trait WithFullPostOrderPath<IdN>: RootedPosition<IdN> + WithPostOrderPath<IdN> {
        fn iter_with_nodes(&self) -> (IdN, Self::ItPath);
    }

    /// - p should only return each node once
    /// - resolved children should corespond
    #[cfg(debug_assertions)]
    pub fn assert_invariants_post_full<'store, IdN, P, HAST>(p: &P, store: &'store HAST)
    where
        IdN: std::cmp::Eq + std::hash::Hash + std::fmt::Debug + Clone + NodeId,
        P: WithFullPostOrderPath<IdN>,
        HAST: HyperAST<'store, IdN = IdN::IdN, Idx = P::Idx>,
        <IdN as NodeId>::IdN: PartialEq<<<IdN as NodeId>::IdN as NodeId>::IdN>,
        <IdN as NodeId>::IdN: std::fmt::Debug,
        <<IdN as NodeId>::IdN as NodeId>::IdN: std::fmt::Debug,
    {
        use crate::types::NodeStore;
        use std::collections::HashSet;
        let mut set: HashSet<IdN> = HashSet::default();
        let (node, it) = p.iter_with_nodes();
        let mut prev = node.clone();
        let snd_it = p.iter_offsets_and_parents();
        let third_it = p.iter();
        set.insert(node);
        for (((o0, x), (o1, y)), o2) in it.into_iter().zip(snd_it).zip(third_it) {
            dbg!(&prev, o0);
            assert_eq!(x, y);
            assert_eq!(o0, o1);
            assert_eq!(o2, o1);
            if !set.insert(x.clone()) {
                panic!("path returns 2 times the same node")
            }
            let b = store.node_store().resolve(x.as_id());
            assert_eq!(prev.as_id(), &b.child(&(o0)).expect("should have a child"));
            prev = x.clone();
        }
    }

    pub trait TopoIndexPositionT<IdN>
    where
        Self::IdI: PrimInt,
    {
        type IdI;
        fn index(&self) -> Self::IdI;
    }
    pub trait FileAndOffsetPostionT<IdN>
    where
        Self::IdO: PrimInt,
    {
        /// Offset in characters or bytes
        type IdO;
        fn file(&self) -> std::path::PathBuf;
        fn offset(&self) -> Self::IdO;
        fn len(&self) -> Self::IdO;
        fn start(&self) -> Self::IdO;
        fn end(&self) -> Self::IdO;
    }
}

pub struct PositionConverter<'src, SrcPos> {
    src: &'src SrcPos,
}

impl<'src, SrcPos> PositionConverter<'src, SrcPos> {
    pub fn new(src: &'src SrcPos) -> Self {
        Self { src }
    }
    pub fn with_stores<'store, HAST>(
        self,
        stores: &'store HAST,
    ) -> WithHyperAstPositionConverter<'store, 'src, SrcPos, HAST> {
        WithHyperAstPositionConverter {
            src: self.src,
            stores,
        }
    }
}

pub struct WithHyperAstPositionConverter<'store, 'src, SrcPos, HAST> {
    src: &'src SrcPos,
    stores: &'store HAST,
}

pub mod building;

pub mod tags {
    #[derive(Clone, Copy, Debug)]
    pub struct TopDownNoSpace;
    #[derive(Clone, Copy, Debug)]
    pub struct TopDownFull;
    #[derive(Clone, Copy, Debug)]
    pub struct BottomUpNoSpace;
    #[derive(Clone, Copy, Debug)]
    pub struct BottomUpFull;
}

mod node_filter_traits {
    pub trait NoSpace {}
    pub trait Full {}
}

pub use building::CompoundPositionPreparer;

pub mod offsets;

pub mod file_and_offset;

pub type Position = file_and_offset::Position<PathBuf, usize>;

pub mod offsets_and_nodes;
pub use offsets_and_nodes::*;

mod topological_offset;
pub use topological_offset::*;

mod spaces_related;
pub use spaces_related::{
    compute_position_and_nodes_with_no_spaces, compute_position_with_no_spaces,
    global_pos_with_spaces, path_with_spaces,
};

mod computing_offset_bottom_up;
// pub use computing_offset_bottom_up::{extract_file_postion, extract_position};

mod computing_offset_top_down;
pub use computing_offset_top_down::{compute_position, compute_position_and_nodes, compute_range};

mod computing_path;
pub use computing_path::resolve_range;

// advanced optimization, uses a dag StructuralPositionStore to share parent paths

mod structural_pos;
pub use structural_pos::{
    ExploreStructuralPositions, Scout, SpHandle, StructuralPositionStore, TypedScout,
};
pub type StructuralPosition<IdN = NodeIdentifier, Idx = u16> =
    structural_pos::StructuralPosition<IdN, Idx>;