use std::{fmt, time::Duration};

use lotus_script::{content::ContentId, prelude::message_type};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TerminusText([String; 2]);

impl From<[String; 2]> for TerminusText {
    fn from(val: [String; 2]) -> Self {
        TerminusText(val)
    }
}

impl TerminusText {
    pub fn new(line_1: impl Into<String>, line_2: impl Into<String>) -> Self {
        TerminusText([line_1.into(), line_2.into()])
    }
    pub fn new_empty() -> Self {
        TerminusText(["".to_string(), "".to_string()])
    }
    pub fn is_multiline(&self) -> bool {
        !self.0[1].is_empty()
    }
    pub fn line_1(&self) -> String {
        self.0[0].clone()
    }
    pub fn line_2(&self) -> String {
        self.0[1].clone()
    }
    pub fn is_empty(&self) -> bool {
        self.0[0].is_empty() && self.0[1].is_empty()
    }
}

//-----------------------------
// Lotus Messages
//-----------------------------

//===========================================================

/// Current line number & specialchar (entered by hand, or by line/route default)
/// Mandatory
///
/// The special character 0 corresponds to ‘no special character’.
/// Due to the data type u32, negative values are not possible.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct LineSpecialchar {
    pub line: u32,
    pub specialchar: u32,
}

message_type!(LineSpecialchar, "Std_Pis", "LineSpecialchar", "TrainBus");

//-----------------------------

///A new parameter in the standard is the course number. This comes from Julian's `Wagenbus`
/// and the VDV-300 script. It can be used to control digital course display modules,
/// for example. If no course is used, this message does not need to be sent.
/// Otherwise, however, it is a mandatory message. Here, the value 0 corresponds to ‘no course’, too.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Course {
    pub value: u32,
}

message_type!(Course, "Std_Pis", "Course", "TrainBus");

//-----------------------------

///Here we have the first change in content compared to the previous solution.
/// In the old system, `ROUTE_LISTINDEX` is transferred here. In the future, however,
/// we want to transfer the route number entered (NOT the ListIndex). A breakdown
/// into the ListIndex would then take place in the module. This message is also a mandatory message.
/// Route 0 corresponds to ‘no route’.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Route {
    pub value: u32,
}

message_type!(Route, "Std_Pis", "Route", "TrainBus");

//-----------------------------

///Here we have another major change. The message `TERMINUS_LISTINDEX` from the old system
/// is hereby replaced and expanded. As it is possible in modern IBIS devices and vehicles,
/// we want to offer the option of overwriting the display text with free text
/// (or with text from special FIS files). This is made possible by the `Option<TerminusText>`,
/// both for overwriting front displays and for side displays.
/// In addition, the destination code (NOT the ListIndex) is transferred.
/// This always applies when no overwriting destination text is transferred and is generally
/// considered a valid destination for AI passengers (more on this below).
///
/// In principle, the transmission of the destination code is a mandatory message,
/// but the use of override texts is optional.
/// However, since the transmission of destination texts originates from the VDV 300 font,
/// displays should support it.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Terminus {
    pub code: u32,
    pub override_front: Option<TerminusText>,
    pub override_side: Option<TerminusText>,
}

message_type!(Terminus, "Std_Pis", "Terminus", "TrainBus");

//-----------------------------

///In the old Lotus, `LINE` and `ROUTE` were used for the information that the vehicle sent to the route balises.
/// The vehicle then sent these values to the ITCS server.
/// In the future, we want to reduce this to a kind of train control number.
/// Nothing will change for conventional IBIS devices for the time being.
/// Together with a planned additional text field in the FIS file, these train control numbers
/// should be adjustable per line and route.
/// This will also allow alternative train control devices to be supported and
/// direction-based traffic light requests to be achieved in the future.
/// (However, this is still a long way off and is more of a preliminary step at this stage.)
/// This is a mandatory message. Ultimately, however, the vehicle must also decide
/// whether it supports this type of request at all.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct RoutingCode {
    pub value: u32,
}

message_type!(RoutingCode, "Std_Pis", "RoutingCode", "TrainBus");

//-----------------------------

///We would like to offer an alternative to labelling simple internal displays.
/// In the VDV-300 font, these simple internal displays are supplied directly with text from IBIS.
/// For this reason, we wanted to use this option as well.
/// The ‘script logic’ for simple internal displays is thus transferred to the IBIS device,
/// and the displays themselves only show the text transmitted to them.
/// The IBIS device must determine this display text.
///
///More complex interior displays (such as monitors) are explicitly not controlled by this.
/// These continue to form their content from the other available information.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct StopText {
    pub value: TerminusText,
}

message_type!(StopText, "Std_Pis", "StopText", "TrainBus");

//-----------------------------

///Here we replace the previous message `STOP_SEQ`.
/// The index of the current stop on a route is passed as an option on a 0-based scale.
/// If no route is set, the value is `NONE`. Once a route is set, `SOME(u32)` corresponds to the index.
/// This message is again a mandatory message.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct StopIndex {
    pub value: Option<u32>,
}

message_type!(StopIndex, "Std_Pis", "StopIndex", "TrainBus");

//-----------------------------

///As an optional message, we would like to add parameters for validator modules
/// (it is now called ‘validator’ here, but the values can also be used by all other modules).
///
/// * The value `zone` corresponds to the fare zone (or honeycomb, depending on the local requirements
///   of the transport company). If the validators are not available
///   (e.g. if no fare zone is set or if the validators are locked in raid mode),
///   `NONE` is transmitted; otherwise, a fare zone is specified as `SOME(u32)`.
///
/// * The value `stop_index` works like the `StopIndex` from the previous section.
///   Since certain IBIS devices only increment the StopIndex for the validators when the doors were open,
///   there may be deviations from the `stop_index` in the previous section.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Validator {
    pub zone: Option<u32>,
    pub stop_index: Option<u32>,
}

message_type!(Validator, "Std_Pis", "Validator", "TrainBus");

//-----------------------------

///It is possible to transfer a timestamp both in Julian 's ‘Wagenbus’ and in the VDV-300 document.
/// For the sake of simplicity, let us assume that the modules use the simulation time directly.
/// To enable the ‘system time’ to be adjusted on the IBIS device, we want to make this possible
/// through this message. A duration is sent. This represents the offset to the simulation time.
/// This offers the advantage that the information in question only needs to be transferred once
/// when it changes and can be directly offset against the time stamp of the situation.
/// (Simulation time + duration = changed IBIS system time).
/// This message is, of course, completely optional for sending,
/// just as such a feature is not available in every IBIS.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TimeDelta {
    pub value: Duration,
}

message_type!(TimeDelta, "Std_Pis", "TimeDelta", "TrainBus");

//-----------------------------

///This section should still be treated with caution, because, as mentioned in the introduction,
/// there is no interface to the FIS file yet. Playing ID-based sounds is also not yet available.
/// So there may still be some changes here.
///
///In general, we want to use this message to replace several old messages:
/// `ANNOUNCE_USERID`, `ANNOUNCE_SUBID` and `ANNOUNCE_GLUED`. A ContentId corresponds to a
/// Lotus structure and directly bundles the user ID and sub-ID.
/// A list of content IDs is transferred. I cannot explain any more about this at this stage because,
/// as I said, the corresponding interfaces do not yet exist.
///
/// The transmission of announcements is, of course, also optional.
/// Announcements only need to be transmitted if they are available and the
/// IBIS device supports this function. Similarly, a vehicle only needs to be able to
/// evaluate and play them if it has the appropriate functions.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Announcement {
    pub value: Vec<ContentId>,
}

message_type!(Announcement, "Std_Pis", "Announcement", "TrainBus");

//-----------------------------

///Until now, switch requests have been transmitted from IBIS to the vehicle using `SWITCH`.
/// We want to make two changes here. Firstly, all components (including the vehicle itself)
/// should send information about whether a switch command is currently pending
/// (there are IBIS devices, for example, that can also display switch commands from the
/// vehicle with switch buttons). The second change is that each module/vehicle itself
/// checks the conditions for transmitting the command.
/// If a setting command is to be sent from the IBIS for 100 meters when a button is pressed,
/// the IBIS first sends the direction of the setting command,
/// checks the distance travelled itself, and resets the setting command after 100 meters by
/// transmitting RoutingDirection::Off. The decision as to which signal overwrites which
/// (e.g. vehicle overwrites IBIS) is still made in the vehicle when the command is transmitted.
/// This message is optional and only needs to be sent.
#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize, PartialEq)]
pub enum RoutingDirection {
    Left,
    Right,
    Straight,
    #[default]
    Off,
}

impl fmt::Display for RoutingDirection {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            RoutingDirection::Left => write!(f, "Left"),
            RoutingDirection::Right => write!(f, "Right"),
            RoutingDirection::Straight => write!(f, "Straight"),
            RoutingDirection::Off => write!(f, "Off"),
        }
    }
}

message_type!(RoutingDirection, "Std_Pis", "RoutingDirection", "TrainBus");

//-----------------------------

///This message is currently more of a glimpse into the future.
/// LOTUS does not yet support manual requests. It currently serves only as a preliminary service.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct RoutingRequest {}

message_type!(RoutingRequest, "Std_Pis", "RoutingRequest", "TrainBus");

//===========================================================

/// This is intended to inform the vehicle which destination code they are currently
/// displaying (or None, if no destination is displayed). In a sense, this is a
/// preliminary service for the AI passengers. As is known from the old system,
/// the AI passengers respond to the destination code set in RC_PIS_Terminus.
/// Until now, this value has only been provided by IBIS.
///
///This preliminary work also makes things possible, such as distinguishing between AI people getting on
/// and AI people getting off. For example, in the case of school transport,
/// passengers should only get on the way to school and only get off when they reach
/// the school. In addition, this system also provides better support for plug-in signs
/// (whether in the vehicle or as a module). It also allows displays to individually specify
/// ‘when’ they are finished with signs.
///
///Using the transmitter's module slot, a vehicle can then clearly recognise
/// which module slot and thus which type of module the information came from.
///
///As a precise link to the AI is still pending (due to the lack of AI), I would like to
/// leave this point half open for now.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct NowDisplaying {
    pub code: Option<u32>,
}

message_type!(NowDisplaying, "Std_Pis", "Displaying");
