#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

#define ARR_LEN(X) (sizeof(X) / sizeof(X[0]))

typedef enum {
  START_STATE,
  DECIMAL_STATE,
  NUM_STATE,
  FRACTION_STATE,
  EXPONENT_STATE,
  EXP_SIGN_STATE,
  POWER_STATE,
  END_STATE,
  INVALID_STATE,
} states;

states handle_start(char input);
states handle_decimal(char input);
states handle_num(char input);
states handle_fraction(char input);
states handle_exponent(char input);
states handle_exp_sign(char input);
states handle_power(char input);

states state_machine(states current, char input);

bool check_number(const char *num);

const char *inputs[] = {
    "0000000", // INVALID
    "01", // INVALID
    "f0", // INVALID
    "1f", // INVALID
    "1.f", // INVALID
    "1.0f", // INVALID
    "8987", // VALID
    "9.5", // VALID
    "    7", // VALID
    "  7.8", // VALID
    "732.9834e",   // INVALID
    "732.9834e-",  // INVALID
    "732.9834e+",  // INVALID
    "732.9834e-g", // INVALID
    "732.9834e-8", // VALID
    "732.9834e+8", // VALID
};

int main(int argc, char *argv[]) {
  for (int i = 0; i < ARR_LEN(inputs); ++i) {
    printf("%s is %s\n", inputs[i],
           check_number(inputs[i]) ? "VALID" : "INVALID");
  }

  return EXIT_SUCCESS;
}

states handle_start(char input) {
  if (isspace(input)) {
    return START_STATE;
  } else if (input == '0') {
    return DECIMAL_STATE;
  } else if (isdigit(input)) {
    return NUM_STATE;
  }

  return INVALID_STATE;
}

states handle_decimal(char input) {
  if (input == '.') {
    return FRACTION_STATE;
  }

  return INVALID_STATE;
}

states handle_num(char input) {
  if (isdigit(input)) {
    return NUM_STATE;
  } else if (input == '.') {
    return FRACTION_STATE;
  } else if (isspace(input)) {
    return END_STATE;
  }

  return INVALID_STATE;
}

states handle_fraction(char input) {
  if (isdigit(input)) {
    return FRACTION_STATE;
  } else if (isspace(input)) {
    return END_STATE;
  } else if (input == 'e' || input == 'E') {
    return EXPONENT_STATE;
  }

  return INVALID_STATE;
}

states handle_exponent(char input) {
  if (isdigit(input)) {
    return POWER_STATE;
  } else if (input == '+' || input == '-') {
    return EXP_SIGN_STATE;
  }

  return INVALID_STATE;
}

states handle_exp_sign(char input) {
  if (isdigit(input)) {
    return POWER_STATE;
  }

  return INVALID_STATE;
}

states handle_power(char input) {
  if (isdigit(input)) {
    return POWER_STATE;
  } else if (isspace(input)) {
    return END_STATE;
  }

  return INVALID_STATE;
}

states state_machine(states current, char input) {
  switch (current) {
  case START_STATE:
    return handle_start(input);
  case DECIMAL_STATE:
    return handle_decimal(input);
  case NUM_STATE:
    return handle_num(input);
  case FRACTION_STATE:
    return handle_fraction(input);
  case EXPONENT_STATE:
    return handle_exponent(input);
  case EXP_SIGN_STATE:
    return handle_exp_sign(input);
  case POWER_STATE:
    return handle_power(input);
  case END_STATE:
  case INVALID_STATE:
    return current;
  }
}

bool check_number(const char *num) {
  states current = START_STATE;

  for (const char *c = &(num[0]); *c != '\0'; ++c) {
    current = state_machine(current, *c);

    if (current == INVALID_STATE) {
      return false;
    }
  }

  return current == END_STATE || current == NUM_STATE ||
         current == FRACTION_STATE || current == POWER_STATE;
}