1use std::{
6 cmp,
7 collections::VecDeque,
8};
9
10use log::*;
11
12use crate::{
13 context::{Context, ContextHeader},
14 line::Line,
15};
16
17enum FlipState {
18 StartContext,
19 Context,
20 Buffering,
21}
22
23impl Context {
24 pub fn flip(&self, mut context_radius: usize) -> VecDeque<Self> {
25 context_radius = cmp::max(1, context_radius);
26 context_radius = cmp::min(context_radius, self.opening_context_size());
27
28 let mut results = VecDeque::new();
29
30 let mut output = Self::default();
31 output.header.file1_l = self.header.file2_l;
32 output.header.file2_l = self.header.file1_l;
33
34 let mut state = FlipState::StartContext;
35 let mut deletes = Vec::new();
36 let mut inserts = Vec::new();
37
38 trace!("START");
39 for line in self.data.iter() {
40 match line {
41 Line::Context(_) => {
42 match state {
43 FlipState::StartContext => {
44 trace!("Context StartContext");
45 while output.opening_context_size() >= context_radius {
46 output.data.pop();
47 output.header.file1_l += 1;
48 output.header.file2_l += 1;
49 trace!("POP START");
50 }
51 output.data.push(line.clone());
52 },
53 FlipState::Context => {
54 trace!("Context Context");
55 output.data.push(line.clone());
56 let lines = output.closing_context_size();
57 if lines > 2 * context_radius {
58 let mut data = Vec::new();
59 data.push(output.data.pop().unwrap());
60 for _ in context_radius*2..lines {
61 output.data.pop();
62 trace!("POP END");
63 }
64 output.update();
65
66 trace!("PUSH OUTPUT");
67 let output_next = Context {
68 header: ContextHeader {
69 file1_l: output.header.file2_l + output.header.file2_s + lines - 2,
70 file1_s: Default::default(),
71 file2_l: output.header.file1_l + output.header.file1_s + lines - 2,
72 file2_s: Default::default(),
73 },
74 data,
75 };
76
77 if output.has_changes() {
78 results.push_back(output);
79 }
80 output = output_next;
81
82 state = FlipState::Context;
83 }
84 },
85 FlipState::Buffering => {
86 trace!("Context Buffering");
87 output.data.append(&mut deletes);
88 output.data.append(&mut inserts);
89 state = FlipState::Context;
90 output.data.push(line.clone());
91 },
92 }
93 },
94 Line::Delete(_) => {
95 if let FlipState::StartContext = state {
96 trace!("Delete StartContext");
97 state = FlipState::Buffering;
98 }
99 if let FlipState::Context = state {
100 trace!("Delete Context");
101 state = FlipState::Buffering;
102 }
103 if let FlipState::Buffering = state {
104 trace!("Delete Buffering");
105 inserts.push(line.flip());
106 }
107 },
108 Line::Insert(_) => {
109 trace!("INSERT");
110 if let FlipState::StartContext = state {
111 trace!("Insert StartContext");
112 state = FlipState::Buffering;
113 }
114 if let FlipState::Context = state {
115 trace!("Insert Context");
116 state = FlipState::Buffering;
117 }
118 if let FlipState::Buffering = state {
119 trace!("Insert Buffering");
120 deletes.push(line.flip());
121 }
122 },
123 }
124 }
125
126 let lines = output.closing_context_size();
127 if lines > context_radius {
128 for _ in context_radius..lines {
129 output.data.pop();
130 trace!("POP END");
131 }
132 }
133
134 output.update();
135 if output.has_changes() {
136 results.push_back(output);
137 }
138
139 trace!("END");
140
141 results
142 }
143}