import { Buffer } from 'buffer';
import { candyMachineBeet, CandyMachine } from '@metaplex-foundation/mpl-candy-machine-core';
import { CANDY_MACHINE_HIDDEN_SECTION } from '../constants.mjs';
import { deserializeCandyMachineHiddenSection } from './CandyMachineHiddenSection.mjs';
import assert from '../../../utils/assert.mjs';
import { isModel, assertModel } from '../../../types/Model.mjs';
import { createSerializerFromSolitaType, deserializeAccount } from '../../../types/Serializer.mjs';
import { toBigNumber } from '../../../types/BigNumber.mjs';
import { toAccountInfo } from '../../../types/Account.mjs';
import { removeEmptyChars } from '../../../utils/common.mjs';
import { deserializeFeatureFlags } from '../../../types/FeatureFlags.mjs';

/**
 * This model contains all the relevant information about a Candy Machine.
 * This includes its settings but also all of the items (a.k.a. config lines)
 * loaded inside the Candy Machine along with some statistics about the items.
 *
 * @group Models
 */

/** @group Model Helpers */
const isCandyMachine = value => isModel('candyMachine', value);

/** @group Model Helpers */
function assertCandyMachine(value) {
  assertModel(isCandyMachine(value), `Expected CandyMachine model`);
}

/** @group Model Helpers */
const toCandyMachine = (account, candyGuard = null) => {
  const serializer = createSerializerFromSolitaType(CandyMachine, candyMachineBeet.description);
  const parsedAccount = deserializeAccount(account, serializer);
  const itemsAvailable = toBigNumber(parsedAccount.data.data.itemsAvailable);
  const itemsMinted = toBigNumber(parsedAccount.data.itemsRedeemed);
  const itemsRemaining = toBigNumber(itemsAvailable.sub(itemsMinted));
  let items = [];
  let itemsLoaded = 0;
  let isFullyLoaded = true;
  const {
    hiddenSettings
  } = parsedAccount.data.data;
  const {
    configLineSettings
  } = parsedAccount.data.data;
  let itemSettings;
  if (hiddenSettings) {
    itemSettings = {
      ...hiddenSettings,
      type: 'hidden'
    };
  } else {
    assert(!!configLineSettings, 'Expected either hidden or config line settings');
    itemSettings = {
      ...configLineSettings,
      type: 'configLines'
    };
    const hiddenSection = deserializeCandyMachineHiddenSection(account.data, itemsAvailable.toNumber(), itemsRemaining.toNumber(), itemSettings, CANDY_MACHINE_HIDDEN_SECTION);
    items = hiddenSection.items;
    itemsLoaded = hiddenSection.itemsLoaded;
    isFullyLoaded = hiddenSection.itemsLoaded >= itemsAvailable.toNumber();
  }
  return {
    model: 'candyMachine',
    address: account.publicKey,
    accountInfo: toAccountInfo(account),
    authorityAddress: parsedAccount.data.authority,
    mintAuthorityAddress: parsedAccount.data.mintAuthority,
    collectionMintAddress: parsedAccount.data.collectionMint,
    symbol: removeEmptyChars(parsedAccount.data.data.symbol),
    sellerFeeBasisPoints: parsedAccount.data.data.sellerFeeBasisPoints,
    isMutable: parsedAccount.data.data.isMutable,
    maxEditionSupply: toBigNumber(parsedAccount.data.data.maxSupply),
    creators: parsedAccount.data.data.creators.map(creator => ({
      ...creator,
      share: creator.percentageShare
    })),
    items,
    itemsAvailable,
    itemsMinted,
    itemsRemaining,
    itemsLoaded,
    isFullyLoaded,
    itemSettings,
    featureFlags: deserializeFeatureFlags(toBigNumber(parsedAccount.data.features).toArrayLike(Buffer, 'le', 8), 64)[0],
    candyGuard
  };
};
const toCandyMachineData = candyMachine => {
  return {
    itemsAvailable: candyMachine.itemsAvailable,
    symbol: candyMachine.symbol,
    sellerFeeBasisPoints: candyMachine.sellerFeeBasisPoints,
    maxSupply: candyMachine.maxEditionSupply,
    isMutable: candyMachine.isMutable,
    creators: candyMachine.creators.map(creator => ({
      ...creator,
      verified: false,
      percentageShare: creator.share
    })),
    configLineSettings: candyMachine.itemSettings.type === 'configLines' ? candyMachine.itemSettings : null,
    hiddenSettings: candyMachine.itemSettings.type === 'hidden' ? candyMachine.itemSettings : null
  };
};
const getCandyMachineSize = data => {
  if (data.hiddenSettings) {
    return CANDY_MACHINE_HIDDEN_SECTION;
  }

  // This should not happen as the candy machine input type
  // ensures exactly on of them is provided.
  assert(!!data.configLineSettings, 'No config line settings nor hidden settings were provided. ' + 'Please provide one of them.');
  const itemsAvailable = toBigNumber(data.itemsAvailable).toNumber();
  const configLineSize = data.configLineSettings.nameLength + data.configLineSettings.uriLength;
  return Math.ceil(CANDY_MACHINE_HIDDEN_SECTION +
  // Number of currently items inserted.
  4 +
  // Config line data.
  itemsAvailable * configLineSize + (
  // Bit mask to keep track of which ConfigLines have been added.
  4 + Math.floor(itemsAvailable / 8) + 1) + (
  // Mint indices.
  4 + itemsAvailable * 4));
};

export { assertCandyMachine, getCandyMachineSize, isCandyMachine, toCandyMachine, toCandyMachineData };
//# sourceMappingURL=CandyMachine.mjs.map
