<?xml version="1.0" encoding="UTF-8"?>
<!--
  CabCodeXF — CabCode eXchange Format, v1.0 (DRAFT schema)
  Machine-neutral panel machining-intent interchange. See ../cabcodexf-spec.md for the
  prose spec, conventions and rationale. This XSD is the validatable contract a
  producer's post-processor is certified against.

  Conventions enforced here structurally; semantic conventions (datum, faces,
  depth-into-face, grain-explicit) are documented in the spec.
-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns="https://cabcode.uk/cabcodexf/1.0"
           targetNamespace="https://cabcode.uk/cabcodexf/1.0"
           elementFormDefault="qualified">

  <!-- ============================================================= root -->
  <xs:element name="CabCodeXF">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Header" type="Header"/>
        <xs:element name="Materials" type="Materials" minOccurs="0"/>
        <xs:element name="EdgeMaterials" type="EdgeMaterials" minOccurs="0"/>
        <xs:element name="Designs" type="Designs" minOccurs="0"/>
        <!-- A file holds Parts, Designs or both. A file with no Part must have a
             Designs block (the spec's rule; XSD 1.0 cannot express it). -->
        <xs:element name="Part" type="Part" minOccurs="0" maxOccurs="unbounded"/>
        <xs:element name="Assembly" type="Assembly" minOccurs="0"/>
      </xs:sequence>
      <xs:attribute name="version" type="xs:string" use="required"/>
    </xs:complexType>
  </xs:element>

  <!-- ============================================================= header -->
  <xs:complexType name="Header">
    <xs:sequence>
      <xs:element name="Order" minOccurs="0">
        <xs:complexType>
          <xs:attribute name="ref" type="xs:string"/>
          <xs:attribute name="customer" type="xs:string"/>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
    <xs:attribute name="units" type="xs:string" use="required"/>      <!-- "mm" -->
    <xs:attribute name="angleUnit" type="xs:string" default="deg"/>
    <xs:attribute name="producer" type="xs:string"/>
    <xs:attribute name="created" type="xs:dateTime"/>
  </xs:complexType>

  <!-- ====================================================== materials lib -->
  <xs:complexType name="Materials">
    <xs:sequence>
      <xs:element name="Material" type="Material" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>

  <!-- Neutral, resolvable descriptors + the producer's own shorthand token.
       No factory-internal code is authoritative (optional factoryCode hint only). -->
  <xs:complexType name="Material">
    <xs:attribute name="id" type="xs:ID" use="required"/>
    <xs:attribute name="token" type="xs:string"/>          <!-- producer shorthand -->
    <xs:attribute name="manufacturer" type="xs:string"/>
    <xs:attribute name="decor" type="xs:string"/>
    <xs:attribute name="finish" type="xs:string"/>
    <xs:attribute name="type" type="xs:string"/>           <!-- MFC, MDF, ply, … -->
    <xs:attribute name="thickness" type="xs:decimal"/>
    <xs:attribute name="description" type="xs:string"/>
    <xs:attribute name="factoryCode" type="xs:string"/>    <!-- non-authoritative hint -->
  </xs:complexType>

  <xs:complexType name="EdgeMaterials">
    <xs:sequence>
      <xs:element name="EdgeMaterial" type="EdgeMaterial" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="EdgeMaterial">
    <xs:attribute name="id" type="xs:ID" use="required"/>
    <xs:attribute name="token" type="xs:string"/>
    <xs:attribute name="kind" type="xs:string"/>           <!-- ABS, PVC, veneer, … -->
    <xs:attribute name="thickness" type="xs:decimal"/>
    <xs:attribute name="description" type="xs:string"/>
    <xs:attribute name="factoryCode" type="xs:string"/>
  </xs:complexType>

  <!-- ============================================================= designs -->
  <!-- Optional: whole parametric cabinets as their producing system designed
       them (a template plus sizes, materials and settings), so that system can
       reopen, resize and re-price them. Consumers that do not know the system
       ignore this block and machine the Parts. parts="omitted" means the file
       carries no Parts for that design: only a resolver for its `system`
       (e.g. CabCode) can turn it into parts. -->
  <xs:complexType name="Designs">
    <xs:sequence>
      <xs:element name="Design" type="Design" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="Design">
    <xs:sequence>
      <!-- One per copy, in order: where that copy sits. Overrides the Design's own x/y/z/rotation. -->
      <xs:element name="Place" type="DesignPlace" minOccurs="0" maxOccurs="unbounded"/>
      <xs:element name="Use" type="DesignUse" minOccurs="0" maxOccurs="unbounded"/>
      <xs:element name="Param" type="DesignParam" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="id" type="xs:ID" use="required"/>
    <xs:attribute name="system" type="xs:string" use="required"/>   <!-- owner of the template ids, e.g. "cabcode" -->
    <xs:attribute name="template" type="xs:string" use="required"/> <!-- e.g. "C101" -->
    <xs:attribute name="name" type="xs:string"/>
    <xs:attribute name="quantity" type="xs:positiveInteger" default="1"/>
    <xs:attribute name="height" type="xs:decimal" use="required"/>
    <xs:attribute name="width" type="xs:decimal" use="required"/>
    <xs:attribute name="depth" type="xs:decimal" use="required"/>
    <!-- Where the first copy sits (see DesignPlace). Unplaced copies follow the one before. -->
    <xs:attributeGroup ref="DesignPlacement"/>
    <xs:attribute name="code" type="xs:string"/>        <!-- the system's own saved-design code -->
    <xs:attribute name="catalogue" type="xs:string"/>   <!-- template catalogue version, if known -->
    <xs:attribute name="parts" type="DesignParts" default="included"/>
  </xs:complexType>

  <xs:simpleType name="DesignParts">
    <xs:restriction base="xs:string">
      <xs:enumeration value="included"/>
      <xs:enumeration value="omitted"/>
    </xs:restriction>
  </xs:simpleType>

  <!-- Placement in the job, as a CabCode job places its cabinets (mm, degrees):
       x along the wall, y up, z out from the wall; (x, y, z) is the cabinet's
       left, bottom, back corner; rotation turns it about the vertical. A value
       left out carries on from the copy before (x: that copy's x plus its width). -->
  <xs:attributeGroup name="DesignPlacement">
    <xs:attribute name="x" type="xs:decimal"/>
    <xs:attribute name="y" type="xs:decimal"/>
    <xs:attribute name="z" type="xs:decimal"/>
    <xs:attribute name="rotation" type="xs:decimal"/>
  </xs:attributeGroup>

  <xs:complexType name="DesignPlace">
    <xs:attributeGroup ref="DesignPlacement"/>
  </xs:complexType>

  <!-- A board or edging the design uses, by role (carcass, front, back, edging, …). -->
  <xs:complexType name="DesignUse">
    <xs:attribute name="role" type="xs:string" use="required"/>
    <xs:attribute name="material" type="xs:IDREF"/>      <!-- -> Material -->
    <xs:attribute name="edgeMaterial" type="xs:IDREF"/>  <!-- -> EdgeMaterial -->
  </xs:complexType>

  <!-- One of the system's own design settings, opaque to other consumers. -->
  <xs:complexType name="DesignParam">
    <xs:attribute name="key" type="xs:string" use="required"/>
    <xs:attribute name="value" type="xs:string" use="required"/>
    <xs:attribute name="type" type="ParamType" default="string"/>
  </xs:complexType>

  <xs:simpleType name="ParamType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="string"/>
      <xs:enumeration value="number"/>
      <xs:enumeration value="boolean"/>
      <xs:enumeration value="json"/>
    </xs:restriction>
  </xs:simpleType>

  <!-- ================================================================ part -->
  <xs:complexType name="Part">
    <xs:sequence>
      <xs:element name="Edging" type="Edging" minOccurs="0"/>
      <xs:element name="Operations" type="Operations" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute name="id" type="xs:ID" use="required"/>
    <xs:attribute name="name" type="xs:string"/>
    <xs:attribute name="quantity" type="xs:positiveInteger" default="1"/>
    <xs:attribute name="length" type="xs:decimal" use="required"/>   <!-- X -->
    <xs:attribute name="width" type="xs:decimal" use="required"/>    <!-- Y -->
    <xs:attribute name="thickness" type="xs:decimal" use="required"/><!-- Z -->
    <xs:attribute name="grain" type="GrainDir" default="none"/>
    <xs:attribute name="material" type="xs:IDREF"/>                  <!-- -> Material -->
    <xs:attribute name="design" type="xs:IDREF"/>                    <!-- -> Design it came from -->
  </xs:complexType>

  <xs:simpleType name="GrainDir">
    <xs:restriction base="xs:string">
      <xs:enumeration value="length"/>
      <xs:enumeration value="width"/>
      <xs:enumeration value="none"/>
    </xs:restriction>
  </xs:simpleType>

  <!-- ============================================================== edging -->
  <xs:complexType name="Edging">
    <xs:sequence>
      <xs:element name="Edge" maxOccurs="4">
        <xs:complexType>
          <xs:attribute name="edge" type="EdgeId" use="required"/>   <!-- geometric -->
          <xs:attribute name="as" type="xs:string"/>                 <!-- L1/L2/W1/W2 -->
          <xs:attribute name="edgeMaterial" type="xs:IDREF" use="required"/>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>

  <xs:simpleType name="EdgeId">
    <xs:restriction base="xs:string">
      <xs:enumeration value="X0"/><xs:enumeration value="Xmax"/>
      <xs:enumeration value="Y0"/><xs:enumeration value="Ymax"/>
    </xs:restriction>
  </xs:simpleType>

  <!-- ========================================================== operations -->
  <xs:complexType name="Operations">
    <xs:choice minOccurs="0" maxOccurs="unbounded">
      <xs:element name="Bore" type="Bore"/>
      <xs:element name="Route" type="Route"/>
      <xs:element name="Pocket" type="Pocket"/>
    </xs:choice>
  </xs:complexType>

  <!-- Face id: the two faces (A/B) or an edge (for horizontal/edge boring). -->
  <xs:simpleType name="FaceId">
    <xs:restriction base="xs:string">
      <xs:enumeration value="A"/><xs:enumeration value="B"/>
      <xs:enumeration value="X0"/><xs:enumeration value="Xmax"/>
      <xs:enumeration value="Y0"/><xs:enumeration value="Ymax"/>
    </xs:restriction>
  </xs:simpleType>

  <!-- OpLabel: optional, on every operation. name = a short label (shown to the
       operator, or used as the machine program's operation name); note = free
       text for the operator. Neither changes what is machined: an importer
       must never read meaning from them. -->
  <xs:attributeGroup name="OpLabel">
    <xs:attribute name="name" type="xs:string"/>
    <xs:attribute name="note" type="xs:string"/>
  </xs:attributeGroup>

  <!-- Bore: one or more holes of the same diameter/depth on one face.
       Face bore (A/B): positions use <At x= y=>. Edge bore: <At u=> + @z. -->
  <xs:complexType name="Bore">
    <xs:sequence>
      <xs:element name="At" type="At" maxOccurs="unbounded"/>
      <xs:element name="Pattern" type="Pattern" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute name="face" type="FaceId" use="required"/>
    <xs:attribute name="diameter" type="xs:decimal" use="required"/>
    <xs:attribute name="depth" type="xs:decimal"/>
    <xs:attribute name="through" type="xs:boolean" default="false"/>
    <xs:attribute name="z" type="xs:decimal"/>   <!-- edge bores: height from face A -->
    <xs:attributeGroup ref="OpLabel"/>
  </xs:complexType>

  <xs:complexType name="At">
    <xs:attribute name="x" type="xs:decimal"/>   <!-- face bores -->
    <xs:attribute name="y" type="xs:decimal"/>   <!-- face bores -->
    <xs:attribute name="u" type="xs:decimal"/>   <!-- edge bores: along-edge distance -->
  </xs:complexType>

  <xs:complexType name="Pattern">
    <xs:attribute name="count" type="xs:positiveInteger" use="required"/>
    <xs:attribute name="dx" type="xs:decimal" default="0"/>
    <xs:attribute name="dy" type="xs:decimal" default="0"/>
  </xs:complexType>

  <!-- Route: a routed path on a face, as connected Line / Arc segments in the
       part frame (X along length, Y along width, seen from face A).
       toolWidth = cutter diameter. toolSide says where the cutter runs relative
       to the direction of travel: left / right put the path on the finished
       edge (the cutter is offset to that side), centre runs the cutter centre
       on the path (a groove of toolWidth). -->
  <xs:complexType name="Route">
    <xs:sequence>
      <xs:choice maxOccurs="unbounded">
        <xs:element name="Line">
          <xs:complexType>
            <xs:attribute name="x1" type="xs:decimal" use="required"/>
            <xs:attribute name="y1" type="xs:decimal" use="required"/>
            <xs:attribute name="x2" type="xs:decimal" use="required"/>
            <xs:attribute name="y2" type="xs:decimal" use="required"/>
          </xs:complexType>
        </xs:element>
        <!-- circular arc from (x1,y1) to (x2,y2) about centre (cx,cy); dir is
             clockwise / counter-clockwise in the part frame seen from face A -->
        <xs:element name="Arc">
          <xs:complexType>
            <xs:attribute name="x1" type="xs:decimal" use="required"/>
            <xs:attribute name="y1" type="xs:decimal" use="required"/>
            <xs:attribute name="x2" type="xs:decimal" use="required"/>
            <xs:attribute name="y2" type="xs:decimal" use="required"/>
            <xs:attribute name="cx" type="xs:decimal" use="required"/>
            <xs:attribute name="cy" type="xs:decimal" use="required"/>
            <xs:attribute name="dir" type="ArcDir" use="required"/>
          </xs:complexType>
        </xs:element>
      </xs:choice>
    </xs:sequence>
    <xs:attribute name="face" type="FaceId" use="required"/>
    <xs:attribute name="toolWidth" type="xs:decimal" use="required"/>
    <xs:attribute name="toolSide" type="ToolSide" default="centre"/>
    <xs:attribute name="depth" type="xs:decimal"/>
    <xs:attribute name="through" type="xs:boolean" default="false"/>
    <xs:attribute name="closed" type="xs:boolean" default="false"/>  <!-- path returns to its start -->
    <xs:attributeGroup ref="OpLabel"/>
  </xs:complexType>

  <!-- Pocket: a rectangular recess cleared to depth (a pocketing cycle, e.g.
       WoodWOP Tasche on a BHX). x,y = its centre in the part frame; sizeX /
       sizeY = its extent along part X / Y before rotation; cornerRadius = the
       radius of its corners (0 = square, a factory may leave a cutter radius);
       angle = rotation about its centre, degrees, counter-clockwise seen from
       face A. A pocket may run past the part's edges (an open-ended groove). -->
  <xs:complexType name="Pocket">
    <xs:attribute name="face" type="FaceId" use="required"/>
    <xs:attribute name="x" type="xs:decimal" use="required"/>
    <xs:attribute name="y" type="xs:decimal" use="required"/>
    <xs:attribute name="sizeX" type="xs:decimal" use="required"/>
    <xs:attribute name="sizeY" type="xs:decimal" use="required"/>
    <xs:attribute name="cornerRadius" type="xs:decimal" default="0"/>
    <xs:attribute name="angle" type="xs:decimal" default="0"/>
    <xs:attribute name="depth" type="xs:decimal"/>
    <xs:attribute name="through" type="xs:boolean" default="false"/>
    <xs:attributeGroup ref="OpLabel"/>
  </xs:complexType>

  <xs:simpleType name="ArcDir">
    <xs:restriction base="xs:string">
      <xs:enumeration value="cw"/><xs:enumeration value="ccw"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="ToolSide">
    <xs:restriction base="xs:string">
      <xs:enumeration value="left"/><xs:enumeration value="right"/><xs:enumeration value="centre"/>
    </xs:restriction>
  </xs:simpleType>

  <!-- ============================================================ assembly -->
  <xs:complexType name="Assembly">
    <xs:sequence>
      <xs:element name="Instance" type="Instance" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="id" type="xs:ID" use="required"/>
    <xs:attribute name="name" type="xs:string"/>
    <xs:attribute name="units" type="xs:string" default="mm"/>
    <xs:attribute name="design" type="xs:IDREF"/>   <!-- -> Design it places -->
  </xs:complexType>

  <xs:complexType name="Instance">
    <xs:sequence>
      <xs:element name="Position">
        <xs:complexType>
          <xs:attribute name="x" type="xs:decimal" use="required"/>
          <xs:attribute name="y" type="xs:decimal" use="required"/>
          <xs:attribute name="z" type="xs:decimal" use="required"/>
        </xs:complexType>
      </xs:element>
      <!-- Orientation: axis-map (readable, orthogonal cabinets) OR matrix (angled).
           A validator expands axis-map/euler to the canonical matrix. -->
      <xs:element name="Orientation">
        <xs:complexType>
          <xs:attribute name="localX" type="AxisMap"/>
          <xs:attribute name="localY" type="AxisMap"/>
          <xs:attribute name="localZ" type="AxisMap"/>
          <xs:attribute name="matrix" type="xs:string"/>   <!-- 16 space-sep values, row-major -->
        </xs:complexType>
      </xs:element>
    </xs:sequence>
    <xs:attribute name="part" type="xs:IDREF" use="required"/>
    <xs:attribute name="id" type="xs:ID" use="required"/>
  </xs:complexType>

  <xs:simpleType name="AxisMap">
    <xs:restriction base="xs:string">
      <xs:pattern value="[+-](X|Y|Z)"/>
    </xs:restriction>
  </xs:simpleType>

</xs:schema>
