1package jid
2
3import (
4	"io"
5	"strings"
6
7	termbox "github.com/nsf/termbox-go"
8)
9
10const (
11	DefaultY     int    = 1
12	FilterPrompt string = "[Filter]> "
13)
14
15type EngineInterface interface {
16	Run() EngineResultInterface
17	GetQuery() QueryInterface
18}
19
20type EngineResultInterface interface {
21	GetQueryString() string
22	GetContent() string
23	GetError() error
24}
25
26type Engine struct {
27	manager        *JsonManager
28	query          QueryInterface
29	queryCursorIdx int
30	term           *Terminal
31	complete       []string
32	keymode        bool
33	candidates     []string
34	candidatemode  bool
35	candidateidx   int
36	contentOffset  int
37	queryConfirm   bool
38	prettyResult   bool
39}
40
41type EngineAttribute struct {
42	DefaultQuery string
43	Monochrome   bool
44	PrettyResult bool
45}
46
47func NewEngine(s io.Reader, ea *EngineAttribute) (EngineInterface, error) {
48	j, err := NewJsonManager(s)
49	if err != nil {
50		return nil, err
51	}
52	e := &Engine{
53		manager:       j,
54		term:          NewTerminal(FilterPrompt, DefaultY, ea.Monochrome),
55		query:         NewQuery([]rune(ea.DefaultQuery)),
56		complete:      []string{"", ""},
57		keymode:       false,
58		candidates:    []string{},
59		candidatemode: false,
60		candidateidx:  0,
61		contentOffset: 0,
62		queryConfirm:  false,
63		prettyResult:  ea.PrettyResult,
64	}
65	e.queryCursorIdx = e.query.Length()
66	return e, nil
67}
68
69type EngineResult struct {
70	content string
71	qs      string
72	err     error
73}
74
75func (er *EngineResult) GetQueryString() string {
76	return er.qs
77}
78
79func (er *EngineResult) GetContent() string {
80	return er.content
81}
82func (er *EngineResult) GetError() error {
83	return er.err
84}
85
86func (e *Engine) GetQuery() QueryInterface {
87	return e.query
88}
89
90func (e *Engine) Run() EngineResultInterface {
91
92	err := termbox.Init()
93	if err != nil {
94		panic(err)
95	}
96	defer termbox.Close()
97
98	var contents []string
99
100	for {
101
102		if e.query.StringGet() == "" {
103			e.query.StringSet(".")
104			e.queryCursorIdx = e.query.Length()
105		}
106
107		bl := len(contents)
108		contents = e.getContents()
109		e.setCandidateData()
110		e.queryConfirm = false
111		if bl != len(contents) {
112			e.contentOffset = 0
113		}
114
115		ta := &TerminalDrawAttributes{
116			Query:           e.query.StringGet(),
117			Contents:        contents,
118			CandidateIndex:  e.candidateidx,
119			ContentsOffsetY: e.contentOffset,
120			Complete:        e.complete[0],
121			Candidates:      e.candidates,
122			CursorOffset:    e.query.IndexOffset(e.queryCursorIdx),
123		}
124		err = e.term.Draw(ta)
125		if err != nil {
126			panic(err)
127		}
128
129		switch ev := termbox.PollEvent(); ev.Type {
130		case termbox.EventKey:
131			switch ev.Key {
132			case 0:
133				e.inputChar(ev.Ch)
134			case termbox.KeyBackspace, termbox.KeyBackspace2:
135				e.deleteChar()
136			case termbox.KeyTab:
137				e.tabAction()
138			case termbox.KeyArrowLeft, termbox.KeyCtrlB:
139				e.moveCursorBackward()
140			case termbox.KeyArrowRight, termbox.KeyCtrlF:
141				e.moveCursorForward()
142			case termbox.KeyHome, termbox.KeyCtrlA:
143				e.moveCursorToTop()
144			case termbox.KeyEnd, termbox.KeyCtrlE:
145				e.moveCursorToEnd()
146			case termbox.KeyCtrlK:
147				e.scrollToAbove()
148			case termbox.KeyCtrlJ:
149				e.scrollToBelow()
150			case termbox.KeyCtrlG:
151				e.scrollToBottom(len(contents))
152			case termbox.KeyCtrlT:
153				e.scrollToTop()
154			case termbox.KeyCtrlN:
155				_, h := termbox.Size()
156				e.scrollPageDown(len(contents), h)
157			case termbox.KeyCtrlP:
158				_, h := termbox.Size()
159				e.scrollPageUp(h)
160			case termbox.KeyCtrlL:
161				e.toggleKeymode()
162			case termbox.KeyCtrlU:
163				e.deleteLineQuery()
164			case termbox.KeyCtrlW:
165				e.deleteWordBackward()
166			case termbox.KeyEsc:
167				e.escapeCandidateMode()
168			case termbox.KeyEnter:
169				if !e.candidatemode {
170					var cc string
171					var err error
172					if e.prettyResult {
173						cc, _, _, err = e.manager.GetPretty(e.query, true)
174					} else {
175						cc, _, _, err = e.manager.Get(e.query, true)
176					}
177
178					return &EngineResult{
179						content: cc,
180						qs:      e.query.StringGet(),
181						err:     err,
182					}
183				}
184				e.confirmCandidate()
185			case termbox.KeyCtrlC:
186				return &EngineResult{}
187			default:
188			}
189		case termbox.EventError:
190			panic(ev.Err)
191			break
192		default:
193		}
194	}
195}
196
197func (e *Engine) getContents() []string {
198	var c string
199	var contents []string
200	c, e.complete, e.candidates, _ = e.manager.GetPretty(e.query, e.queryConfirm)
201	if e.keymode {
202		contents = e.candidates
203	} else {
204		contents = strings.Split(c, "\n")
205	}
206	return contents
207}
208
209func (e *Engine) setCandidateData() {
210	if l := len(e.candidates); e.complete[0] == "" && l > 1 {
211		if e.candidateidx >= l {
212			e.candidateidx = 0
213		}
214	} else {
215		e.candidatemode = false
216	}
217	if !e.candidatemode {
218		e.candidateidx = 0
219		e.candidates = []string{}
220	}
221}
222
223func (e *Engine) confirmCandidate() {
224	_, _ = e.query.PopKeyword()
225	_ = e.query.StringAdd(".")
226	_ = e.query.StringAdd(e.candidates[e.candidateidx])
227	e.queryCursorIdx = e.query.Length()
228	e.queryConfirm = true
229}
230
231func (e *Engine) deleteChar() {
232	if i := e.queryCursorIdx - 1; i > 0 {
233		_ = e.query.Delete(i)
234		e.queryCursorIdx--
235	}
236
237}
238
239func (e *Engine) deleteLineQuery() {
240	_ = e.query.StringSet("")
241	e.queryCursorIdx = 0
242}
243
244func (e *Engine) scrollToBelow() {
245	e.contentOffset++
246}
247
248func (e *Engine) scrollToAbove() {
249	if o := e.contentOffset - 1; o >= 0 {
250		e.contentOffset = o
251	}
252}
253
254func (e *Engine) scrollToBottom(rownum int) {
255	e.contentOffset = rownum - 1
256}
257
258func (e *Engine) scrollToTop() {
259	e.contentOffset = 0
260}
261
262func (e *Engine) scrollPageDown(rownum int, height int) {
263	co := rownum - 1
264	if o := rownum - e.contentOffset; o > height {
265		co = e.contentOffset + (height - DefaultY)
266	}
267	e.contentOffset = co
268}
269
270func (e *Engine) scrollPageUp(height int) {
271	co := 0
272	if o := e.contentOffset - (height - DefaultY); o > 0 {
273		co = o
274	}
275	e.contentOffset = co
276}
277
278func (e *Engine) toggleKeymode() {
279	e.keymode = !e.keymode
280}
281func (e *Engine) deleteWordBackward() {
282	if k, _ := e.query.StringPopKeyword(); k != "" && !strings.Contains(k, "[") {
283		_ = e.query.StringAdd(".")
284	}
285	e.queryCursorIdx = e.query.Length()
286}
287func (e *Engine) tabAction() {
288	if !e.candidatemode {
289		e.candidatemode = true
290		if e.complete[0] != e.complete[1] && e.complete[0] != "" {
291			if k, _ := e.query.StringPopKeyword(); !strings.Contains(k, "[") {
292				_ = e.query.StringAdd(".")
293			}
294			_ = e.query.StringAdd(e.complete[1])
295		} else {
296			_ = e.query.StringAdd(e.complete[0])
297		}
298	} else {
299		e.candidateidx = e.candidateidx + 1
300	}
301	e.queryCursorIdx = e.query.Length()
302}
303func (e *Engine) escapeCandidateMode() {
304	e.candidatemode = false
305}
306func (e *Engine) inputChar(ch rune) {
307	_ = e.query.Insert([]rune{ch}, e.queryCursorIdx)
308	e.queryCursorIdx++
309}
310
311func (e *Engine) moveCursorBackward() {
312	if i := e.queryCursorIdx - 1; i >= 0 {
313		e.queryCursorIdx--
314	}
315}
316
317func (e *Engine) moveCursorForward() {
318	if e.query.Length() > e.queryCursorIdx {
319		e.queryCursorIdx++
320	}
321}
322
323func (e *Engine) moveCursorWordBackwark() {
324}
325func (e *Engine) moveCursorWordForward() {
326}
327func (e *Engine) moveCursorToTop() {
328	e.queryCursorIdx = 0
329}
330func (e *Engine) moveCursorToEnd() {
331	e.queryCursorIdx = e.query.Length()
332}
333