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

export type {{ instructionParsedType }}<
  TProgram extends string = typeof {{ programAddressConstant }},
  {% if hasAccounts %}
    TAccountMetas extends readonly AccountMeta[] = readonly AccountMeta[],
  {% endif %}
> = {
  programAddress: Address<TProgram>;
  {% if hasAccounts %}
    accounts: {
      {% for account in instruction.accounts %}
        {% if account.docs.length > 0 %}
          {{ macros.docblock(account.docs) }}
        {% endif %}
        {{ account.name | camelCase }}{{ '?' if account.isOptional }}: TAccountMetas[{{ loop.index0 }}]{{ ' | undefined' if account.isOptional }},
      {% endfor %}
    };
  {% endif %}
  {% if hasData %}
    data: {{ dataTypeFragment }};
  {% endif %}
};

export function {{ instructionParseFunction }}<
  TProgram extends string,
  {% if hasAccounts %}
    TAccountMetas extends readonly AccountMeta[],
  {% endif %}
>(
  instruction: Instruction<TProgram>
    {% if hasAccounts %}
      & InstructionWithAccounts<TAccountMetas>
    {% endif %}
    {% if hasData %}
      & InstructionWithData<ReadonlyUint8Array>
    {% endif %}
): {{ instructionParsedType }}<TProgram {{ ', TAccountMetas' if hasAccounts }}> {
  {% if hasAccounts %}
    if (instruction.accounts.length < {{ minimumNumberOfAccounts }}) {
      // TODO: Coded error.
      throw new Error('Not enough accounts');
    }
    let accountIndex = 0;
    const getNextAccount = () => {
      const accountMeta = instruction.accounts![accountIndex]!;
      accountIndex += 1;
      return accountMeta;
    }
    {% if hasOptionalAccounts and instruction.optionalAccountStrategy === 'omitted' %}
      let optionalAccountsRemaining = instruction.accounts.length - {{ minimumNumberOfAccounts }};
      const getNextOptionalAccount = () => {
        if (optionalAccountsRemaining === 0) return undefined;
        optionalAccountsRemaining -= 1;
        return getNextAccount();
      };
    {% elif hasOptionalAccounts %}
      const getNextOptionalAccount = () => {
        const accountMeta = getNextAccount();
        return accountMeta.address === {{ programAddressConstant }} ? undefined : accountMeta;
      };
    {% endif %}
  {% endif %}
  return {
    programAddress: instruction.programAddress,
    {% if hasAccounts %}
      accounts: {
        {% for account in instruction.accounts %}
          {% if account.isOptional %}
            {{ account.name | camelCase }}: getNextOptionalAccount(),
          {% else %}
            {{ account.name | camelCase }}: getNextAccount(),
          {% endif %}
        {% endfor %}
      },
    {% endif %}
    {% if hasData %}
      data: {{ decoderFunction }}.decode(instruction.data),
    {% endif %}
  };
}
