import * as React from 'react';

interface ICheckboxGroupButtonsProps {
  name: string
  selectedValues: number[]
  inputClassName: string
  labelText: string
  inputGroups: ICheckboxInputGroup[]
  colQty: number
  onChange: (newSelectedValues: number[]) => void
}

// TODO: After modeler uses Typescript >= 4.x, extends Omit<IInputGroup, 'value'>
interface ICheckboxInputGroup {
  key: string
  text: React.ReactNode
  value: number
}

// Use whole class name for react to preparse
const GRID_COLS = [
  'grid-cols-none',
  'grid-cols-1',
  'grid-cols-2',
  'grid-cols-3',
  'grid-cols-4',
  'grid-cols-5',
  'grid-cols-6',
  'grid-cols-7',
  'grid-cols-8',
  'grid-cols-9',
  'grid-cols-10',
  'grid-cols-11',
  'grid-cols-12'
];

export function CheckboxGroupButtons(props: ICheckboxGroupButtonsProps): JSX.Element {
  const selectedOptions = new Set<number>(props.selectedValues);
  const inputGroups = props.inputGroups.map((inputGroup) => (
    <div key={inputGroup.key}>
      <input
        key={inputGroup.key}
        id={inputGroup.key}
        type='checkbox'
        name={props.name}
        className={`peer m-2 ${props.inputClassName}`}
        value={inputGroup.value}
        checked={IsChecked(inputGroup)}
        onChange={(event) => {
          const newSelectedValues = SetChecked(Number(event.target.value), event.target.checked);
          props.onChange(newSelectedValues);
        }} />
      <label htmlFor={inputGroup.key} className='text-gray-400 peer-checked:text-blue-500 group'>
        {inputGroup.text}
      </label>
    </div>
  ));

  const gridColsClass = GRID_COLS[props.colQty];

  return (
    <>
      <label className='text-xs font-medium text-gray-800'>
        {props.labelText}
      </label>
      <div className={`grid ${gridColsClass}`}>
        {inputGroups}
      </div>
    </>
  );

  function IsChecked(inputGroup: ICheckboxInputGroup): boolean {
    return selectedOptions.has(inputGroup.value);
  }

  /**
   * Sedt an option by using a bitwise operation and returns the new selected values
   * example: set the 4th option to 0: (1001) xor (1 << 3) => 0001
   * @param selectedValue The option to set
   * @returns The new selected values
   */
  function SetChecked(selectedValue: number, isChecked: boolean): number[] {
    isChecked ? selectedOptions.add(selectedValue) : selectedOptions.delete(selectedValue);
    return [...selectedOptions];
  }
}
