/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.iotdb.commons.path.fa;

import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.commons.path.PathPatternTree;

import java.util.Iterator;
import java.util.Map;
import java.util.Objects;

/**
 * This interface defines the behaviour of a FA(Finite Automation), generated from a path pattern or
 * a pattern tree.
 */
public interface IPatternFA {

  /**
   * @param state the source state of the returned transitions
   * @return transitions, that the given state has and only match one specified event rather than
   *     batch events
   */
  Map<String, IFATransition> getPreciseMatchTransition(IFAState state);

  /**
   * @param state the source state of the returned transitions
   * @return transitions, that the given state has and only match one specified event rather than
   *     batch events
   */
  Iterator<IFATransition> getPreciseMatchTransitionIterator(IFAState state);

  /**
   * @param state state the source state of the returned transitions
   * @return transitions, that the given state has and can match batch events
   */
  Iterator<IFATransition> getFuzzyMatchTransitionIterator(IFAState state);

  /**
   * @param state state the source state of the returned transitions
   * @return num of transitions, that the given state has and can match batch events
   */
  int getFuzzyMatchTransitionSize(IFAState state);

  /**
   * @param sourceState source state
   * @param transition transition that the source state has
   * @return next state
   */
  IFAState getNextState(IFAState sourceState, IFATransition transition);

  /**
   * @return the initial state of this FA
   */
  IFAState getInitialState();

  /**
   * @return the size of states this FA has
   */
  int getStateSize();

  /**
   * @param index the index of the target state, used for uniquely identifying states in FA
   * @return the state identified by given index
   */
  IFAState getState(int index);

  /**
   * Determines if there is overlap between the state transfer events of this FA. If it returns
   * true, then there may be overlap. If it returns false, there must be no overlap.
   *
   * @return may transition overlap
   */
  boolean mayTransitionOverlap();

  final class Builder {
    private PartialPath pathPattern;
    private PathPatternTree patternTree;
    private boolean isPrefixMatch = false;

    public Builder() {}

    public Builder pattern(PartialPath pattern) {
      this.pathPattern = pattern;
      return this;
    }

    /**
     * @param patternTree the included PartialPath must be a prefix or a fullPath
     */
    public Builder patternTree(PathPatternTree patternTree) {
      this.patternTree = patternTree;
      return this;
    }

    public Builder isPrefixMatch(boolean isPrefixMatch) {
      this.isPrefixMatch = isPrefixMatch;
      return this;
    }

    public PartialPath getPathPattern() {
      return pathPattern;
    }

    public PathPatternTree getPatternTree() {
      return patternTree;
    }

    public boolean isPrefixMatch() {
      return isPrefixMatch;
    }

    public IPatternFA buildNFA() {
      return FAFactory.getInstance().constructNFA(this);
    }

    public IPatternFA buildDFA() {
      return FAFactory.getInstance().constructDFA(this);
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      Builder builder = (Builder) o;
      return isPrefixMatch == builder.isPrefixMatch
          && Objects.equals(pathPattern, builder.pathPattern)
          && Objects.equals(patternTree, builder.patternTree);
    }

    @Override
    public int hashCode() {
      return Objects.hash(pathPattern, patternTree, isPrefixMatch);
    }
  }
}
