//! Representation of the hidden API data

use crate::{Result, Visitable, VisitableMut, Visitor, VisitorMut};
use log::warn;
#[cfg(feature = "python")]
use pyo3::prelude::*;
use serde::{Deserialize, Serialize};

#[cfg_attr(feature = "python", pyclass)]
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize, Serialize)]
pub struct HiddenApiData {
    pub permission: HiddenApiPermission,
    pub domain: HiddenApiDomain,
}

impl From<&androscalpel_serializer::HiddenApiFlags> for HiddenApiData {
    fn from(flags: &androscalpel_serializer::HiddenApiFlags) -> Self {
        Self {
            permission: flags.value.into(),
            domain: flags.domain_api.into(),
        }
    }
}
impl From<androscalpel_serializer::HiddenApiFlags> for HiddenApiData {
    fn from(flags: androscalpel_serializer::HiddenApiFlags) -> Self {
        (&flags).into()
    }
}

impl From<&HiddenApiData> for androscalpel_serializer::HiddenApiFlags {
    fn from(flags: &HiddenApiData) -> Self {
        Self {
            value: (&flags.permission).into(),
            domain_api: (&flags.domain).into(),
        }
    }
}
impl From<HiddenApiData> for androscalpel_serializer::HiddenApiFlags {
    fn from(flags: HiddenApiData) -> Self {
        (&flags).into()
    }
}

impl<V: Visitor> Visitable<V> for HiddenApiData {
    fn default_visit(&self, v: &mut V) -> Result<()> {
        v.visit_hidden_api_permission(&self.permission)?;
        v.visit_hidden_api_domain(&self.domain)?;
        Ok(())
    }
}
impl<V: VisitorMut> VisitableMut<V> for HiddenApiData {
    fn default_visit_mut(self, v: &mut V) -> Result<Self> {
        Ok(Self {
            permission: v.visit_hidden_api_permission(self.permission)?,
            domain: v.visit_hidden_api_domain(self.domain)?,
        })
    }
}

#[cfg_attr(feature = "python", pyclass)]
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize, Serialize)]
pub enum HiddenApiPermission {
    /// Interfaces that can be freely used and are supported as
    /// part of the officially documented Android framework Package Index
    Whitelist {},
    /// Non-SDK interfaces that can be used regardless of the
    /// application's target API level
    Greylist {},
    /// Non-SDK interfaces that cannot be used regardless of the
    /// application's target API level. Accessing one of these
    /// interfaces causes a runtime error.
    Blacklist {},
    /// Non-SDK interfaces that can be used for Android 8.x and
    /// below unless they are restricted (targetSdkVersion <= 27 (O_MR1)).
    GreylistMaxO {},
    /// Non-SDK interfaces that can be used for Android 9.x unless
    /// they are restricted.
    GreylistMaxP {},
    /// Non-SDK interfaces that can be used for Android 10.x unless
    /// they are restricted.
    GreylistMaxQ {},
    /// Non-SDK interfaces that can be used for Android 11.x unless
    /// they are restricted.
    GreylistMaxR {},
    GreylistMaxS {},
    /// Unknown flag, either an error or this crate is out of date.
    Unknwon {
        value: u8,
    },
}

impl HiddenApiPermission {
    pub fn to_smali_name(&self) -> &'static str {
        match self {
            Self::Whitelist {} => "whitelist",
            Self::Greylist {} => "greylist",
            Self::Blacklist {} => "blacklist",
            Self::GreylistMaxO {} => "greylist-max-o",
            Self::GreylistMaxP {} => "greylist-max-p",
            Self::GreylistMaxQ {} => "greylist-max-q",
            Self::GreylistMaxR {} => "greylist-max-r",
            Self::GreylistMaxS {} => "greylist-max-s",
            Self::Unknwon { .. } => "???list",
        }
    }
}

impl From<&androscalpel_serializer::HiddenApiValue> for HiddenApiPermission {
    fn from(permission: &androscalpel_serializer::HiddenApiValue) -> Self {
        match permission {
            androscalpel_serializer::HiddenApiValue::Whitelist => Self::Whitelist {},
            androscalpel_serializer::HiddenApiValue::Greylist => Self::Greylist {},
            androscalpel_serializer::HiddenApiValue::Blacklist => Self::Blacklist {},
            androscalpel_serializer::HiddenApiValue::GreylistMaxO => Self::GreylistMaxO {},
            androscalpel_serializer::HiddenApiValue::GreylistMaxP => Self::GreylistMaxP {},
            androscalpel_serializer::HiddenApiValue::GreylistMaxQ => Self::GreylistMaxQ {},
            androscalpel_serializer::HiddenApiValue::GreylistMaxR => Self::GreylistMaxR {},
            androscalpel_serializer::HiddenApiValue::GreylistMaxS => Self::GreylistMaxS {},
            androscalpel_serializer::HiddenApiValue::Unknwon(val) => Self::Unknwon { value: *val },
        }
    }
}
impl From<androscalpel_serializer::HiddenApiValue> for HiddenApiPermission {
    fn from(permission: androscalpel_serializer::HiddenApiValue) -> Self {
        (&permission).into()
    }
}

impl From<&HiddenApiPermission> for androscalpel_serializer::HiddenApiValue {
    fn from(permission: &HiddenApiPermission) -> Self {
        match permission {
            HiddenApiPermission::Whitelist {} => Self::Whitelist,
            HiddenApiPermission::Greylist {} => Self::Greylist,
            HiddenApiPermission::Blacklist {} => Self::Blacklist,
            HiddenApiPermission::GreylistMaxO {} => Self::GreylistMaxO,
            HiddenApiPermission::GreylistMaxP {} => Self::GreylistMaxP,
            HiddenApiPermission::GreylistMaxQ {} => Self::GreylistMaxQ,
            HiddenApiPermission::GreylistMaxR {} => Self::GreylistMaxR,
            HiddenApiPermission::GreylistMaxS {} => Self::GreylistMaxS,
            HiddenApiPermission::Unknwon { value } => Self::Unknwon(*value),
        }
    }
}

impl From<HiddenApiPermission> for androscalpel_serializer::HiddenApiValue {
    fn from(permission: HiddenApiPermission) -> Self {
        (&permission).into()
    }
}

#[cfg_attr(feature = "python", pyclass)]
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize, Serialize)]
pub struct HiddenApiDomain {
    pub is_core_platform_api: bool,
    pub is_test_api: bool,
    /// Unknown domains.
    pub unknown_flags: u32,
}

impl HiddenApiDomain {
    pub fn to_smali_name(&self) -> String {
        let mut value = String::new();
        if self.is_core_platform_api {
            value += "core-platform-api";
        }
        if self.is_test_api {
            if !value.is_empty() {
                value += "|";
            }
            value += "test-api";
        }
        if self.unknown_flags != 0 {
            warn!("This attribut has an unknown hiddenapi domain set, this will not be display for compatibility with apktool");
        }
        value
    }
}

impl From<&androscalpel_serializer::HiddenApiDomain> for HiddenApiDomain {
    fn from(domain: &androscalpel_serializer::HiddenApiDomain) -> Self {
        Self {
            is_core_platform_api: domain.is_core_platform_api,
            is_test_api: domain.is_test_api,
            unknown_flags: domain.unknown_flags,
        }
    }
}

impl From<androscalpel_serializer::HiddenApiDomain> for HiddenApiDomain {
    fn from(domain: androscalpel_serializer::HiddenApiDomain) -> Self {
        (&domain).into()
    }
}

impl From<&HiddenApiDomain> for androscalpel_serializer::HiddenApiDomain {
    fn from(domain: &HiddenApiDomain) -> androscalpel_serializer::HiddenApiDomain {
        Self {
            is_core_platform_api: domain.is_core_platform_api,
            is_test_api: domain.is_test_api,
            unknown_flags: domain.unknown_flags,
        }
    }
}

impl From<HiddenApiDomain> for androscalpel_serializer::HiddenApiDomain {
    fn from(domain: HiddenApiDomain) -> androscalpel_serializer::HiddenApiDomain {
        (&domain).into()
    }
}
