<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           elementFormDefault="qualified"
           attributeFormDefault="unqualified"
           version="1.0"
           id="RRL">

  <xs:annotation>
    <xs:documentation>
      Independent Rail Replacement Location (RRL) XML Schema.

      This schema carries data that is not included in NaPTAN but, where 
	  practical, mirrors the NaPTAN 2.4 XML structure. Equivalent concepts 
	  generally use the same element names, ordering and underlying data
	  types to maximise consistency for consumers familiar with NaPTAN. 
	  While this XSD uses structures and validation rules derived from 
	  NaPTAN where appropriate, it contains a number of RRL-specific elements 
	  and business rules that do not exist within the NaPTAN 2.4 schema. 
	  Consumers should therefore treat this as a distinct schema and should 
	  not assume full compatibility with NaPTAN.
	  
	  The RRL dataset remains a distinct schema and includes a small number of 
	  deliberate deviations required to support rail replacement location data 
	  and pilot-specific business rules.
	  
	  StopClassification/RailReplacement is the principal extension. These 
	  elements have no direct NaPTAN equivalent. CrsRef shares the NaPTAN 
	  CrsCodeType rule but is used here for a different purpose and therefore 
	  appears in a different location within the model.

      Language is carried on a local, unqualified lang attribute rather 
	  than the xml:lang attribute NaPTAN uses. This keeps the schema a 
	  single self-contained file with no external import, and makes the 
	  XML and JSON forms of RRL consistent with each other. It is a 
	  deliberate deviation from NaPTAN: consumers expecting xml:lang must 
	  read lang instead. English (en) and Welsh (cy) are supported for the 
	  pilot, but the schema deliberately permits additional valid language 
	  codes without requiring a schema change.

      Each section below identifies the NaPTAN 2.4 module or concept from which the definition was derived.
    </xs:documentation>
  </xs:annotation>

  <!-- ====Root================================================================ -->
  <xs:element name="RailReplacementLocation">
    <xs:annotation>
      <xs:documentation>Schema for exchanging Rail Replacement Location data.

@CreationDateTime,
@ModificationDateTime,
@Modification,
@RevisionNumber,
@FileName,
@Version</xs:documentation>
    </xs:annotation>
    <xs:complexType>
      <xs:sequence>
        <xs:element name="StopPoints" type="StopPointsStructure">
          <xs:annotation>
            <xs:documentation>Definitions of rail replacement stop points. Mandatory in RRL; the equivalent NaPTAN element is optional.</xs:documentation>
          </xs:annotation>
        </xs:element>
      </xs:sequence>
      <xs:attributeGroup ref="DocumentModificationDetailsGroup"/>
      <xs:attribute name="Version" type="xs:NMTOKEN" use="required" fixed="1.0">
        <xs:annotation>
          <xs:documentation>The version of the schema that the RRL instance document was written against.</xs:documentation>
        </xs:annotation>
      </xs:attribute>
    </xs:complexType>
    <xs:key name="StopPointKey">
      <xs:annotation>
        <xs:documentation>Stop point keys must be unique within the document.</xs:documentation>
      </xs:annotation>
      <xs:selector xpath=".//StopPoints/StopPoint"/>
      <xs:field xpath="AtcoCode"/>
    </xs:key>
  </xs:element>

  <!-- ====Versioning: mirrors NaPT_versioningAttributes-v2-2.xsd============== -->
  <xs:simpleType name="ModificationEnumeration">
    <xs:annotation>
      <xs:documentation>Nature of last modification. A restriction of the NaPTAN enumeration, which also allows delta and archive.</xs:documentation>
    </xs:annotation>
    <xs:restriction base="xs:NMTOKEN">
      <xs:enumeration value="new"/>
      <xs:enumeration value="delete"/>
      <xs:enumeration value="revise"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="RevisionNumberType">
    <xs:annotation>
      <xs:documentation>An integer that should be increased by one each time the unit it refers to is modified.</xs:documentation>
    </xs:annotation>
    <xs:restriction base="xs:nonNegativeInteger"/>
  </xs:simpleType>

  <xs:attributeGroup name="DocumentModificationDetailsGroup">
    <xs:annotation>
      <xs:documentation>Grouping for document change metadata.</xs:documentation>
    </xs:annotation>
    <xs:attribute name="CreationDateTime" type="xs:dateTime" use="required"/>
    <xs:attribute name="ModificationDateTime" type="xs:dateTime" use="required"/>
    <xs:attribute name="Modification" type="ModificationEnumeration" use="required"/>
    <xs:attribute name="RevisionNumber" type="RevisionNumberType" use="required"/>
    <xs:attribute name="FileName" type="xs:string" use="required"/>
  </xs:attributeGroup>

  <xs:complexType name="VersionedObjectTimestampedStructure">
    <xs:annotation>
      <xs:documentation>Base for a versioned object carrying its own timestamps. Modification and RevisionNumber are required in RRL; both are optional in NaPTAN.</xs:documentation>
    </xs:annotation>
    <xs:attribute name="CreationDateTime" type="xs:dateTime" use="required"/>
    <xs:attribute name="ModificationDateTime" type="xs:dateTime" use="optional"/>
    <xs:attribute name="Modification" type="ModificationEnumeration" use="required"/>
    <xs:attribute name="RevisionNumber" type="RevisionNumberType" use="required"/>
  </xs:complexType>

  <!-- ====Text: mirrors NaPT_utility_xml-v2-1.xsd and NaPT_utility_types-v2-1.xsd -->
  <xs:simpleType name="LanguageCodeType">
    <xs:annotation>
      <xs:documentation>Language of a natural language value.

NaPTAN uses the standard xml:lang attribute from the XML namespace. RRL uses a local, unqualified lang attribute instead, so that this schema is a single self-contained file with no external import. This is a deliberate deviation from NaPTAN. It also makes the XML and JSON forms of RRL identical in this respect, since JSON has no namespaces and already carries a plain lang key.

English (en) and Welsh (cy) are in use for the pilot. The type deliberately permits any well-formed language tag so that further languages need no schema change. The pattern matches the JSON schema's LanguageCode exactly: a two or three letter primary subtag with optional further subtags.</xs:documentation>
    </xs:annotation>
    <xs:restriction base="xs:language">
      <xs:pattern value="[A-Za-z]{2,3}(-[A-Za-z0-9]{1,8})*"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="PopulatedStringType">
    <xs:annotation>
      <xs:documentation>A string that requires at least one character of text, of which at least one must be non-whitespace. NaPTAN checks only the length; requiring a visible character as well is a restriction of the NaPTAN rule, so RRL values remain NaPTAN-valid.</xs:documentation>
    </xs:annotation>
    <xs:restriction base="xs:string">
      <xs:minLength value="1"/>
      <xs:pattern value="[\s\S]*\S[\s\S]*"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="PopulatedPlaceNameType">
    <xs:annotation>
      <xs:documentation>A name that requires at least one character of text and forbids certain reserved characters.</xs:documentation>
    </xs:annotation>
    <xs:restriction base="PopulatedStringType">
      <xs:maxLength value="48"/>
      <xs:pattern value="[^,\[\]\{\}\?$£%\^=@#;:]+"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:complexType name="NaturalLanguagePlaceNameStructure">
    <xs:annotation>
      <xs:documentation>@lang. ISO language code (default is en).

A natural language name that requires at least one character of text and forbids certain reserved characters.</xs:documentation>
    </xs:annotation>
    <xs:simpleContent>
      <xs:extension base="PopulatedPlaceNameType">
        <xs:attribute name="lang" type="LanguageCodeType"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>

  <xs:complexType name="NaturalLanguageStringStructure">
    <xs:annotation>
      <xs:documentation>@lang. ISO language code (default is en).

A string containing a phrase in a natural language.</xs:documentation>
    </xs:annotation>
    <xs:simpleContent>
      <xs:extension base="PopulatedStringType">
        <xs:attribute name="lang" type="LanguageCodeType"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>

  <xs:complexType name="AlternativeNaturalLanguagePlaceNameStructure">
    <xs:annotation>
      <xs:documentation>As NaturalLanguagePlaceNameStructure, but lang is required. Used only within AlternativeDescriptors, where the language of the alternative is the point of the entry. This is a restriction of the NaPTAN rule, not a departure from it.</xs:documentation>
    </xs:annotation>
    <xs:simpleContent>
      <xs:restriction base="NaturalLanguagePlaceNameStructure">
        <xs:attribute name="lang" type="LanguageCodeType" use="required"/>
      </xs:restriction>
    </xs:simpleContent>
  </xs:complexType>

  <xs:complexType name="AlternativeNaturalLanguageStringStructure">
    <xs:annotation>
      <xs:documentation>As NaturalLanguageStringStructure, but lang is required.</xs:documentation>
    </xs:annotation>
    <xs:simpleContent>
      <xs:restriction base="NaturalLanguageStringStructure">
        <xs:attribute name="lang" type="LanguageCodeType" use="required"/>
      </xs:restriction>
    </xs:simpleContent>
  </xs:complexType>

  <!-- ====Location: mirrors NaPT_location-v2-1.xsd============================ -->
  <xs:simpleType name="LocationGridTypeEnumeration">
    <xs:annotation>
      <xs:documentation>A restriction of the NaPTAN enumeration, which also allows IrishOS.</xs:documentation>
    </xs:annotation>
    <xs:restriction base="xs:NMTOKEN">
      <xs:enumeration value="UKOS"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="EastingType">
    <xs:restriction base="xs:nonNegativeInteger">
      <xs:maxInclusive value="999999"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="NorthingType">
    <xs:restriction base="xs:nonNegativeInteger">
      <xs:maxInclusive value="1999999"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="LongitudeType">
    <xs:restriction base="xs:decimal">
      <xs:minInclusive value="-180"/>
      <xs:maxInclusive value="180"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="LatitudeType">
    <xs:restriction base="xs:decimal">
      <xs:minInclusive value="-90"/>
      <xs:maxInclusive value="90"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:group name="GridGroup">
    <xs:sequence>
      <xs:element name="GridType" type="LocationGridTypeEnumeration" default="UKOS" minOccurs="0"/>
      <xs:element name="Easting" type="EastingType"/>
      <xs:element name="Northing" type="NorthingType"/>
    </xs:sequence>
  </xs:group>

  <xs:group name="WgsGroup">
    <xs:sequence>
      <xs:element name="Longitude" type="LongitudeType"/>
      <xs:element name="Latitude" type="LatitudeType"/>
    </xs:sequence>
  </xs:group>

  <xs:complexType name="TranslationStructure">
    <xs:annotation>
      <xs:documentation>Grid and WGS84 coordinates for the same point.</xs:documentation>
    </xs:annotation>
    <xs:sequence>
      <xs:group ref="GridGroup"/>
      <xs:group ref="WgsGroup"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="LocationStructure">
    <xs:annotation>
      <xs:documentation>Position of a point. NaPTAN allows a choice of grid only, WGS84 only, or a Translation carrying both; RRL requires the Translation branch, so every location carries both systems. An RRL Location is therefore valid against the NaPTAN LocationStructure.</xs:documentation>
    </xs:annotation>
    <xs:sequence>
      <xs:element name="Translation" type="TranslationStructure"/>
    </xs:sequence>
  </xs:complexType>

  <!-- ====Locality: mirrors NPTG_locality_support-v2-4.xsd==================== -->
  <xs:simpleType name="NptgLocalityCodeType">
    <xs:annotation>
      <xs:documentation>NPTG coding for a locality identifier. ab999999, where a is E or N, b is 0 or S, 9 is numeric.</xs:documentation>
    </xs:annotation>
    <xs:restriction base="xs:NMTOKEN">
      <xs:pattern value="[EN][0S][0-9]{6}"/>
    </xs:restriction>
  </xs:simpleType>

  <!-- ====Stop points: mirrors NaPT_stop-v2-4.xsd============================= -->
  <xs:simpleType name="AtcoCodeType">
    <xs:annotation>
      <xs:documentation>Nationally unique identifier for a stop point. Mirrors the NaPTAN 2.4 rule: administrative area code, separator, then a locally assigned identifier.</xs:documentation>
    </xs:annotation>
    <xs:restriction base="xs:normalizedString">
      <xs:pattern value="[0-9]{3}[0-1][A-Za-z0-9]{1,8}"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="RrlAtcoCodeType">
    <xs:annotation>
      <xs:documentation>ATCO code for a rail replacement location, in the form 911 + 0 + CRS code + sequence number, for example 9110OCK0, 9110OCK1, 9110AMR0.

The 911 area code is reserved for rail replacement data. The sequence number starts at zero for the first location associated with a CRS code and increments for each further location at the same station.

Derived by restriction from AtcoCodeType, so an RRL ATCO code is also a valid NaPTAN ATCO code. The inherited rule caps the CRS code plus sequence number at eight characters, which allows sequence numbers up to five digits.

MarkedStopAtcoCode is deliberately NOT this type: it references an existing NaPTAN stop in its own administrative area, and so uses the general AtcoCodeType.</xs:documentation>
    </xs:annotation>
    <xs:restriction base="AtcoCodeType">
      <xs:pattern value="9110[A-Z]{3}[0-9]+"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="StopTypeEnumeration">
    <xs:annotation>
      <xs:documentation>Classification of the stop. RRL is not a NaPTAN 2.4 stop type; it is introduced by this schema.</xs:documentation>
    </xs:annotation>
    <xs:restriction base="xs:NMTOKEN">
      <xs:enumeration value="RRL">
        <xs:annotation>
          <xs:documentation>Rail replacement location.</xs:documentation>
        </xs:annotation>
      </xs:enumeration>
    </xs:restriction>
  </xs:simpleType>

  <xs:complexType name="StopPointsStructure">
    <xs:sequence>
      <xs:element name="StopPoint" type="StopPointStructure" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="StopPointStructure">
    <xs:annotation>
      <xs:documentation>A rail replacement stop point.

@CreationDateTime,
@ModificationDateTime,
@Modification,
@RevisionNumber</xs:documentation>
    </xs:annotation>
    <xs:complexContent>
      <xs:extension base="VersionedObjectTimestampedStructure">
        <xs:sequence>
          <xs:element name="AtcoCode" type="RrlAtcoCodeType"/>
          <xs:element name="Descriptor" type="DescriptorStructure"/>
          <xs:element name="AlternativeDescriptors" type="AlternativeDescriptorsStructure" minOccurs="0"/>
          <xs:element name="Place" type="PlaceStructure"/>
          <xs:element name="StopClassification" type="StopClassificationStructure"/>
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

  <xs:complexType name="DescriptorStructure">
    <xs:annotation>
      <xs:documentation>Primary descriptor for the stop. NaPTAN also allows ShortCommonName, Landmark, Crossing and Indicator here; RRL omits them.</xs:documentation>
    </xs:annotation>
    <xs:sequence>
      <xs:group ref="DescriptorGroup"/>
    </xs:sequence>
  </xs:complexType>

  <xs:group name="DescriptorGroup">
    <xs:sequence>
      <xs:element name="CommonName" type="NaturalLanguagePlaceNameStructure"/>
      <xs:element name="Street" type="NaturalLanguagePlaceNameStructure" minOccurs="0"/>
      <xs:element name="Description" type="NaturalLanguageStringStructure">
        <xs:annotation>
          <xs:documentation>Free text description of the stop. No NaPTAN equivalent; the nearest NaPTAN concept is StopPoint/Notes.</xs:documentation>
        </xs:annotation>
      </xs:element>
    </xs:sequence>
  </xs:group>

  <xs:group name="AlternativeDescriptorGroup">
    <xs:annotation>
      <xs:documentation>As DescriptorGroup, but every value must carry lang.</xs:documentation>
    </xs:annotation>
    <xs:sequence>
      <xs:element name="CommonName" type="AlternativeNaturalLanguagePlaceNameStructure"/>
      <xs:element name="Street" type="AlternativeNaturalLanguagePlaceNameStructure" minOccurs="0"/>
      <xs:element name="Description" type="AlternativeNaturalLanguageStringStructure"/>
    </xs:sequence>
  </xs:group>

  <xs:complexType name="AlternativeDescriptorsStructure">
    <xs:annotation>
      <xs:documentation>Descriptors for the stop in other languages.

Neither this element nor the primary Descriptor carries change metadata of its own: both are versioned by the parent StopPoint. A change to either language therefore updates the same StopPoint ModificationDateTime and RevisionNumber, so no change goes untracked, but a revision cannot be attributed to one language rather than the other. NaPTAN places creation and revision details on each alternative descriptor entry, treating them as an independently maintained list. If that granularity is wanted later, apply an attributeGroup carrying a required CreationDateTime and optional ModificationDateTime, Modification and RevisionNumber to the Descriptor element below.</xs:documentation>
    </xs:annotation>
    <xs:sequence>
      <xs:element name="Descriptor" maxOccurs="unbounded">
        <xs:complexType>
          <xs:sequence>
            <xs:group ref="AlternativeDescriptorGroup"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="PlaceStructure">
    <xs:annotation>
      <xs:documentation>Where the stop is. NaPTAN also allows LocalityName, AlternativeNptgLocalities, MainNptgLocalities, Suburb, Town and LocalityCentre here; RRL omits them.

Location is optional here, which is a deliberate deviation: NaPTAN requires it. In the published RRL dataset a minority of records carry a locality but no surveyed coordinates, and those are almost all rail replacement only stops. Making Location optional keeps the locality reference on such records rather than dropping the whole Place. If the source data is completed so that every record has coordinates, this should be tightened back to mandatory to match NaPTAN.</xs:documentation>
    </xs:annotation>
    <xs:sequence>
      <xs:element name="NptgLocalityRef" type="NptgLocalityCodeType"/>
      <xs:element name="Location" type="LocationStructure" minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="StopClassificationStructure">
    <xs:sequence>
      <xs:element name="StopType" type="StopTypeEnumeration"/>
      <xs:element name="RailReplacement" type="RailReplacementStructure"/>
    </xs:sequence>
  </xs:complexType>

  <!-- ====Rail replacement: RRL specific, no NaPTAN 2.4 equivalent=========== -->
  <xs:simpleType name="CrsCodeType">
    <xs:annotation>
      <xs:documentation>Three character CRS code identifying a station.

Mirrors the NaPTAN 2.4 CrsCodeType rule exactly. NaPTAN carries a CrsRef of its own under StopClassification/OffStreet/Rail/AnnotatedRailRef for a different purpose; the RRL CrsRef deliberately keeps its own position here, but shares the NaPTAN rule.</xs:documentation>
    </xs:annotation>
    <xs:restriction base="xs:NMTOKEN">
      <xs:length value="3"/>
      <xs:pattern value="[A-Z]{3}"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="SfoCodeType">
    <xs:annotation>
      <xs:documentation>Station Facility Owner code, identifying the Train Operating Company responsible for the station. Exactly two characters, as in the published dataset.</xs:documentation>
    </xs:annotation>
    <xs:restriction base="xs:NMTOKEN">
      <xs:length value="2"/>
      <xs:pattern value="[A-Za-z0-9]{2}"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:complexType name="RailReplacementStructure">
    <xs:annotation>
      <xs:documentation>Rail replacement details. This section is the intended deviation from NaPTAN; it replaces the OnStreet/OffStreet choice that NaPTAN carries at this position.</xs:documentation>
    </xs:annotation>
    <xs:sequence>
      <xs:element name="CrsRef" type="CrsCodeType"/>
      <xs:element name="SFOCode" type="SfoCodeType">
        <xs:annotation>
          <xs:documentation>Named to match the SFOCode column of the published RRL CSV, which is already in circulation. NaPTAN naming convention would suggest SfoRef, but alignment with the published dataset takes precedence here.</xs:documentation>
        </xs:annotation>
      </xs:element>
      <xs:element name="MarkedStopAtcoCode" type="AtcoCodeType" minOccurs="0">
        <xs:annotation>
          <xs:documentation>The NaPTAN stop this rail replacement location is marked against. Typed as AtcoCodeType, since it references a NaPTAN stop point.</xs:documentation>
        </xs:annotation>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
</xs:schema>