{% extends "layout.njk" %}
{% import "macros.njk" as macros %}

{% block main %}

{{ imports }}

/// Accounts.
pub struct {{ instruction.name | pascalCase }} {
  {% for account in instruction.accounts %}
    {% if account.docs.length > 0 %}
      {{ macros.docblock(account.docs) }}
    {% endif %}

    {% if account.isSigner === 'either' %}
      {% set type = '(solana_program::pubkey::Pubkey, bool)' %}
    {% else %}
      {% set type = 'solana_program::pubkey::Pubkey' %}
    {% endif %}

    {% if account.isOptional %}
      pub {{ account.name | snakeCase }}: Option<{{ type }}>,
    {% else %}
      pub {{ account.name | snakeCase }}: {{ type }},
    {% endif %}
  {% endfor %}
}

impl {{ instruction.name | pascalCase }} {
  pub fn instruction(&self{{ ', args: ' + instruction.name | pascalCase + 'InstructionArgs' if hasArgs }}) -> solana_program::instruction::Instruction {
    self.instruction_with_remaining_accounts({{ 'args, ' if hasArgs }}&[])
  }
  #[allow(clippy::vec_init_then_push)]
  pub fn instruction_with_remaining_accounts(&self{{ ', args: ' + instruction.name | pascalCase + 'InstructionArgs' if hasArgs }}, remaining_accounts: &[solana_program::instruction::AccountMeta]) -> solana_program::instruction::Instruction {
    let mut accounts = Vec::with_capacity({{ instruction.accounts.length }} + remaining_accounts.len());
    {% for account in instruction.accounts %}
      {% if account.isSigner === 'either' %}
        {% if account.isOptional %}
          {% if instruction.optionalAccountStrategy === 'programId' %}
            if let Some(({{ account.name | snakeCase }}, signer)) = self.{{ account.name | snakeCase }} {
              accounts.push(solana_program::instruction::AccountMeta::{{ 'new' if account.isWritable else 'new_readonly' }}(
                {{ account.name | snakeCase }},
                signer,
              ));
            } else {
              accounts.push(solana_program::instruction::AccountMeta::new_readonly(
                crate::{{ program.name | snakeCase | upper }}_ID,
                false,
              ));
            }
          {% else %}
            if let Some(({{ account.name | snakeCase }}, signer)) = self.{{ account.name | snakeCase }} {
              accounts.push(solana_program::instruction::AccountMeta::{{ 'new' if account.isWritable else 'new_readonly' }}(
                {{ account.name | snakeCase }},
                signer,
              ));
            }
          {% endif %}
        {% else %}
          accounts.push(solana_program::instruction::AccountMeta::{{ 'new' if account.isWritable else 'new_readonly' }}(
            self.{{ account.name | snakeCase }}.0,
            self.{{ account.name | snakeCase }}.1,
          ));
        {% endif %}
      {% else %}
        {% if account.isOptional %}
          {% if instruction.optionalAccountStrategy === 'programId' %}
            if let Some({{ account.name | snakeCase }}) = self.{{ account.name | snakeCase }} {
              accounts.push(solana_program::instruction::AccountMeta::{{ 'new' if account.isWritable else 'new_readonly' }}(
                {{ account.name | snakeCase }},
                {{ 'true' if account.isSigner else 'false' }},
              ));
            } else {
              accounts.push(solana_program::instruction::AccountMeta::new_readonly(
                crate::{{ program.name | snakeCase | upper }}_ID,
                false,
              ));
            }
          {% elif account.isOptional %}
            if let Some({{ account.name | snakeCase }}) = self.{{ account.name | snakeCase }} {
              accounts.push(solana_program::instruction::AccountMeta::{{ 'new' if account.isWritable else 'new_readonly' }}(
                {{ account.name | snakeCase }},
                {{ 'true' if account.isSigner else 'false' }},
              ));
            }
          {% endif %}
        {% else %}
          accounts.push(solana_program::instruction::AccountMeta::{{ 'new' if account.isWritable else 'new_readonly' }}(
            self.{{ account.name | snakeCase }},
            {{ 'true' if account.isSigner else 'false' }}
          ));
        {% endif %}
      {% endif %}
    {% endfor %}
    accounts.extend_from_slice(remaining_accounts);
    let {{ 'mut ' if hasArgs }}data = {{ instruction.name | pascalCase }}InstructionData::new().try_to_vec().unwrap();
    {% if hasArgs %}
      let mut args = args.try_to_vec().unwrap();
      data.append(&mut args);
    {% endif %}

    solana_program::instruction::Instruction {
      program_id: crate::{{ program.name | snakeCase | upper }}_ID,
      accounts,
      data,
    }
  }
}

#[derive(BorshDeserialize, BorshSerialize)]
pub struct {{ instruction.name | pascalCase }}InstructionData {
  {% for arg in instructionArgs %}
    {% if arg.default %}
      {{ arg.name | snakeCase }}: {{ arg.type }},
    {% endif %}
  {% endfor %}
}

impl {{ instruction.name | pascalCase }}InstructionData {
  pub fn new() -> Self {
    Self {
      {% for arg in instructionArgs %}
        {% if arg.default %}
          {{ arg.name | snakeCase }}: {{ arg.value }},
        {% endif %}
      {% endfor %}
    }
  }
}

{% if hasArgs %}
#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct {{ instruction.name | pascalCase }}InstructionArgs {
  {% for arg in instructionArgs %}
    {% if not arg.default %}
      pub {{ arg.name | snakeCase }}: {{ arg.type }},
    {% endif %}
  {% endfor %}
}
{% endif %}

{% for nestedStruct in typeManifest.nestedStructs %}
#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
{{ nestedStruct }}
{% endfor %}

{% include "instructionsPageBuilder.njk" %}

{% include "instructionsCpiPage.njk" %}

{% endblock %}
