/**
   * {@inheritDoc} <br>
   * Referring pages: 69.
   *
   * @since 1.0
   */
  @SuppressWarnings("unchecked")
  @Override
  public boolean check(final Node node) {

    final int size = node.childrenCount();
    final Node child = node.getChild(0);
    child.addAttributeRecursive(Attribute.ITYPE, node.getAttribute(Attribute.ITYPE));
    if (!child.check()) {
      return Utils.badNode(node);
    }

    final Type type = (Type) child.getAttribute(Attribute.TYPE);
    Integer elemCount = (Integer) child.getAttribute(Attribute.ELEMENT_COUNT);
    if (elemCount == null) {
      elemCount = 1;
    }

    if (size == 1) {
      // 2. <izravni_deklarator> != {const(T), niz(const(T))}
      final boolean first = type.isConst();
      final boolean second = type.isArray() ? ((ArrayType) type).fromArray().isConst() : false;
      if (first || second) {
        return Utils.badNode(node);
      }

      return true;
    }

    for (int i = 1; i < size; i++) {
      final Node current = node.getChild(i);

      if (!current.check()) {
        return Utils.badNode(node);
      }

      if (current.name().equals(InitializatorManipulator.HR_NAME)) {
        List<Type> initTypes;
        if (type.isArray()) {
          initTypes = (List<Type>) current.getAttribute(Attribute.TYPES);
        } else {
          initTypes = new ArrayList<>();
          final Type initType = (Type) current.getAttribute(Attribute.TYPE);
          if (initType == null) {
            return Utils.badNode(node);
          }
          initTypes.add((Type) current.getAttribute(Attribute.TYPE));
        }

        if (handleInits(elemCount, type, initTypes)) {
          return true;
        } else {
          return Utils.badNode(node);
        }
      }
    }

    System.err.println("Shold never happen");
    SemanticErrorReporter.report(node);
    return false;
  }
  @Override
  public void generate(Node node) {
    Node directDeclarator = node.getChild(0);
    String name = (String) directDeclarator.getChild(0).getAttribute(Attribute.VALUE);
    Type type = directDeclarator.identifierTable().variable(name);

    switch (Production.fromNode(node)) {
      case INITIALIZATION_DECLARATOR_1:
        {
          // INITIALIZATION_DECLARATOR_1("<init_deklarator> ::= <izravni_deklarator>"),
          FRISCGenerator.generateCommand(ch.move(0, Reg.R5));
          FRISCGenerator.generateCommand(ch.push(Reg.R5));
          CallStack.push(name, type);
          if (type.isArray()) {
            int size = (Integer) directDeclarator.getChild(2).getAttribute(Attribute.VALUE);

            for (int i = 1; i < size; i++) {
              FRISCGenerator.generateCommand(ch.push(Reg.R5));
              CallStack.push(null, type.fromArray());
            }
          }
          break;
        }

      case INITIALIZATION_DECLARATOR_2:
        {
          // INITIALIZATION_DECLARATOR_2("<init_deklarator> ::= <izravni_deklarator> OP_PRIDRUZI
          // <inicijalizator>");
          node.getChild(2).generate();
          CallStack.push(name, type);
          if (type.isArray()) {
            int size = (Integer) directDeclarator.getChild(2).getAttribute(Attribute.VALUE);

            for (int i = 1; i < size; i++) {
              CallStack.push(null, type.fromArray());
            }
          }
          break;
        }

      default:
        System.err.println("Generation reached undefined production!");
        break;
    }
  }
  private static boolean handleInits(
      final Integer elemCount, Type myType, final List<Type> initTypes) {
    if ((initTypes == null) || (myType == null) || (elemCount < initTypes.size())) {
      return false;
    }

    if (myType.isArray()) {
      myType = myType.fromArray();
    }

    for (final Type type : initTypes) {
      if (!type.implicitConversion(myType)) {
        return false;
      }
    }

    return true;
  }
  /**
   * {@inheritDoc} <br>
   * Referring pages: 57.
   *
   * @since 1.0
   */
  @Override
  public boolean check(final Node node) {
    final Node firstChild = node.getChild(0);
    final String firstSymbol = firstChild.name();

    // <multiplikativni_izraz> ::= <cast_izraz>
    if ("<cast_izraz>".equals(firstSymbol)) {

      // 1. provjeri(<cast_izraz>)
      if (!firstChild.check()) {
        SemanticErrorReporter.report(node);
        return false;
      }

      node.addAttribute(Attribute.TYPE, firstChild.getAttribute(Attribute.TYPE));
      node.addAttribute(Attribute.L_EXPRESSION, firstChild.getAttribute(Attribute.L_EXPRESSION));
      return true;
    }
    final Node secondChild = node.getChild(1);
    final String secondSymbol = secondChild.name();
    final Node thirdChild = node.getChild(2);
    // <multiplikativni_izraz> ::= <multiplikativni_izraz> OP_PUTA <cast_izraz>
    if ("OP_PUTA".equals(secondSymbol)) {

      // 1. provjeri(<multiplikativni_izraz>
      if (!firstChild.check()) {
        SemanticErrorReporter.report(node);
        return false;
      }

      // 2. <multiplikativni_izraz>.tip ~ int
      final Type type1 = (Type) firstChild.getAttribute(Attribute.TYPE);
      if (!type1.implicitConversion(new IntType())) {
        SemanticErrorReporter.report(node);
        return false;
      }

      // 3. provjeri(<cast_izraz>)
      if (!thirdChild.check()) {
        SemanticErrorReporter.report(node);
        return false;
      }

      // 4. <cast_izraz>.tip ~ int
      final Type type2 = (Type) thirdChild.getAttribute(Attribute.TYPE);
      if (!type2.implicitConversion(new IntType())) {
        SemanticErrorReporter.report(node);
        return false;
      }

      node.addAttribute(Attribute.TYPE, new IntType());
      node.addAttribute(Attribute.L_EXPRESSION, false);
      return true;
    }

    // <multiplikativni_izraz> ::= <multiplikativni_izraz> OP_DIJELI <cast_izraz>
    if ("OP_DIJELI".equals(secondSymbol)) {

      // 1. provjeri(<multiplikativni_izraz>
      if (!firstChild.check()) {
        SemanticErrorReporter.report(node);
        return false;
      }

      // 2. <multiplikativni_izraz>.tip ~ int
      final Type type1 = (Type) firstChild.getAttribute(Attribute.TYPE);
      if (!type1.implicitConversion(new IntType())) {
        SemanticErrorReporter.report(node);
        return false;
      }

      // 3. provjeri(<cast_izraz>)
      if (!thirdChild.check()) {
        SemanticErrorReporter.report(node);
        return false;
      }

      // 4. <cast_izraz>.tip ~ int
      final Type type2 = (Type) thirdChild.getAttribute(Attribute.TYPE);
      if (!type2.implicitConversion(new IntType())) {
        SemanticErrorReporter.report(node);
        return false;
      }

      node.addAttribute(Attribute.TYPE, new IntType());
      node.addAttribute(Attribute.L_EXPRESSION, false);
      return true;
    }

    // <multiplikativni_izraz> ::= <multiplikativni_izraz> OP_MOD <cast_izraz>
    if ("OP_MOD".equals(secondSymbol)) {

      // 1. provjeri(<multiplikativni_izraz>
      if (!firstChild.check()) {
        SemanticErrorReporter.report(node);
        return false;
      }

      // 2. <multiplikativni_izraz>.tip ~ int
      final Type type1 = (Type) firstChild.getAttribute(Attribute.TYPE);
      if (!type1.implicitConversion(new IntType())) {
        SemanticErrorReporter.report(node);
        return false;
      }

      // 3. provjeri(<cast_izraz>)
      if (!thirdChild.check()) {
        SemanticErrorReporter.report(node);
        return false;
      }

      // 4. <cast_izraz>.tip ~ int
      final Type type2 = (Type) thirdChild.getAttribute(Attribute.TYPE);
      if (!type2.implicitConversion(new IntType())) {
        SemanticErrorReporter.report(node);
        return false;
      }

      node.addAttribute(Attribute.TYPE, new IntType());
      node.addAttribute(Attribute.L_EXPRESSION, false);
      return true;
    }

    System.err.println("Shold never happen");
    SemanticErrorReporter.report(node);
    return false;
  }