use snafu::{ResultExt, Snafu};
use crate::server::Session;
use crate::server::aid::AID;
use crate::server::extended_field_attributes::ExtendedFieldAttribute;
use crate::server::stream::{
BufferAddressCalculator, IncomingRecord, StreamFormatError, WriteCommand, WriteCommandCode,
WriteOrder,
};
use crate::server::wcc::{FieldAttribute, WCC};
#[derive(Copy, Clone, Debug)]
pub struct Address {
pub row: u16,
pub col: u16,
}
pub enum FieldData<'a> {
RO(&'a str),
RW(&'a mut String),
}
impl<'a> AsRef<str> for FieldData<'a> {
fn as_ref(&self) -> &str {
match self {
FieldData::RO(data) => data,
FieldData::RW(data) => data,
}
}
}
pub struct Field<'a> {
pub address: Address,
pub attrs: Vec<ExtendedFieldAttribute>,
pub data: FieldData<'a>,
}
impl Field<'static> {
pub fn at(row: u16, col: u16) -> Self {
Self { address: Address { row, col }, attrs: vec![], data: FieldData::RO("") }
}
}
impl<'a> Field<'a> {
pub fn ro_text<'b>(self, text: &'b str) -> Field<'b> {
Field { address: self.address, attrs: self.attrs, data: FieldData::RO(text) }
}
pub fn rw_text<'b>(self, text: &'b mut String) -> Field<'b> {
Field { address: self.address, attrs: self.attrs, data: FieldData::RW(text) }
}
pub fn with_attr(mut self, attr: ExtendedFieldAttribute) -> Self {
self.attrs.push(attr);
self
}
}
pub struct Screen<'a> {
pub fields: Vec<Field<'a>>,
}
pub struct Response {
pub address: Address,
pub aid: AID,
}
#[derive(Snafu, Debug)]
pub enum ScreenError {
IoError { context: &'static str, source: std::io::Error },
StreamError { source: StreamFormatError },
}
impl<'a> Screen<'a> {
pub fn present(&mut self, session: &mut Session) -> Result<Response, ScreenError> {
let acalc = BufferAddressCalculator { width: 80, height: 24 };
{
let command = WriteCommand {
command: WriteCommandCode::EraseWrite,
wcc: WCC::RESET_MDT | WCC::KBD_RESTORE,
orders: self
.fields
.iter()
.flat_map(|field| {
use std::iter::*;
let Address { row, col } = field.address;
let bufaddr = acalc.encode_address(row, col);
let ro = matches!(field.data, FieldData::RO(_));
let mut field_attr = field.attrs.clone();
let mut have_fa = false;
for attr in field_attr.iter_mut() {
if let ExtendedFieldAttribute::FieldAttribute(attr) = attr {
attr.set(FieldAttribute::PROTECTED, ro);
have_fa = true;
}
}
if !have_fa {
field_attr.insert(
0,
ExtendedFieldAttribute::FieldAttribute(if ro {
FieldAttribute::PROTECTED
} else {
FieldAttribute::NONE
}),
);
}
vec![
WriteOrder::SetBufferAddress(bufaddr),
WriteOrder::StartFieldExtended(field_attr),
WriteOrder::SendText(field.data.as_ref().to_owned()),
WriteOrder::StartField(FieldAttribute::PROTECTED),
]
.into_iter()
})
.collect(),
};
session.send_record(&command).context(IoSnafu { context: "Failed to send screen" })?;
}
let response = session
.receive_record(None)
.context(IoSnafu { context: "Failed to read response" })?
.unwrap();
let incoming = IncomingRecord::parse_record(response.as_slice()).context(StreamSnafu)?;
let mut incoming_addr = !0;
for order in incoming.orders {
match order {
WriteOrder::SetBufferAddress(addr) => {
incoming_addr = addr;
}
WriteOrder::SendText(text) => {
for field in self.fields.iter_mut() {
if acalc.encode_address(field.address.row, field.address.col)
== incoming_addr - 1
&& let FieldData::RW(ref mut data) = field.data
{
**data = text.clone();
}
}
}
_ => {}
}
}
let (row, col) = acalc.decode_address(incoming.addr);
Ok(Response { address: Address { row, col }, aid: incoming.aid })
}
}