/*
 * Copyright 2014 Google Inc. 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 SEMISTATIC_GRAPH_H
#define SEMISTATIC_GRAPH_H

#include <fruit/impl/data_structures/semistatic_map.h>

#ifdef FRUIT_EXTRA_DEBUG
#include <iostream>
#endif

namespace fruit {
namespace impl {

// The alignas ensures that a SemistaticGraphInternalNodeId* always has 0 in the low-order bit.
struct alignas(2) alignas(alignof(std::size_t)) SemistaticGraphInternalNodeId {
  // This stores the index in the vector times sizeof(NodeData).
  std::size_t id;
  
  bool operator==(const SemistaticGraphInternalNodeId& x) const;
  bool operator<(const SemistaticGraphInternalNodeId& x) const;
};

/**
 * A direct graph implementation where most of the graph is fixed at construction time, but a few nodes and edges can be added
 * later.
 * 
 * Also, nodes can either be normal nodes or terminal nodes. Terminal nodes can't have outgoing edges. Note that a node with no
 * outgoing edges may or may not be marked as terminal.
 * 
 * While insertion of elements after construction is supported, inserting or changing the neighbors of more than O(1) nodes
 * after construction will raise the cost of any further operations to more than O(1).
 * 
 * Even though adding nodes/edges after construction is inefficient, it is efficient to turn non-terminal nodes into terminal ones
 * (and therefore removing all the outgoing edges from the node) after construction.
 * 
 * NodeId and Node must be default constructible and trivially copyable.
 */
template <typename NodeId, typename Node>
class SemistaticGraph {
private:
  using InternalNodeId = SemistaticGraphInternalNodeId;
  
  // The node data for nodeId is in nodes[node_index_map.at(nodeId)/sizeof(NodeData)].
  // To avoid hash table lookups, the edges in edges_storage are stored as indexes of `nodes' instead of as NodeIds.
  // node_index_map contains all known NodeIds, including ones known only due to an outgoing edge ending there from another node.
  SemistaticMap<NodeId, InternalNodeId> node_index_map;
  
  struct NodeData {
#ifdef FRUIT_EXTRA_DEBUG
    NodeId key;
#endif
    
  public:
    // If edges_begin==0, this is a terminal node.
    // If edges_begin==1, this node doesn't exist, it's just referenced by another node.
    // Otherwise, reinterpret_cast<InternalNodeId*>(edges_begin) is the beginning of the edges range.
    std::uintptr_t edges_begin;
  
  // An explicit "public" specifier here prevents the compiler from reordering the fields.
  // We want the edges_begin field to be stored first because that's what we're going to branch on.
  public:
    Node node;
  };
  
  std::size_t first_unused_index;
  
  FixedSizeVector<NodeData> nodes;
  
  // Stores vectors of edges as contiguous chunks of node IDs.
  // The NodeData elements in `nodes' contain indexes into this vector (stored as already multiplied by sizeof(NodeData)).
  // The first element is unused.
  FixedSizeVector<InternalNodeId> edges_storage;
  
#ifdef FRUIT_EXTRA_DEBUG
  template <typename NodeIter>
  void printGraph(NodeIter first, NodeIter last);
#endif
  
  NodeData* nodeAtId(InternalNodeId internalNodeId);
  const NodeData* nodeAtId(InternalNodeId internalNodeId) const;
  
  static NodeData* nodeAtId(NodeData* nodes_begin, InternalNodeId internalNodeId);  
  static const NodeData* nodeAtId(const NodeData* nodes_begin, InternalNodeId internalNodeId);
    
public:
  
  class edge_iterator;
  
  class node_iterator {
  private:
    NodeData* itr;
    
    friend class SemistaticGraph<NodeId, Node>;
    
    node_iterator(NodeData* itr);
    
  public:
    Node& getNode();
    
    bool isTerminal();
    
    // Turns the node into a terminal node, also removing all the deps.
    void setTerminal();
  
    // Assumes !isTerminal().
    // neighborsEnd() is NOT provided/stored for efficiency, the client code is expected to know the number of neighbors.
    edge_iterator neighborsBegin();
    
    bool operator==(const node_iterator&) const;
  };
  
  class const_node_iterator {
  private:
    const NodeData* itr;
    
    friend class SemistaticGraph<NodeId, Node>;
    
    const_node_iterator(const NodeData* itr);
    
  public:
    const Node& getNode();
    
    bool isTerminal();
    
    bool operator==(const const_node_iterator&) const;
  };
  
  class edge_iterator {
  private:
    // Iterator on edges_storage.
    InternalNodeId* itr;
    
    friend class SemistaticGraph<NodeId, Node>;
    friend class SemistaticGraph<NodeId, Node>::node_iterator;
    
    edge_iterator(InternalNodeId* itr);

  public:
    // getNodeIterator(graph.nodes.begin()) returns the first neighbor.
    node_iterator getNodeIterator(node_iterator nodes_begin);
    
    void operator++();
    
    // Equivalent to i times operator++ followed by getNodeIterator(nodes_begin).
    node_iterator getNodeIterator(std::size_t i, node_iterator nodes_begin);
  };
  
  // Constructs an *invalid* graph (as if this graph was just moved from).
  SemistaticGraph() = default;
  
  // A value x obtained dereferencing a NodeIter::value_type must support the following operations:
  // * x.getId(), returning a NodeId
  // * x.getValue(), returning a Node
  // * x.isTerminal(), returning a bool
  // * x.getEdgesBegin() and x.getEdgesEnd(), that if !x.isTerminal() define a range of values of type NodeId (the outgoing edges).
  // 
  // This constructor is *not* defined in semistatic_graph.templates.h, but only in semistatic_graph.cc.
  // All instantiations must have a matching instantiation in semistatic_graph.cc.
  template <typename NodeIter>
  SemistaticGraph(NodeIter first, NodeIter last, NodeId invalidNodeId1, NodeId invalidNodeId2);
  
  SemistaticGraph(SemistaticGraph&&) = default;
  SemistaticGraph(const SemistaticGraph&) = delete;
  
  // Creates a copy of x with the additional nodes in [first, last). The requirements on NodeIter as the same as for the 2-arg
  // constructor.
  // The nodes in [first, last) must NOT be already in x, but can be neighbors of nodes in x.
  // The new graph will share data with `x', so must be destroyed before `x' is destroyed.
  // Also, after this is called, `x' must not be modified until this object has been destroyed.
  template <typename NodeIter>
  SemistaticGraph(const SemistaticGraph& x, NodeIter first, NodeIter last);
  
  SemistaticGraph& operator=(const SemistaticGraph&) = delete;
  SemistaticGraph& operator=(SemistaticGraph&&) = default;
  
  // The result is unspecified. The only guarantee is that it's the right value to pass to edge_iterator's getNodeIterator() methods.
  node_iterator begin();
  
  node_iterator end();
  const_node_iterator end() const;
  
  // Precondition: `nodeId' must exist in the graph.
  // Unlike std::map::at(), this yields undefined behavior if the precondition isn't satisfied (instead of throwing).
  node_iterator at(NodeId nodeId);
  
  // Prefer using at() when possible, this is slightly slower.
  // Returns end() if the node ID was not found.
  node_iterator find(NodeId nodeId);
  const_node_iterator find(NodeId nodeId) const;
  
  // Changes the node with ID nodeId (that must exist) to a terminal node.
  void changeNodeToTerminal(NodeId nodeId);
  
#ifdef FRUIT_EXTRA_DEBUG
  // Emits a runtime error if some node was not created but there is an edge pointing to it.
  void checkFullyConstructed();
#endif
};

} // namespace impl
} // namespace fruit

#include <fruit/impl/data_structures/semistatic_graph.defn.h>

// semistatic_graph.templates.h is not included here to limit the transitive includes. Include it explicitly (in .cpp files).

#endif // SEMISTATIC_GRAPH_H
