syntax = "proto3";

package vg;

// *Graphs* are collections of nodes and edges
// They can represent subgraphs of larger graphs
// or be wholly-self-sufficient.
// Protobuf memory limits of 67108864 bytes mean we typically keep the size
// of them small generating graphs as collections of smaller subgraphs.
//
message Graph {
    repeated Node node = 1;
    repeated Edge edge = 2;
    repeated Path path = 3;
}

// *Nodes* store sequence data and also links to other nodes.
// The graph is doubly-linked via IDs in prev/next fields in the Node objects.
//
message Node {
    string sequence = 1;   // sequence
    string name = 2;  // a name provides an identifier
    int64 id = 3;     // ids are critical, and unique within a graph
}

// *Edges* describe linkages between nodes. They are directed: from=prev, to=next.
//
message Edge {
    int64 from = 1; // ID of upstream node
    int64 to = 2;   // ID of downstream node
    bool from_start = 3; // if the edge leaves from the 5' (start) of a node
    bool to_end = 4; // if the edge goes to the 3' (end) of a node
    int32 overlap = 5; // length of overlap
}

// *Edits* describe how to generate a new string from elements
// in the graph. To determine the new string, just walk the series of edits,
// stepping from_length distance in the basis node, and to_length in the
// novel element, replacing from_length in the basis node with the sequence.
//
// *matches* from_length == to_length
// *snps* from_length == to_length; sequence = alt
// *deletions* from_length > to_length; sequence may be unset or empty
// *insertions* from_length < to_length; sequence contains relative insertion
//
message Edit {
    int32 from_length = 1; // length in target/ref
    int32 to_length = 2; // length in read/alt
    string sequence = 3;
}

// A Mapping defines the relationship between a node in system and another entity.
// An empty edit list implies complete match, however it is preferred to specify the full edit structure.
// as it is more complex to handle special cases.
//
message Mapping {
    Position position = 1; // the position at which the first Edit, if any, in the Mapping starts. Inclusive.
    repeated Edit edit = 2; // series of edit to transform to region in read/alt
    int64 rank = 5; // the 1-based rank of the mapping in its containing path
}

// A position in the graph is a node, direction, and offset.
// The node is stored by ID, and the offset is 0-based and counts from the start of the node in the specified orientation.
// The direction specifies which orientation of the node we are considering, the forward (as stored) or reverse complement.
//
// Example:
//
// seq+        G A T T A C A
// offset+  → 0 1 2 3 4 5 6 7
//
// seq-        C T A A T G T
// offset-    7 6 5 4 3 2 1 0 ←
//
message Position {
    int64 node_id = 1;
    int64 offset = 2;
    bool is_reverse = 4; // true if we obtain the original sequence of the path by reverse complementing the mappings
}

// Paths are walks through nodes defined by a series of edits.
// They can be used to represent:
//    - haplotypes
//    - mappings of reads, or alignments, by including edits
//    - relationships between nodes
//    - annotations from other data sources, such as:
//          genes, exons, motifs, transcripts, peaks
//
message Path {
    string name = 1;
    repeated Mapping mapping = 2;
    bool is_circular = 3;
}

// Alignments link query strings, such as other genomes or reads, to paths.
//
message Alignment {
    string sequence = 1;
    Path path = 2;
    string name = 3;
    bytes quality = 4;
    int32 mapping_quality = 5;
    int32 score = 6;
    int32 query_position = 7;
    string sample_name = 9;
    string read_group = 10;
    Alignment fragment_prev = 11; // e.g. contains an alignment with only a name, or only a graph mapping position
    Alignment fragment_next = 12; // same thing for next in fragment
    bool is_secondary = 15; // All but one maximal-scoring alignment of a given read in a GAM file must be secondary.
    double identity = 16; // Portion of aligned bases that are perfect matches, or 0 if no bases are aligned.
}

// Fragments represent the library fragments that yield pairs of alignments
// or possibly a series of small alignments generated by a strobed read
message Fragment {
    repeated Alignment alignment = 1;
}

// used to serialize kmer matches
message KmerMatch {
    string sequence = 1;
    int64 node_id = 2;
    sint32 position = 3;
    
    // If true, this kmer is backwards relative to its node, and position counts from the end of the node.
    bool backward = 4; 
}

// Summarizes reads that map to single position in the graph.
// This structure is pretty much identical to a line in Samtools pileup format
// if qualities set, it must have size = num_bases
message BasePileup {
    int32 ref_base = 1;
    int32 num_bases = 2;
    string bases = 3;
    bytes qualities = 4;
}

// Collect pileup records by node.  Saves some space and hashing over
// storing individually, assuming not too sparse and avg. node length more than couple bases
// the ith BasePileup in the array corresponds to the position at offset i. 
message NodePileup {
    int64 node_id = 1;
    repeated BasePileup base_pileup = 2;
}

// Keep pileup-like record for reads that span edges
message EdgePileup {
    Edge edge = 1;
 	int32 num_reads = 2;
	bytes qualities = 3;
}

// Bundle up Node and Edge pileups
message Pileup {
    repeated NodePileup node_pileups = 1;
    repeated EdgePileup edge_pileups = 2;
}

message Locus {
    string name = 1;
    // These are all the alleles at the locus, not just the called ones.
    // Note that a primary reference allele may or may not appear.
    repeated Path allele = 2;
    // These supports are per-allele, matching the alleles above
    repeated Support support = 3;
    // sorted by likelihood or posterior
    //  the first one is the "call"
    repeated Genotype genotype = 4;
    // We also have a Support for the locus overall, because reads may have
    // supported multiple alleles and we want to know how many total there were.
    Support overall_support = 5;
}

message Genotype {
    // these refer to the offsets of the alleles in the Locus object
    repeated int32 allele = 1;
    bool is_phased = 2;
    double likelihood = 3;
    double log_likelihood = 4; // Likelihood logged base 2
    double log_prior = 5; // Prior logged base 2
    double log_posterior = 6; // Posterior logged base 2 (unnormalized)
}

// aggregates information about the reads supporting an allele
message Support {
    double quality = 1;
    int32 forward = 2;
    int32 reverse = 3;
    int32 left = 4;
    int32 right = 5;
}

message Translation {
    Path from = 1;
    Path to = 2;
}
