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
mod conductor;
mod performer;

pub mod chord;

use self::conductor::{Conductor, Hint, Message};

use crate::model::{CommonStyles, Renderer, Schema, TaggedValue};

use std::borrow::Cow;
use std::io;
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
use std::thread::JoinHandle;

pub type Music = Vec<u8>;

pub struct Orchestra {
    styles: CommonStyles,
    pipe: SyncSender<Message>,
    cond: (JoinHandle<Result<(), OrchError>>, Receiver<Music>),
}

impl Orchestra {
    pub fn new<S>(schema: S) -> io::Result<Orchestra>
    where
        S: Schema + Clone + 'static,
    {
        let (sender, receiver) = sync_channel(32);

        let styles = schema.get_common_styles();

        let renderer = Renderer; // ::new (&cset);
        let schema = schema;

        let cond = Conductor::run(receiver, renderer, schema)?;

        Ok(Orchestra {
            styles: styles,
            pipe: sender,
            cond: cond,
        })
    }

    pub fn get_styles(&self) -> CommonStyles {
        self.styles
    }

    pub fn play(&self, level: usize, value: TaggedValue) -> Result<(), OrchError> {
        self.pipe
            .send(Message::Value(level, value))
            .or_else(|_| Err(OrchError::Error("Conductor has quit already".to_string())))
    }

    pub fn volume_border_top(&self, print: bool) -> Result<(), OrchError> {
        self.pipe
            .send(Message::Hint(Hint::BorderTop(print)))
            .or_else(|_| Err(OrchError::Error("Conductor has quit already".to_string())))
    }

    pub fn volume_border_bot(&self, print: bool) -> Result<(), OrchError> {
        self.pipe
            .send(Message::Hint(Hint::BorderBot(print)))
            .or_else(|_| Err(OrchError::Error("Conductor has quit already".to_string())))
    }

    pub fn directive_yaml(&self, print: bool) -> Result<(), OrchError> {
        self.pipe
            .send(Message::Hint(Hint::DirectiveYaml(print)))
            .or_else(|_| Err(OrchError::Error("Conductor has quit already".to_string())))
    }

    pub fn directive_tags(
        &self,
        tags: Vec<(Cow<'static, str>, Cow<'static, str>)>,
    ) -> Result<(), OrchError> {
        self.pipe
            .send(Message::Hint(Hint::DirectiveTags(tags)))
            .or_else(|_| Err(OrchError::Error("Conductor has quit already".to_string())))
    }

    pub fn volumes(&self, size: usize) -> Result<(), OrchError> {
        self.pipe
            .send(Message::Hint(Hint::Volumes(size)))
            .or_else(|_| Err(OrchError::Error("Conductor has quit already".to_string())))
    }

    pub fn vol_next(&self) -> Result<(), OrchError> {
        self.pipe
            .send(Message::Hint(Hint::VolumeNext))
            .or_else(|_| Err(OrchError::Error("Conductor has quit already".to_string())))
    }

    pub fn vol_end(&self) -> Result<(), OrchError> {
        self.pipe
            .send(Message::Hint(Hint::VolumeEnd))
            .or_else(|_| Err(OrchError::Error("Conductor has quit already".to_string())))
    }

    pub fn vol_reserve(&self, size: usize) -> Result<(), OrchError> {
        self.pipe
            .send(Message::Hint(Hint::VolumeSize(size)))
            .or_else(|_| Err(OrchError::Error("Conductor has quit already".to_string())))
    }

    pub fn the_end(&self) -> Result<(), OrchError> {
        self.pipe
            .send(Message::Hint(Hint::TheEnd))
            .or_else(|_| Err(OrchError::Error("Conductor has quit already".to_string())))
    }

    pub fn listen(&self) -> Result<Music, OrchError> {
        match self.cond.1.recv() {
            Ok(music) => Ok(music),
            Err(_) => Err(OrchError::Error(String::from("orchestra vanished"))),
        }
    }
}

pub enum OrchError {
    Error(String),
    IoError(io::Error),
}

impl From<io::Error> for OrchError {
    fn from(err: io::Error) -> OrchError {
        OrchError::IoError(err)
    }
}