use super::BuildTargetIdentifier;
use lsp_types::Url;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
/// The build target sources request is sent from the client to the server to
/// query for the list of text documents and directories that are belong to a
/// build target. The sources response must not include sources that are
/// external to the workspace.
pub struct BuildTargetSources {
    targets: Vec<BuildTargetIdentifier>,
}

impl BuildTargetSources {
    pub fn new(targets: Vec<BuildTargetIdentifier>) -> Self {
        Self { targets }
    }

    /// Get a reference to the bsp sources params's targets.
    pub fn targets(&self) -> &[BuildTargetIdentifier] {
        self.targets.as_ref()
    }

    /// Set the bsp sources params's targets.
    pub fn set_targets(&mut self, targets: Vec<BuildTargetIdentifier>) {
        self.targets = targets;
    }
}

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct BuildTargetSourcesResult {
    items: Vec<SourcesCollection>,
}

impl BuildTargetSourcesResult {
    pub fn new(items: Vec<SourcesCollection>) -> Self {
        Self { items }
    }

    /// Get a reference to the bsp sources result's items.
    pub fn items(&self) -> &[SourcesCollection] {
        self.items.as_ref()
    }

    /// Set the bsp sources result's items.
    pub fn set_items(&mut self, items: Vec<SourcesCollection>) {
        self.items = items;
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct SourcesCollection {
    target: BuildTargetIdentifier,

    /// The text documents and directories that belong to this build target.
    sources: Vec<Sources>,
}

impl SourcesCollection {
    pub fn new(target: BuildTargetIdentifier, sources: Vec<Sources>) -> Self {
        Self { target, sources }
    }

    /// Get a reference to the bsp sources collection's target.
    pub fn target(&self) -> &BuildTargetIdentifier {
        &self.target
    }

    /// Set the bsp sources collection's target.
    pub fn set_target(&mut self, target: BuildTargetIdentifier) {
        self.target = target;
    }

    /// Get a reference to the bsp sources collection's sources.
    pub fn sources(&self) -> &[Sources] {
        self.sources.as_ref()
    }

    /// Set the bsp sources collection's sources.
    pub fn set_sources(&mut self, sources: Vec<Sources>) {
        self.sources = sources;
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Sources {
    /// Either a text document or a directory. A directory entry must end with a
    /// forward slash "/" and a directory entry implies that every nested text
    /// document within the directory belongs to this source item.
    uri: Url,

    /// Type of file of the source item, such as whether it is file or directory.
    kind: SourceKind,

    /// Indicates if this source is automatically generated by the build and is
    /// not intended to be manually edited by the user.
    generated: bool,
}

impl Sources {
    /// Either a text document or a directory. A directory entry must end with a
    /// forward slash "/" and a directory entry implies that every nested text
    /// document within the directory belongs to this source item.
    pub fn new(uri: Url, kind: SourceKind, generated: bool) -> Self {
        Self {
            uri,
            kind,
            generated,
        }
    }

    /// Get a reference to the bsp sources item's kind.
    pub fn kind(&self) -> &SourceKind {
        &self.kind
    }

    /// Set the bsp sources item's kind.
    pub fn set_kind(&mut self, kind: SourceKind) {
        self.kind = kind;
    }

    /// Get a reference to the bsp sources item's uri.
    pub fn uri(&self) -> &Url {
        &self.uri
    }

    /// Set the bsp sources item's uri.
    pub fn set_uri(&mut self, uri: Url) {
        self.uri = uri;
    }

    /// Get the bsp sources item's generated.
    pub fn generated(&self) -> bool {
        self.generated
    }

    /// Set the bsp sources item's generated.
    pub fn set_generated(&mut self, generated: bool) {
        self.generated = generated;
    }
}

#[derive(Debug, serde_repr::Deserialize_repr, serde_repr::Serialize_repr)]
#[repr(u16)]
pub enum SourceKind {
    /// The source item references a normal file.
    File = 1,
    /// The source item references a directory.
    Directory = 2,
}

impl Default for SourceKind {
    fn default() -> Self {
        Self::File
    }
}
