//===-- BreakpointName.h --------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_BREAKPOINT_BREAKPOINTNAME_H
#define LLDB_BREAKPOINT_BREAKPOINTNAME_H

#include <memory>
#include <string>
#include <unordered_set>
#include <vector>

#include "lldb/Breakpoint/BreakpointID.h"
#include "lldb/Breakpoint/BreakpointLocationCollection.h"
#include "lldb/Breakpoint/BreakpointLocationList.h"
#include "lldb/Breakpoint/BreakpointOptions.h"
#include "lldb/Breakpoint/Stoppoint.h"
#include "lldb/Core/SearchFilter.h"
#include "lldb/Utility/Event.h"
#include "lldb/Utility/Flags.h"
#include "lldb/Utility/StringList.h"
#include "lldb/Utility/StructuredData.h"

namespace lldb_private {

class BreakpointName {
public:
  class Permissions
  {
  public:
  
    enum PermissionKinds { listPerm = 0, disablePerm = 1, 
                       deletePerm = 2, allPerms = 3 };

    Permissions(bool in_list, bool in_disable, bool in_delete) 
    {
      m_permissions[listPerm]    = in_list;
      m_permissions[disablePerm] = in_disable;
      m_permissions[deletePerm]  = in_delete;
      m_set_mask.Set(permissions_mask[allPerms]);
    }
    
    Permissions(const Permissions &rhs)
    {
      m_permissions[listPerm]    = rhs.m_permissions[listPerm];
      m_permissions[disablePerm] = rhs.m_permissions[disablePerm];
      m_permissions[deletePerm]  = rhs.m_permissions[deletePerm];
      m_set_mask = rhs.m_set_mask;
    }
    
    Permissions() 
    {
      m_permissions[listPerm]    = true;
      m_permissions[disablePerm] = true;
      m_permissions[deletePerm]  = true;
      m_set_mask.Clear();
    }
    
    const Permissions &operator= (const Permissions &rhs)
    {
      if (this != &rhs) {
        m_permissions[listPerm]    = rhs.m_permissions[listPerm];
        m_permissions[disablePerm] = rhs.m_permissions[disablePerm];
        m_permissions[deletePerm]  = rhs.m_permissions[deletePerm];
        m_set_mask = rhs.m_set_mask;
      }
      return *this;
    }
    
    void Clear() {
      *this = Permissions();
    }
    
    // Merge the permissions from incoming into this set of permissions. Only
    // merge set permissions, and most restrictive permission wins.
    void MergeInto(const Permissions &incoming)
    {
      MergePermission(incoming, listPerm);
      MergePermission(incoming, disablePerm);
      MergePermission(incoming, deletePerm);
    }

    bool GetAllowList() const { return GetPermission(listPerm); }
    bool SetAllowList(bool value) { return SetPermission(listPerm, value); }
    
    bool GetAllowDelete() const { return GetPermission(deletePerm); }
    bool SetAllowDelete(bool value) { return SetPermission(deletePerm, value); }
    
    bool GetAllowDisable() const { return GetPermission(disablePerm); }
    bool SetAllowDisable(bool value) { return SetPermission(disablePerm, 
                                                            value); }

    bool GetPermission(enum PermissionKinds permission) const
    {
      return m_permissions[permission];
    }

    bool GetDescription(Stream *s, lldb::DescriptionLevel level);

    bool IsSet(enum PermissionKinds permission) const
    {
      return m_set_mask.Test(permissions_mask[permission]);
    }
    
    bool AnySet() {
      return m_set_mask.AnySet(permissions_mask[allPerms]);
    }
    
  private:
    static const Flags::ValueType permissions_mask[allPerms + 1];
    
    bool m_permissions[allPerms];
    Flags m_set_mask;
    
    bool SetPermission(enum PermissionKinds permission, bool value)
    {
      bool old_value = m_permissions[permission];
      m_permissions[permission] = value;
      m_set_mask.Set(permissions_mask[permission]);
      return old_value;
    }
    
    // If either side disallows the permission, the resultant disallows it.
    void MergePermission(const Permissions &incoming, 
                         enum PermissionKinds permission)
    {
      if (incoming.IsSet(permission))
      {
        SetPermission(permission, !(m_permissions[permission] |
            incoming.m_permissions[permission]));
      }
    }
  };
  
  BreakpointName(ConstString name, const char *help = nullptr) :
      m_name(name), m_options(false)
   {
     SetHelp(help);
   }
  
  BreakpointName(const BreakpointName &rhs) :
      m_name(rhs.m_name), m_options(rhs.m_options),
      m_permissions(rhs.m_permissions), m_help(rhs.m_help)
  {}
      
  ConstString GetName() const { return m_name; }
  BreakpointOptions &GetOptions() { return m_options; }
  const BreakpointOptions &GetOptions() const { return m_options; }
  
  void SetOptions(const BreakpointOptions &options) {
    m_options = options;
  }
  
  Permissions &GetPermissions() { return m_permissions; }
  const Permissions &GetPermissions() const { return m_permissions; }
  void SetPermissions(const Permissions &permissions) {
    m_permissions = permissions;
  }
  
  bool GetPermission(Permissions::PermissionKinds permission) const
  {
    return m_permissions.GetPermission(permission);
  }
  
  void SetHelp(const char *description)
  {
    if (description)
      m_help.assign(description);
    else
      m_help.clear();
  }
  
  const char *GetHelp()
  {
    return m_help.c_str();
  }
  
  // Returns true if any options were set in the name
  bool GetDescription(Stream *s, lldb::DescriptionLevel level);
  
  void ConfigureBreakpoint(lldb::BreakpointSP bp_sp);
  
private:
  ConstString        m_name;
  BreakpointOptions  m_options;
  Permissions        m_permissions;
  std::string        m_help;
};

} // namespace lldb_private

#endif // LLDB_BREAKPOINT_BREAKPOINTNAME_H
