use super::*;

pub(super) fn encode_len_prefixed_string(text: &str) -> Vec<u8> {
    let mut bytes = Vec::with_capacity(1 + text.len());
    bytes.push(text.len() as u8);
    bytes.extend_from_slice(text.as_bytes());
    bytes
}

pub(super) fn encode_template(
    record_id: u32,
    trigger_kind: u8,
    flags: u8,
    actions: &[Vec<u8>],
) -> Vec<u8> {
    let mut bytes = Vec::new();
    bytes.extend_from_slice(PACKED_EVENT_RECORD_TEMPLATE_SYNTHETIC_MAGIC);
    bytes.extend_from_slice(&record_id.to_le_bytes());
    bytes.push(trigger_kind);
    bytes.push(flags);
    bytes.push(actions.len() as u8);
    bytes.push(0);
    for action in actions {
        bytes.extend_from_slice(action);
    }
    bytes
}

pub(super) fn encode_action_set_world_flag(key: &str, value: bool) -> Vec<u8> {
    let mut bytes = vec![0x01];
    bytes.extend_from_slice(&encode_len_prefixed_string(key));
    bytes.push(u8::from(value));
    bytes
}

pub(super) fn encode_action_set_special_condition(label: &str, value: u32) -> Vec<u8> {
    let mut bytes = vec![0x05];
    bytes.extend_from_slice(&encode_len_prefixed_string(label));
    bytes.extend_from_slice(&value.to_le_bytes());
    bytes
}

pub(super) fn encode_action_adjust_company_cash_ids(ids: &[u32], delta: i64) -> Vec<u8> {
    let mut bytes = vec![0x02, 0x01, ids.len() as u8];
    for id in ids {
        bytes.extend_from_slice(&id.to_le_bytes());
    }
    bytes.extend_from_slice(&delta.to_le_bytes());
    bytes
}

pub(super) fn encode_action_append_template(template: Vec<u8>) -> Vec<u8> {
    let mut bytes = vec![0x06];
    bytes.extend_from_slice(&(template.len() as u32).to_le_bytes());
    bytes.extend_from_slice(&template);
    bytes
}

pub(super) fn build_synthetic_event_record(
    trigger_kind: u8,
    flags: u8,
    standalone_count: u8,
    grouped_counts: [u8; 4],
    text_bands: [&[u8]; 6],
    actions: &[Vec<u8>],
) -> Vec<u8> {
    let mut bytes = Vec::new();
    bytes.extend_from_slice(PACKED_EVENT_RECORD_SYNTHETIC_MAGIC);
    bytes.push(trigger_kind);
    bytes.push(flags);
    bytes.push(standalone_count);
    bytes.push(actions.len() as u8);
    bytes.extend_from_slice(&grouped_counts);
    for band in text_bands {
        bytes.extend_from_slice(&(band.len() as u16).to_le_bytes());
        bytes.extend_from_slice(band);
    }
    for action in actions {
        bytes.extend_from_slice(action);
    }
    bytes
}

pub(super) fn encode_real_optional_string(text: &str) -> Vec<u8> {
    let mut bytes = Vec::new();
    bytes.extend_from_slice(&(text.len() as u16).to_le_bytes());
    bytes.extend_from_slice(text.as_bytes());
    bytes
}

pub(super) fn build_real_condition_row(
    raw_condition_id: i32,
    subtype: u8,
    flag_seed: u8,
    candidate_name: Option<&str>,
) -> Vec<u8> {
    let mut bytes = Vec::new();
    bytes.extend_from_slice(&(raw_condition_id as u32).to_le_bytes());
    bytes.push(subtype);
    while bytes.len() < PACKED_EVENT_REAL_CONDITION_ROW_LEN {
        bytes.push(flag_seed.wrapping_add(bytes.len() as u8));
    }
    match candidate_name {
        Some(text) => bytes.extend_from_slice(&encode_real_optional_string(text)),
        None => bytes.extend_from_slice(&0u16.to_le_bytes()),
    }
    bytes
}

pub(super) fn build_real_condition_row_with_threshold(
    raw_condition_id: i32,
    subtype: u8,
    threshold: i32,
    candidate_name: Option<&str>,
) -> Vec<u8> {
    let mut bytes = build_real_condition_row(raw_condition_id, subtype, 0, candidate_name);
    bytes[5..9].copy_from_slice(&threshold.to_le_bytes());
    bytes
}

pub(super) struct RealGroupedEffectRowSpec<'a> {
    pub(super) descriptor_id: u32,
    pub(super) opcode: u8,
    pub(super) raw_scalar_value: i32,
    pub(super) value_byte_0x09: u8,
    pub(super) value_dword_0x0d: u32,
    pub(super) value_byte_0x11: u8,
    pub(super) value_byte_0x12: u8,
    pub(super) value_word_0x14: u16,
    pub(super) value_word_0x16: u16,
    pub(super) locomotive_name: Option<&'a str>,
}

pub(super) fn build_real_grouped_effect_row(spec: RealGroupedEffectRowSpec<'_>) -> Vec<u8> {
    let mut bytes = vec![0; PACKED_EVENT_REAL_GROUPED_EFFECT_ROW_LEN];
    bytes[0..4].copy_from_slice(&spec.descriptor_id.to_le_bytes());
    bytes[4..8].copy_from_slice(&(spec.raw_scalar_value as u32).to_le_bytes());
    bytes[8] = spec.opcode;
    bytes[9] = spec.value_byte_0x09;
    bytes[0x0d..0x11].copy_from_slice(&spec.value_dword_0x0d.to_le_bytes());
    bytes[0x11] = spec.value_byte_0x11;
    bytes[0x12] = spec.value_byte_0x12;
    bytes[0x14..0x16].copy_from_slice(&spec.value_word_0x14.to_le_bytes());
    bytes[0x16..0x18].copy_from_slice(&spec.value_word_0x16.to_le_bytes());
    match spec.locomotive_name {
        Some(text) => bytes.extend_from_slice(&encode_real_optional_string(text)),
        None => bytes.extend_from_slice(&0u16.to_le_bytes()),
    }
    bytes
}

#[derive(Clone, Copy)]
pub(super) struct RealCompactControlSpec {
    pub(super) mode_byte_0x7ef: u8,
    pub(super) primary_selector_0x7f0: u32,
    pub(super) grouped_mode_0x7f4: u8,
    pub(super) one_shot_header_0x7f5: u32,
    pub(super) modifier_flag_0x7f9: u8,
    pub(super) modifier_flag_0x7fa: u8,
    pub(super) grouped_target_scope_ordinals_0x7fb: [u8; PACKED_EVENT_REAL_GROUP_COUNT],
    pub(super) grouped_scope_checkboxes_0x7ff: [u8; PACKED_EVENT_REAL_GROUP_COUNT],
    pub(super) summary_toggle_0x800: u8,
    pub(super) grouped_territory_selectors_0x80f: [i32; PACKED_EVENT_REAL_GROUP_COUNT],
}

pub(super) fn build_real_compact_control(spec: RealCompactControlSpec) -> Vec<u8> {
    let mut bytes = Vec::with_capacity(PACKED_EVENT_REAL_COMPACT_CONTROL_LEN);
    bytes.push(spec.mode_byte_0x7ef);
    bytes.extend_from_slice(&spec.primary_selector_0x7f0.to_le_bytes());
    bytes.push(spec.grouped_mode_0x7f4);
    bytes.extend_from_slice(&spec.one_shot_header_0x7f5.to_le_bytes());
    bytes.push(spec.modifier_flag_0x7f9);
    bytes.push(spec.modifier_flag_0x7fa);
    bytes.extend_from_slice(&spec.grouped_target_scope_ordinals_0x7fb);
    bytes.extend_from_slice(&spec.grouped_scope_checkboxes_0x7ff);
    bytes.push(spec.summary_toggle_0x800);
    for selector in spec.grouped_territory_selectors_0x80f {
        bytes.extend_from_slice(&selector.to_le_bytes());
    }
    bytes
}

pub(super) fn build_real_event_record(
    text_bands: [&[u8]; 6],
    compact_control: Option<RealCompactControlSpec>,
    condition_rows: &[Vec<u8>],
    grouped_rows: [&[Vec<u8>]; 4],
) -> Vec<u8> {
    let mut bytes = Vec::new();
    for band in text_bands {
        bytes.extend_from_slice(&(band.len() as u16).to_le_bytes());
        bytes.extend_from_slice(band);
    }
    if let Some(spec) = compact_control {
        bytes.extend_from_slice(&build_real_compact_control(spec));
    }
    bytes.extend_from_slice(&PACKED_EVENT_REAL_CONDITION_MARKER.to_le_bytes());
    bytes.extend_from_slice(&(condition_rows.len() as u16).to_le_bytes());
    for row in condition_rows {
        bytes.extend_from_slice(row);
    }
    bytes.extend_from_slice(&PACKED_EVENT_REAL_GROUPED_EFFECT_MARKER.to_le_bytes());
    for rows in grouped_rows {
        bytes.extend_from_slice(&(rows.len() as u16).to_le_bytes());
    }
    for rows in grouped_rows {
        for row in rows {
            bytes.extend_from_slice(row);
        }
    }
    bytes
}

pub(super) fn empty_analysis_report() -> SmpSaveCompanyChairmanAnalysisReport {
    SmpSaveCompanyChairmanAnalysisReport {
        profile_family: "rt3-105-scenario-save-container-v1".to_string(),
        selected_company_id: None,
        selected_chairman_profile_id: None,
        world_selection_context: None,
        world_issue_37: None,
        world_economic_tuning: None,
        world_finance_neighborhood: None,
        train_collection_header: None,
        train_collection_directory: None,
        region_collection_header: None,
        region_record_triplets: None,
        region_queued_notice_records: None,
        region_fixed_row_run_candidates: None,
        placed_structure_collection_header: None,
        placed_structure_record_triplets: None,
        placed_structure_dynamic_side_buffer: None,
        placed_structure_dynamic_side_buffer_alignment: None,
        unclassified_tagged_collection_headers: Vec::new(),
        company_entries: Vec::new(),
        chairman_entries: Vec::new(),
        notes: Vec::new(),
    }
}
