{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://theamateur.co.uk/schemas/dpl/dpl.draft-grammar-1.schema.json",
  "title": "DPL drafting grammar (dpl.draft-grammar/1)",
  "description": "The versioned data file the drafting protocol reads: coverage recipes (the shots a kind of scene usually needs, as roles, sizes and moves) and the picture shape of known posting platforms. A grammar profile learned from studied films (I2) will be another file of this shape. Unknown members and misspelt terms are refused.",
  "$comment": "Structure and terms only. A test also holds every recipe to the plan format: each role, size, angle and movement is a term of dpl.plan/1, ids are unique, and every source is an entry of docs/prior-art/entries.",
  "type": "object",
  "properties": {
    "format": {
      "type": "string",
      "const": "dpl.draft-grammar/1"
    },
    "version": {
      "type": "string"
    },
    "description": {
      "type": "string"
    },
    "platforms": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/PlatformDefault"
      }
    },
    "recipes": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/Recipe"
      },
      "minItems": 1
    }
  },
  "required": [
    "format",
    "version",
    "description",
    "platforms",
    "recipes"
  ],
  "additionalProperties": false,
  "$defs": {
    "PlatformDefault": {
      "type": "object",
      "properties": {
        "match": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "aspect": {
          "type": "string",
          "enum": [
            "9:16",
            "16:9",
            "1:1",
            "4:5"
          ]
        },
        "note": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "match",
        "aspect"
      ],
      "additionalProperties": false
    },
    "Recipe": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "pattern": "^[a-z][a-z0-9_]*$"
        },
        "title": {
          "type": "string"
        },
        "summary": {
          "type": "string"
        },
        "use_when": {
          "type": "string"
        },
        "signals": {
          "$ref": "#/$defs/RecipeSignals"
        },
        "slots": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/RecipeSlot"
          },
          "minItems": 1
        },
        "notes": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "sources": {
          "description": "Prior-art register entries the recipe is based on (docs/prior-art/entries/PA-…).",
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^PA-\\d+-\\d+$"
          }
        },
        "shoot_plan": {
          "description": "The Shoot Plan recipe it was ported from or matches, when there is one.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "id",
        "title",
        "summary",
        "use_when",
        "signals",
        "slots",
        "notes",
        "sources"
      ],
      "additionalProperties": false
    },
    "RecipeSignals": {
      "type": "object",
      "description": "What makes a recipe fit a scene. Every part is optional; a recipe with none fits only by being the default.",
      "properties": {
        "speakers": {
          "description": "The number of distinct speakers in the scene's lines.",
          "type": [
            "integer",
            "null"
          ],
          "minimum": 1
        },
        "address": {
          "description": "Who the scene's lines are said to: camera when a speaker addresses the lens.",
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "camera"
          ]
        },
        "keywords": {
          "description": "Words in the scene's purpose or change that point to this kind of scene (lower case, matched as whole words or word starts).",
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      },
      "additionalProperties": false
    },
    "RecipeSlot": {
      "type": "object",
      "description": "One shot a recipe asks for, as a role and a framing: never words for the film.",
      "properties": {
        "id": {
          "type": "string",
          "pattern": "^[a-z][a-z0-9_]*$"
        },
        "role": {
          "description": "A coverage.role term.",
          "type": "string",
          "enum": [
            "establishing",
            "master",
            "two_shot",
            "single",
            "over_shoulder",
            "reaction",
            "insert",
            "cutaway",
            "pov",
            "neutral"
          ]
        },
        "priority": {
          "description": "A coverage.priority term.",
          "type": "string",
          "enum": [
            "must",
            "should",
            "nice"
          ]
        },
        "size": {
          "description": "A camera.size term.",
          "type": "string",
          "enum": [
            "extreme_wide",
            "wide",
            "full",
            "medium_wide",
            "cowboy",
            "medium",
            "medium_close_up",
            "close_up",
            "extreme_close_up",
            "detail",
            "macro"
          ]
        },
        "angle": {
          "description": "A camera.angle term.",
          "type": "string",
          "enum": [
            "overhead",
            "high",
            "eye_level",
            "shoulder_level",
            "low",
            "hip_level",
            "knee_level",
            "ground_level",
            "worms_eye",
            "dutch"
          ]
        },
        "movement": {
          "description": "A camera.movement term.",
          "type": "string",
          "enum": [
            "hold",
            "handheld_hold",
            "pan",
            "whip_pan",
            "tilt",
            "push_in",
            "pull_out",
            "track",
            "dolly",
            "crab",
            "arc",
            "orbit",
            "zoom",
            "dolly_zoom",
            "vertigo",
            "handheld",
            "steadicam",
            "boom",
            "jib",
            "crane",
            "pedestal",
            "roll",
            "aerial",
            "helicopter",
            "drone",
            "rise",
            "drift",
            "descend"
          ]
        },
        "setup": {
          "description": "A set-up label (A), or per_speaker, per_listener, per_step, per_beat, per_topic: a set-up of its own for each.",
          "type": "string"
        },
        "repeat": {
          "description": "How many shots of this slot: once, or one for each per_speaker, per_turn (each line that turns the scene), per_step, per_answer, per_beat or per_topic.",
          "type": "string",
          "enum": [
            "once",
            "per_speaker",
            "per_turn",
            "per_step",
            "per_answer",
            "per_beat",
            "per_topic"
          ]
        },
        "job": {
          "description": "What the shot is for, in general terms and with the readiness rules it serves.",
          "type": "string"
        }
      },
      "required": [
        "id",
        "role",
        "priority",
        "size",
        "angle",
        "movement",
        "setup",
        "repeat",
        "job"
      ],
      "additionalProperties": false
    }
  }
}
