/* -*- coding: utf-8 -*-
 *
 * Copyright 2022 IBM RESEARCH. All Rights Reserved.
 *
 * Licensed 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.
 * =============================================================================
 */

#ifndef __QASM_AST_DEFCAL_BUILDER_H
#define __QASM_AST_DEFCAL_BUILDER_H

#include <qasm/AST/ASTDefcal.h>

#include <cassert>
#include <map>
#include <string>
#include <vector>

namespace QASM {

class ASTSymbolTableEntry;

typedef std::vector<ASTDefcalNode *> ASTDefcalNodeList;
typedef std::map<std::string, ASTDefcalNode *> ASTDefcalNodeMap;

class ASTDefcalBuilder {
private:
  static ASTDefcalNodeList DL;
  static ASTDefcalNodeMap DM;
  static ASTDefcalBuilder DB;
  static uint32_t DC;
  static bool DCS;

protected:
  ASTDefcalBuilder() = default;

public:
  using map_type = std::map<std::string, ASTDefcalNode *>;
  using map_iterator = typename map_type::iterator;
  using const_map_iterator = typename map_type::const_iterator;

  using list_type = std::vector<ASTDefcalNode *>;
  using list_iterator = typename list_type::iterator;
  using const_list_iterator = typename list_type::const_iterator;

public:
  static ASTDefcalBuilder &Instance() { return ASTDefcalBuilder::DB; }

  ~ASTDefcalBuilder() = default;

  static ASTDefcalNodeMap *Map() { return &ASTDefcalBuilder::DM; }

  static ASTDefcalNodeList *List() { return &ASTDefcalBuilder::DL; }

  void OpenContext() { DCS = true; }

  void CloseContext() { DCS = false; }

  bool InOpenContext() const { return DCS; }

  void ValidateDefcalContext() const;

  bool Insert(const std::string &Id, ASTDefcalNode *DN) {
    if (ASTDefcalBuilder::DM.insert(std::make_pair(Id, DN)).second) {
      ASTDefcalBuilder::DL.push_back(DN);
      return true;
    }

    return false;
  }

  ASTDefcalNode *Find(const std::string &DN) const {
    const_map_iterator F = DM.find(DN);
    return F == DM.end() ? nullptr : (*F).second;
  }

  ASTDefcalNode *Find(const ASTIdentifierNode *Id) const {
    return Find(Id->GetName());
  }

  void Clear() {
    DM.clear();
    DL.clear();
  }

  std::size_t Size() {
    assert(DM.size() == DL.size() &&
           "Inconsistent DefcalNodeMap <-> DefcalNodeList sizes!");
    return DL.size();
  }

  static ASTDefcalNode *Root() { return DL.front(); }

  uint32_t DefcalCounter() { return ++DC; }

  uint32_t GetDefcalCounter() const { return DC; }

  list_iterator begin() { return DL.begin(); }

  const_list_iterator begin() const { return DL.begin(); }

  list_iterator end() { return DL.end(); }

  const_list_iterator end() const { return DL.end(); }
};

} // namespace QASM

#endif // __QASM_AST_DEFCAL_BUILDER_H
