<?xml version="1.0" encoding="UTF-8"?>
<!--
  CCX — CabCode eXchange, v1.0 (DRAFT schema)
  Machine-neutral panel machining-intent interchange. See ../ccx-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/ccx/1.0"
           targetNamespace="https://cabcode.uk/ccx/1.0"
           elementFormDefault="qualified">

  <!-- ============================================================= root -->
  <xs:element name="CCX">
    <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="Part" type="Part" 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>

  <!-- ================================================================ 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: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: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>
